본문 바로가기
Framework and Tool/JPA

JPA - 엔티티 매니저, 트랜잭션, JPQL

by ocwokocw 2021. 6. 23.

- 참조: 자바 ORM 표준 JPA 프로그래밍

- 이 글에 나오는 코드는 https://ocwokocw.tistory.com/118 글을 먼저 읽었다는 점을 전제로 작성한다.

- Java example

엔티티 매니저를 살펴보기 전에 우리가 DB를 다룰 때 일반적으로 사용하는 등록, 수정, 삭제, 조회 연산 예제 코드를 살펴보자. 아래 코드는 엔티티 매니저 설정, 트랜잭션 관리, 비즈니스 로직 3 부분으로 구성 되어있다.

 

@SpringBootApplication
public class JpaApplication {
	
	public static void main(String[] args) {
		SpringApplication.run(JpaApplication.class, args);
		
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpatest");
		EntityManager em = emf.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		
		try {
			
			tx.begin();
			logic(em);
			tx.commit();
			
		} catch (Exception e) {
			tx.rollback();
		} finally {
			em.close();
		}
		
		emf.close();
	}
	
	private static void logic(EntityManager em) {
		
		String id = "id1";
		
		Member member = new Member();
		member.setId(id);
		member.setUserName("ocwokocw");
		member.setAge(33);
		
		em.persist(member);
		
		member.setAge(20);
		
		Member findMember = em.find(Member.class, id);
		System.out.println("findMember: " + findMember);
		
		List<Member> members = em.createQuery("select m from Member m",  Member.class)
				.getResultList();
		System.out.println("member size: " + members.size());
		
		em.remove(member);
	}
}

 

위의 코드는 META-INF/persistence.xml 로 설정했을 때 EntityManagerFactory bean을 얻어오는 코드이다. 만약 application.properties 로 설정했다면 EntityManagerFactory 를 얻어올 때 아래처럼 Type 기반으로 ApplicationContext 에서 얻어와야 한다.

 

ApplicationContext appCtx = SpringApplication.run(JpaApplication.class, args);
		
EntityManagerFactory emf = appCtx.getBean(EntityManagerFactory.class);

 

위의 코드를 수행하면 아래와 같이 콘솔에 찍힌다. Mybatis 로 SQL 을 작성하지 않았는데도 SQL 문이 자동으로 출력되었다. 엔티티 매니저부터 천천히 살펴보도록 하자.

 

findMember: Member [id=id1, userName=ocwokocw, age=20]
Hibernate: 
    /* insert com.example.demo.member.Member
        */ insert 
        into
            MEMBER
            (age, NAME, ID) 
        values
            (?, ?, ?)
Hibernate: 
    /* update
        com.example.demo.member.Member */ update
            MEMBER 
        set
            age=?,
            NAME=? 
        where
            ID=?
Hibernate: 
    /* select
        m 
    from
        Member m */ select
            member0_.ID as id1_0_,
            member0_.age as age2_0_,
            member0_.NAME as name3_0_ 
        from
            MEMBER member0_
member size: 1
Hibernate: 
    /* delete com.example.demo.member.Member */ delete 
        from
            MEMBER 
        where
            ID=?

- 엔티티 매니저

위의 예제코드에서 EntityManager 를 얻기까지의 동작과정은 아래와 같다.

  • Persistence 가 META-INF/persistence.xml 설정 정보를 조회한다.
  • 이 정보를 기반으로 EntityManagerFactory 를 생성한다.
  • EntityManagerFactory 에서 EntityManager 를 생성한다.

EntityManagerFactory 는 JPA 구현체에 따라 DB 커넥션 풀 생성과 같은 작업을 하므로 비용이 아주 크다. 따라서 어플리케이션에서 한번만 생성한 후 공유해서 사용한다.

 

EntityManager 를 통해서 엔티티를 DB 에 CRUD 할 수 있으며 내부에 데이터소스를 유지하면서 DB와 통신한다. EntityManager 는 커넥션과 관련이 있으므로 스레드간 공유나 재사용을 하면 안된다.

 

사용이 끝난 EntityManager는 close로 종료해줘야 하고, 어플리케이션을 종료할 때에는 EntityManagerFactory를 종료한다.


- 트랜잭션과 비즈니스 로직

트랜잭션을 시작하려면 EntityManager 에서 트랜잭션 API 를 얻어와야 한다. main 코드를 보면 tx 에서 commit 과 rollback 을 함을 알 수 있다.

 

logic 메소드 안에서는 생성, 수정, 조회, 삭제를 하는데 동작을 간략하게 알아보면 아래와 같다.

  • 생성: 엔티티를 저장하려면 EntityManager 의 persist() 메소드에 엔티티를 넘기면 된다.
  • 수정: 수정이 조금 특이한데, setAge setter 로 변경만 했을 뿐 업데이트 동작으로 보이는 메소드를 호출하지 않았다. JPA 는 엔티티가 변경되는 과정을 추적하여 update 구문을 만들어서 수행한다.
  • 삭제: remove() 메소드를 호출하여 해당 엔티티를 삭제한다.
  • 단건 조회: @Id 로 지정한 필드로 find() 메소드로 단건을 조회한다. 

- JPQL

위에서 CUD 와 단건조회는 엔티티 객체를 중심으로 개발하면 DB 처리는 JPA 가 해주었다. 하지만 검색 쿼리는 얘기가 다르다. 만약 엔티티 객체를 대상으로 검색하려면 DB 의 모든 데이터를 애플리케이션의 엔티티 객체로 변경해서 검색해야 한다.

 

JPA 는 필요한 데이터만 검색할 수 있게 JPQL 이라는 쿼리언어로 이 문제를 해결한다. SQL 과 문법이 거의 유사하긴 하지만 가장 큰 차이는 JPQL의 경우 엔티티 객체를 대상으로, SQL은 데이터베이스 테이블을 대상으로 쿼리한다고 할 수 있다.

 

JPQL을 사용한 문자열을 자세히 보면 "select m from Member m" 에서 Member 엔티티 객체이지 H2 의 MEMBER 테이블이 아니다.

댓글