JPA 공부를 위한 프로젝트 생성 정보
DB: h2 database
build tool: Maven
Java: version 11
pom.xml을 이용해 라이브러리를 추가하고, persistence.xml는 JPA 설정 파일에 해당한다.
- /META-INF/persistence.xml 위치에 설정 파일을 저장한다.
- persistence-unit name으로 이름을 지정
- javax-persistence: JPA 표준 속성을 의미
- hibernate: 하이버네이트 전용 속성을 의미(하이버네이트는 JPA 구현체중 하나)
property 태그를 이용해 속성 정보를 하나하나 지정하는 것 같다.
- user: sa
- password: 없음
- url: jdbc:h2:tcp://localhost/~/test
- dialect는 방언으로 어떤 RDBMS의 sql을 사용할 것인지를 지정한다고 보면 된다. (여기서는 h2 dialect를 사용)
데이터베이스 방언
- JPA는 특정 데이터베이스에 종속되지 않는다.
- 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다르다.
- 가변 문자: MySQL은 varchar, Oracle은 varchar2를 사용
- 문자열 자르는 함수: MySQL은 substring(), Oracle은 substr()
- 페이징: MySQL은 LIMIT, Oracle은 ROWNUM
- 방언은 SQL 표준을 지키지 않는 특정 데이터베이스만의 고유 기능을 의미한다.
hibernate는 40가지 이상의 데이터베이스 방언을 지원하기 때문에, 현업에서 사용하는 대부분의 데이터베이스의 방언을 지원한다고 볼 수 있다.
따라서 다양한 database로의 전환이 쉽게 가능함
JPA의 구동 방식
JPA는 다음과 같은 방식으로 동작한다.
- Persistence 객체가 persistence.xml 파일을 확인해서, 설정 정보를 조회한다.
- 이를 이용해 Persistence 객체가 EntityManagerFactory 클래스를 생성한다.
- 생성된 EntityManagerFactory가 필요할 때마다 EntityManager를 생성
위 코드와 같이 Persistence 클래스에서 createEntityManagerFactory method를 이용해 이전에 "hello"라고 지정한 persistenceUnit의 EntityManagerFactory를 생성한다.
이후 EntityManagerFactory를 이용해 필요할 때 EntityManager를 생성할 수 있고, 사용 후에는 EntityManager를 close 한다.
애플리케이션 종료 시에는 EntityManagerFactory도 close method를 호출해 종료시켜야 한다.
JPA를 이용해 간단한 예제를 만들어보자
DB connection을 얻어 query를 보내고 종료하는 일관적인 단위의 작업을 수행할 때마다 EntityManager를 꼭 만들어줘야 한다고 함
DB에 table을 만들고, 객체를 생성한 다음 아래의 예시코드를 수행해 보면
이것은 JPA에서는 transaction이란 단위가 매우 중요하기 때문, JPA에서 모든 데이터를 변경하고 조회하는 작업은 꼭 transaction 안에서 작업을 해야 한다.
그래서 이렇게 고쳐줘야 한다.
transaction의 begin method와 commit method를 이용해, 일련의 과정이 transaction 안에서 발생하도록 변경해 준다.
위와 같이 실행된 sql문이 보이는 것은 hibernate.show_sql 속성을 true로 설정했기 때문이다. (persistence.xml 파일에서)
이렇게 query문을 직접 작성하지 않고, 매핑만 해줘도 JPA가 알아서 해주는 편리한 결과를 확인할 수 있다.
그런데, 어떻게 어느 테이블인지 지정도 안 하고, 테이블의 어떤 속성에 어떤 필드의 값이 들어갈지 설정도 안 했는데 잘 들어간 거지?? -> JPA의 관습 덕분이라고 한다.
지금은 객체의 필드 명과 테이블의 속성 이름이 같아서 자동으로 매핑된 경우이고
다른 이름을 갖는 경우라면, 어노테이션을 이용해서 직접 매핑시킬 수도 있다고 한다.
@Entity
@Table(name = "Member")
public class Member{
@Id
private Long id;
@Column(name = "username")
private String name;
...
}
이런 식으로 직접 이름을 매핑할 수도 있다.
코드를 조금 리팩토링 하면 다음과 같이 바꿔줄 수 있다.
원래 상태의 코드는 중간에서 문제가 발생할 시에 EntityManager와 EntityManagerFactory를 제대로 닫아주지 못했다.
그래서 try, catch 구문을 추가해 문제가 발생해도 transaction을 rollback 하고, connection이 제대로 닫힐 수 있도록 코드를 수정한 개념 (훨씬 안정적이다)
사실 위의 코드는 정석으로 JPA만 사용한 경우이고, 실제로 사용할 때는 Spring과 함께 사용하기 때문에, em.persist만 사용하면, 나머지 코드는 알아서 실행해 준다고 함
위와 같이 아주 간단하게 조회 기능도 구현이 가능하다. (EntityManger 객체의 find method만 사용하면 끝)
수정이 가장 야무지다.
수정은 그냥 find로 객체를 찾은 다음에 setter로 수정하고, persist 하면 되겠지..??
→ persist 할 필요도 없음!
위 결과처럼 JPA가 transaction을 commit 하기 직전에 변경 사항을 감지해서 udpate query를 직접 보내준다.
이는 JPA를 통해서 entity를 가져오면, 이걸 JPA에서 관리해 주기 때문이라고 한다.
주의할 점
- EntityManagerFactory는 하나만 생성해서 애플리케이션 전체에서 공유한다!
- EntityManager는 thread 간에 공유하지 않는다. (사용하고 버려야)
- JPA의 모든 데이터 변경은 transaction 안에서 실행한다.
- 모든 DB가 내부적으로 transaction 단위로 일을 처리하기 때문
- 우리가 단건 query로 DB를 변경하는 것도 내부적으로는 transaction으로 처리된다고 함
JPQL
EntityManager로 식별자를 이용해 간단하게 entity를 조회하는 방법도 있지만,
만약 "나이가 18살 이상인 회원을 모두 검색하자!!" 같은 작업을 처리하고 싶다면??
이럴 때, JPQL이라는 것을 사용한다고 한다.
- JPA를 사용하면, entity 객체를 중심으로 개발
- 문제는 검색 쿼리이다.
- 검색을 할 때에도 테이블이 아닌 entity 객체를 대상으로 검색하게 된다.
- 테이블에서 가져오면, JPA의 사상이 깨짐
- 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
- 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국에는 검색 조건이 포함된 SQL이 필요
이러한 이유로 테이블이 아닌 entity 객체를 대상으로 query를 짤 수 있는 문법이 들어간 것이고, 이것이 JPA에서 제공하는 JPQL이다.
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다!
- SQL과 문법이 유사하며, 기본적으로 select, from, where, group by, having, join을 모두 지원함
- JPQL은 entity 객체를 대상으로 쿼리
- SQL은 db table을 대상으로 쿼리
이렇게 JPQL을 사용하면, DB에 의존적이지 않게 될 수 있다. (DBMS를 바꿔도 똑같이 동작 가능하다)
이후 챕터에서 더 자세하게 다뤄보자
'BackEnd > java spring' 카테고리의 다른 글
[JPA] 엔티티 매핑 (0) | 2023.01.27 |
---|---|
[JPA] 영속성 컨텍스트에 대한 정리 (0) | 2023.01.21 |
[JPA] Java 표준 ORM인 JPA란? (0) | 2023.01.19 |
[Spring] 빈 스코프 알아보기 (0) | 2023.01.07 |
[Spring] 빈 생명주기 콜백 (0) | 2023.01.06 |
댓글