[JPA] EntityManager
JPA는 기본적으로 persistence라는 클래스를 기반으로 동작한다.
persistence-unit에 데이터베이스 연결이나 쿼리 로그 등의 설정들을 만들고 이 내용을 기반으로 EntityManagerFactory를 생성해서 사용하는 것이다.
Persistence
간단하게 persistence.xml을 만들어보자
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
<persistence-unit name="testUnit">
<properties>
<!--필수 속성-->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!--옵션-->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.use_sql_comments" value="true" />
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
persistence-unit은 하나의 DB당 하나씩 생성해서 사용하면 된다. 위의 persistence-unit name="testUnit" 를 보면 여기서 설정한 persistence-unit의 이름이 "testUnit"이라는 것을 알 수 있다.
그리고 필요한 설정들을 해주면 되는데, 여기서는
- h2데이터베이스를 사용하기 위한 설정
- 쿼리로그, sql로그 포맷 여부 등의 설정
들을 작성하였다.
EntityManagerFactory
이 persistence설정으로 EntityFactoryManager를 생성해보자.
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testUnit");
emf.close();
}
}
위의 코드를 실행하면 바로 데이터베이스와 연결이 되고 마지막 clsoe()를 통해 종료되는 것을 알 수 있다. 바로 데이터베이스와 연결되기 때문에 데이터베이스 서버가 살아있어야한다. 여기서는 h2데이터베이스를 사용하기 때문에 h2서버를 실행시킨 후 동작해야 정상적으로 동작 할 것이다.
EntityManagerFactory는 이름 그대로 EntityManager를 담는 공장이다. 이 안에서 EntityManager를 생성해서 DB와 통신하면서 데이터를 주고받을 수 있다.
이번에는 간단하게 EntityManager를 동작시키고 DB에 값을 넣는 작업까지 해보도록 하겠다.
EntityManager
JPA를 사용할 때는 기능을 하나 수행할 때마다 EntityManager가 동작해야한다.
package hellojpa;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testUnit");
EntityManager em = emf.createEntityManager();
em.close();
emf.close();
}
}
여러 스레드가 같은 EntityManager를 바라보면 문제가 생길수 있기 때문에 하나의 EntityManager에서는 하나의 스레드만 동작해야한다.
이제 EntityManager안에서 데이터를 주고받아 보자.
먼저 테이블을 먼저 만들어야한다. JPA에서 테이블은 @Entity 어노테이션을 이용해서 만들어 줄 수 있다.
Entity
메인클래스와 별도로 entity라는 패키지를 생성해서 클래스를 생성해주면 된다. 기본적으로 클래스의 이름이 테이블의 이름이 되기때문에 원하는 테이블의 이름으로 클래스를 만들어주면 된다.
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Board라는 이름의 클래스를 만들었다. 이 이름은 추후에 테이블을 생성할 때 테이블 이름으로 사용되어질 것이다.
- @Entity라는 어노테이션을 사용해야 JPA에서 이 클래스가 Entity클래스라는 것을 인지할 수 있다. Entity클래스가 DB와 직접적으로 닿아있는 클래스이기 때문에 필수적으로 입력해주어야 한다.
- @Id는 테이블의 PK를 지정하는 것이다.
- @GeneratedValue(strategy = GenerationType.AUTO)는 PK값을 자동으로 증가시키는 설정이다.
그리고 getter, setter를 이용해서 값을 넣고 뺄 수 있도록 설정해준다.
이렇게 설정을 마친 후 JpaMain클래스를 실행시키면 create table쿼리가 동작하는 것을 볼 수 있다.
로그를 살펴보면 쿼리가 표시되고 있다. 이건 처음에 persistence.xml에서 쿼리를 볼 수 있도록 설정했기 때문이다.
이렇게 하면 테이블이 생성되어진다. 매우 간단하지않는가?!
이제 테이블도 생성했으니 진짜로 데이터를 넣고 조회해보자.
기본적으로 데이터의 변경이 일어날때는 트랜잭션을 필수로 해주어야한다. 조회의 경우는 상관없지만 데이터가 변하는 삽입, 수정, 삭제의 경우에는 꼭 트랜잭션을 걸어주어야 한다.
import hellojpa.entity.Board;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testUnit");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Board board = new Board();
board.setTitle("testTitle");
em.persist(board);
Board findBoard = em.find(Board.class, 1L);
System.out.println("findBoard.getTitle = " + findBoard.getTitle());
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
앞에서 만든 Board에 값을 넣는 작업이다.
트랜잭션을 걸고 try catch문을 이용해서 코드가 제대로 동작하면 commit을 시켜주고 그렇지 않으면 rollback되도록 했다. 그리고 어떤 일이 있어도 EntityManager는 종료되야하기 때문에 finally에 종료문을 넣어줬다.
setTitle로 값을 입력한 후 persist를 사용하면 값을 넣어줄 수 있다. id값은 자동으로 증가하기 때문에 직접 넣어줄 필요가 없다.
JPA를 사용할 때 주의해야할 것은 persist를 사용한다고 바로 쿼리문이 나가는 것이 아니다. 쿼리는 트랜잭션이 종료되었을 때 DB로 보내진다. (flush()를 사용해서 강제로 쿼리를 전송시키는 방법도 있다.)
그런데 위의 코드를 보면 트랜잭션이 끝나기도 전에 find를 이용해서 데이터를 조회하는 부분이 있다. 아직 트랜잭션이 끝나지 않아서 insert문이 실행되지 않았을텐데 어떻게 이게 가능한 것일까??
이것은 JPA의 특징 중 하나인 영속성 컨테스트 때문이다. 영속성 컨테스트에 대해서는 추후에 자세히 다뤄보도록 하겠다.
이제 코드의 결과를 보도록 하자.
실행시켜보면 insert문이 나가는 것을 확인할 수 있다.
그런데 insert문 위에 System.out.println()으로 출력한 값이 먼저 나오는 것을 알 수 있다. 쿼리는 트랜잭션이 끝나는 시점에 실행되기 때문에 트랜잭션 안에서 작성한 출력문이 먼저 실행되고 쿼리문이 실행되는 것이다. 그리고 출력문이 먼저 실행될 수 있는것은 바로 영속성 때문이다.
이제 데이터가 잘 들어갔는지 h2-console에서 조회를 해보자
데이터도 잘 들어간 것을 확인할 수 있다.
'JPA' 카테고리의 다른 글
QueryDSL 사용이유와 사용방법 (0) | 2022.06.22 |
---|---|
연관관계 매핑(단방향) (0) | 2022.04.09 |
[JPA] 영속성 컨텍스트 (0) | 2022.02.01 |
[JPA] 프로젝트 생성 (0) | 2022.01.31 |
[JPA] JPA 알아보기 (0) | 2022.01.31 |
댓글
이 글 공유하기
다른 글
-
연관관계 매핑(단방향)
연관관계 매핑(단방향)
2022.04.09 -
[JPA] 영속성 컨텍스트
[JPA] 영속성 컨텍스트
2022.02.01 -
[JPA] 프로젝트 생성
[JPA] 프로젝트 생성
2022.01.31 -
[JPA] JPA 알아보기
[JPA] JPA 알아보기
2022.01.31