CAFE

Spring

SpringBoot JPA

작성자ParkYK|작성시간23.01.28|조회수568 목록 댓글 1

🎵 JPA 사용 

ORM(Object-relational mapping) 이란 객체 관계 매핑이다.
  - 객체는 객체대로 설계하고, 관계형 데이터베이스는 관계형 데이터베이스대로 설계한다.
  - ORM 프레임워크가 중간에서 매핑해준다.
  - 대중적인 언어에는 대부분 ORM 기술이 존재한다.
결국 ORM을 사용하면 데이터베이스 시스템 종류에 상관 없이 일관된 자바 코드를 유지할 수 있어서 프로그램을 유지·보수하기가 용이하다. 또한 내부에서 안전한 SQL 쿼리를 자동으로 생성해 주므로 통일된 쿼리를 작성할 수 있고 오류 발생률도 줄일 수 있어 비용절감의 효과가 크다.

 

JPA(Java Persistence API)란 ORM 기술 표준으로, 실제로 동작하는 것이 아니라 자바 인터페이스의 모음이다.
  - EJB : 과거의 자바 표준 (Entity Bean) ORM
  - Hibernate : ORM 프레임워크, Open Source SW
                       EJB2 스타일의 Entity Beans 이용을 대체할 목적으로 개발하였다.

JPA 2.1 표준 명세를 구현한 3가지 구현체로는 Hibernate, EclipseLink, DataNucleus가 있다.
이 중에서 JPA 인터페이스를 구현한 대표적인 오픈소스가 Hibernate라고 할 수 있다.

왜 JPA를 사용해야 하는가?
  - JPA를 자바 컬렉션에 객체를 저장하듯 JPA에게 저장할 객체를 전달.
  - INSERT SQL을 작성하고 JDBC API 사용하는 지루하고 반복적인 일을 JPA가 대신 처리해준다.
  - CREATE TABLE같은 DDL문 자동 생성
  - 데이터베이스 설계 중심의 패러다임을 객체 설계 중심으로 역전

 

스프링 데이터 JPA - 참조 문서

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

http://arahansa.github.io/docs_spring/jpa.html

https://huimang2.github.io/java/jpa

 

Spring Data JDBC vs Spring Data JPA  https://velog.io/@bread_dd/Spring-Data-JDBC-vs-Spring-Data-JPA

 

[Spring JPA] JpaRepository 기본 사용법  https://m.blog.naver.com/hj_kim97/222780110215

 

 

MavenProject로 JPA 실습할 때 사용할 xml 파일 ↴

 

 

JPA에 지원되는 키워드와 해당 키워드를 포함하는 메소드

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

 

 

** 엔티티(테이블) 작성 예 :  Lombok + Hibernate **

import lombok.Getter;
import lombok.Setter;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.time.LocalDateTime;

@Getter
@Builder

@NoArgsConstructor

@Entity                    // @Entity 어노테이션을 적용해야 JPA가 엔티티(실제 테이블과 매핑)로 인식함
public class Test {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)   // pk 컬럼으로 번호 1씩 자동 증가
    private Integer id;         

    @Column(length = 100)
    private string title;

    @Column(columnDefinition = "TEXT")   // 글자 수를 제한할 수 없는 경우에 사용
    private String content;     

    private LocalDateTime createDate;   // 엔티티의 속성은 @Column 애너테이션을 사용하지 않더라도 테이블 컬럼으로 인식함
    // 주의 : createDate 속성의 실제 테이블의 컬럼명은 create_date가 된다. 즉 createDate처럼 대소문자 형태의 Camel Case 이름은 create_date 처럼 모두 소문자로 변경되고 언더바로 단어가 구분되어 실제 컬럼명이 된다.
}

잠깐만... Q&A : @Entity가 설정된 클래스에 의해, 원격DB Server에 테이블이 자동으로 생성될 수 있나?

application.properties에는 아래와 같은 내용을 적어 둔 상태다.
#mariadb server connect
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/mydb
spring.datasource.username=root
spring.datasource.password=123
# jpa
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
logging.level.org.hibernate.SQL=debug

logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect

공식문서 : https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/dialect/package-summary.html

참고 : Hibernate Logging 옵션 정리
https://one-armed-boy.tistory.com/entry/Spring-%EC%8B%A0%EA%B7%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%B4-%EC%82%AC%EC%9A%A9-%ED%95%B4%EB%B4%84%EC%A7%81%ED%95%9C-Hibernate-Logging-%EC%98%B5%EC%85%98-%EC%A0%95%EB%A6%AC

Spring Data JPA를 사용하면, 애플리케이션이 실행될 때 Test 엔티티 클래스에 기반하여 데이터베이스에 자동으로 테이블이 생성될 수 있다. 이를 위해 추가 설정이 필요하다.
application.properties 파일에 다음과 같은 설정을 추가한다.

# JPA Hibernate 설정
spring.jpa.hibernate.ddl-auto=update

위 설정에서 spring.jpa.hibernate.ddl-auto 속성은 Hibernate에게 데이터베이스 스키마를 어떻게 관리할지 알려준다.
이 속성의 가능한 값은 다음과 같다:
  • none: 아무것도 하지 않음
  • validate: 엔티티와 테이블이 매핑되는지 검증
  • update: 엔티티 구조에 맞춰 테이블을 수정
  • create: 애플리케이션 실행 시 기존 테이블을 삭제하고 새로 생성
  • create-drop: 애플리케이션 종료 시 테이블을 삭제
위 설정을 추가한 후, update 값을 사용하면 애플리케이션 실행 시 Test 엔티티에 맞춰 테이블이 자동으로 생성되거나 수정된다.


HikariCP 세팅시 옵션   https://effectivesquid.tistory.com/entry/HikariCP-%EC%84%B8%ED%8C%85%EC%8B%9C-%EC%98%B5%EC%85%98-%EC%84%A4%EB%AA%85

 

그런데 엔티티만으로 데이터베이스에 자료를 저장하거나 조회 할 수 없다. 데이터 처리를 위해서는 데이터베이스와 연동하는 JPA Repository를 필요로 한다. 리포지터리는 엔티티에 의해 생성된 데이터베이스 테이블에 접근하는 메서드들(findAll, save ...)을 사용하기 위한 인터페이스다. 
데이터 처리를 위해서는 테이블에 어떤 값을 넣거나 값을 조회하는 등의 CRUD(Create, Read, Update, Delete)가 필요하다.  이 때 CRUD를 어떻게 처리할지 정의하는 계층을 Repository 라고 한다.

import org.springframework.data.jpa.repository.JpaRepository;
public interface TestRepository extends JpaRepository<Test, Integer> {
   ...
}

 

Repository 인터페이스란?
  - MyBatis 등에서 Dao라고 불리는 DB Layer 접근자로, JPA에서는 Repository라고 부르며 인터페이스로 생성한다. 단순히 인터페이스를 생성 후 JpaRepository<Entity 클래스, PK 타입>를 상속하면 기본적인 CRUD 메소드가 자동으로 생성된다.
  - JpaRepository 인터페이스를 상속받는 것만으로도, 많은 JPA 관련 메소드를 사용할 수 있다.

** JpaRepository는
  ·  PagingAndSortingRepository, QueryByExampleExecutor 인터페이스를 상속받음
  · PagingAndSqortingRepository는 CrudRepository 인터페이스를 상속받음
  · CrudRepository 인터페이스는 기본적인 CRUD 메소드를 제공함
      → save(), findById(), existsById(), count(), deleteById(), delete(), deleteAll()
  · QueryByExampleExecutor 인터페이스에는 더 다양한 CRUD 메소드 제공
      → findOne(), findAll(), count(), exists()

 

 

참고 (필독) JPA vs JDBC, JPA vs Mybatis, JPA vs Spring Data JPA의 차이점과 Hibernate    https://skyblue300a.tistory.com/7

 

역할 분리를 위한 Entity, DTO 개념과 차이점   https://wildeveloperetrain.tistory.com/101

 

참고 : lombok 어노테이션 중 @Data에 대해....
무분별하게 @Data를 사용하면 @ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor 도 
같이 사용되며 발생하는 문제들이 생길 수 있다.

1) @Setter
  setter의 무분별한 사용은 객체의 안전성을 보장하기 어렵게 만든다.
  setter를 사용함과 동시에 객체는 언제든지 변경될 수 있는 상태로 변하기 때문이다.

2) @ToString
  To String 메서드를 만들지 않고 사용할 수 있게 하는 @ToString은 JPA와 같은 ORM을 사용할 때 문제가 될 수 있다.
  예를 들어 아래와 같은 두 개의 모델이 있다고 하자. 

@Data
public class Parent{
    String name;
    int age;
    List<Sun> suns;
}

@Data
public class Sun{
    String name;
    int age;
    Parent parent;
}
이런 경우에는 서로 간의 ToString이 무한대로 호출되어 stackoverflow가 발생할 수 있다. 그러므로 @Data 보다는 필요에 맞춰 안에 들어가는 요인들을 생각해 잘 사용할 수 있도록 하자.

 

https://huimang2.github.io/java/jpa 참조

 

영속성 컨텍스트(persistence context)

: 영속성 컨텐스트란 엔티티를 영구 저장하는 환경이라는 뜻이다. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

어플리케이션과 db 사이에서 객체를 보관하는 가상의 db. entity manager 생성할 때 하나 만들어지고, 이를 통해서 저장(persist), 조회(find)하면 영속성 컨텍스트에 저장(보관 및 관리).

 

https://velog.io/@neptunes032/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80

 

 

 

참고 :

연습1) 1 : N 관계로 Entity를 간단하게 만들기 ( join )

* Entity 설계하기

일반적으로 한 명의 사용자는 여러 개의 게시글을 작성할 수 있고, 게시글은 한 명의 사용자에 작성되기 때문에 User - Post = 1 : N 관계로 Entity를 구현한다.

Post Entity -----
@NoArgsConstructor(access = AccessLevel.PROTECTED)    // https://cobbybb.tistory.com/category/JPA?page=1
@Getter
@Table(name = "post")
@Entity
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @Lob   // BLOB, CLOB 매핑
    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}
 
User Entity -----
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "user")
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;
}

 

 

연습2)

답변은 하나의 질문에 여러 개가 달릴 수 있는 구조다. 그러므로 답변은 Many(많은 것)가 되고 질문은 One(하나)이 된다. 
즉, @ManyToOne은 N:1 관계라고 할 수 있다. 이렇게 @ManyToOne Annotation을 설정하면 Answer 엔티티의 question 속성과 Question 엔티티가 서로 연결된다. 참고로 실제 데이터베이스에서는 ForeignKey 관계가 생성된다.
@ManyToOne은 부모 자식 관계를 갖는 구조에서 사용한다. 여기서 부모는 Question, 자식은 Answer라고 할 수 있다. Answer의 question 속성은 답변 엔티티에서 질문 엔티티를 참조하기 위해 추가했다. 
예를 들어 답변 객체(예:answer)를 통해 질문 객체의 제목을 알고 싶다면 answer.getQuestion().getSubject()처럼 접근할 수 있다. 
하지만 이렇게 속성만 추가하면 안되고 질문 엔티티와 연결된 속성이라는 것을 명시적으로 표시해야 한다. 그래서 위 그림과 같이 question 속성에 @ManyToOne Annotation을 적어 주어야 한다.

그러면 반대로 Question 엔티티에서 Answer 엔티티를 참조할 수는 없을까? 답은 '가능하다' 이다. 답변과 질문이 N:1의 관계라면 질문과 답변은 1:N의 관계라고 할 수 있다. 이럴 때에는 @ManyToOne이 아닌 @OneToMany Annotation을 사용한다.  Question 하나에 Answer는 여러 개이므로 Question 엔티티에 추가할 답변의 속성은 List 형태로 구성해야 한다. 이를 구현하기 위해서는 Question 엔티티를 다음과 같이 수정해 준다.
    ...
    private String content;

    @OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
    private List<Answer> answerList;

Answer 엔티티 객체로 구성된 answerList(변수명)를 속성으로 추가하고 @OneToMany Annotation을 설정했다. 
이제 질문 객체(예:question)에서 답변을 참조하려면 question.getAnswerList()를 호출하면 된다. 
@OneToMany Annotation에 사용된 mappedBy는 참조 엔티티의 속성명을 의미한다. 
즉, Answer 엔티티에서 Question 엔티티를 참조한 속성명 question을 mappedBy에 전달해야 한다.

 

 

⏱ JPA 사용 시 19가지 Tip

  https://velog.io/@wisepine/JPA-%EC%82%AC%EC%9A%A9-%EC%8B%9C-19%EA%B0%80%EC%A7%80-Tip

 

 

 

다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
  • 작성자임효선 | 작성시간 24.07.10
    댓글 이모티콘
댓글 전체보기
맨위로

카페 검색

카페 검색어 입력폼