본문 바로가기

백엔드 개발/JPA

[하이버네이트 유저 가이드 파헤치기] 아키텍처 - 1

반응형
이번 글은 원문을 번역하는 방식으로 하고, 추가적으로 Spring boot에서 EntityManager를 가져오는 방법을 추가적으로 다루고자한다.
번역은 의역으로 하되, 번역기의 의미 전달이 더 명료한 경우 그대로 구글 번역기의 번역을 사용하거나 다듬어서 차용했다.
유저 가이드의 설명이 부족한 경우에는 필자의 지식을 토대로 해설을 넣었고, 잘 모르는 내용은 참고 자료에 첨부된 자료들을 참고해서 작성했다.

하이버네이트 아키텍처에 대하여

 

두 영역 사이에 알맞게 위차한 하이버네이트

위 다이어그램에서 알 수 있듯이 ORM 솔루션으로써 하이버네이트는 Java application data access layer와 RDBMS 사이에 알맞게 위치한다. 자바 애플리케이션은 하이버네이트 API를 데이터베이스에서 도메인 데이터를 불러오고, 저장하고, 질의하고, 그 밖의 동작을 위해 사용한다. 이번 챕터에서 우리는 간략하게 필수적인 하이버네이트 API들을 소개할 것이다. 자세한 사항은 나중에 논의할 것이다.

 

필자 해설
Java application data access layer란?
  - DAO(Data Access Object)
  - JPA에서 Entity
  - Spring Data Jpa에서의 Repository

도메인 데이터란?
  - 도메인 모델과 대응하는 DB Table의 Row
  - 도메인 모델 : 소프트웨어로 문제를 해결하려는 영역과 그 영역을 객체로 모델링한 것.

 

원문
Hibernate, as an ORM solution, effectively "sits between" the Java application data access layer and the Relational Database, as can be seen in the diagram above. The Java application makes use of the Hibernate APIs to load, store, query, etc. its domain data. Here we will introduce the essential Hibernate APIs. This will be a brief introduction; we will discuss these contracts in detail later.

 

아래 다이어그램에서 볼 수 있듯이, JPA 제공자(구현체)로써,  하이버네이트는 JPA API 명세들을 구현하고 JPA 인터페이스들과 하이버네이트가 구현한 명세들을 연관짓는다.

 

원문
As a JPA provider, Hibernate implements the Java Persistence API specifications and the association between JPA interfaces and Hibernate specific implementations can be visualized in the following diagram:

 

JPA 명세와 하이버네이트 구현체의 연관 관계

 

SessionFactory (org.hibernate.SessionFactory)

데이터베이스에 도메인 모델을 맵핑하는 thread-safe한 ( 그리고 불변의) 표현이다.

org.hibernate.Session 인스턴스의 팩토리로 동작한다.

JPA의 EntityManagerFactory에 해당하는 명세이며 기본적으로 이 두 가지는 동일한 SessionFactory 구현으로 수렴됩니다.

(위 다이어그램을 보면, EntityManagerFactory와 SessionFactory는 인터페이스이고 SessionFactoryImpl이 구현체이다. 두 인터페이스를 SessionFactoryImpl가 구현한다는 뜻)

SessionFactory 하나를 생성하기 위해서는 매우 비싼값을 치러야한다(시스템 자원을 많이 소모한다).  그래서, 주어진 어떤 데이터베이스에 대해 애플리케이션은 오직 하나의 연관된 SassionFactor를 가져야한다.

SessionFactory는 2단계 캐시, 커넥션 풀, 트랜잭션 시스템 통합 등과 같이 Hibernate가 모든 세션에서 사용하는 서비스를 유지 관리한다.

원문
SessionFactory (org.hibernate.SessionFactory)
A thread-safe (and immutable) representation of the mapping of the application domain model to a database. Acts as a factory for org.hibernate.Session instances. The EntityManagerFactory is the JPA equivalent of a SessionFactory and basically, those two converge into the same SessionFactory implementation.
A SessionFactory is very expensive to create, so, for any given database, the application should have only one associated SessionFactory. The SessionFactory maintains services that Hibernate uses across all Session(s) such as second level caches, connection pools, transaction system integrations, etc.

 

Session (org.hibernate.Session)

싱글 스레드로 동작시켜야하는, 개념적으로 짧은 생명주기를 가진 오브젝트로 모델링된 "작업 단위"이다. 

JPA에서는 Session을 EntityManager라고 이름 짓는다.

 

뒷 단에서, 하이버네이트 Session은 JDBC java.sql.Connection을 감싸고 org.hibernate.Transaction의 팩토리로 동작한다.

하이버네이트 Session은 일반적으로 애플리케이션 도메인 모델의 "반복가능한 읽기" Persistence context(first level cache)를 유지관리한다.

 

필자 해설
싱글 스레드로 동작시켜야하는 이유
EntityManager(하이버네이트에서는 Session)은  Persistence context를 유지관리한다.
Persistence context는 엔티티에 대한 변경 내용들을 반영하는 역할을 한다.
그런데 동일한 Persistence context를 여러 스레드에서 접근해서 엔티티를 수정하면 안 된다.
유저가이드에서도 나와있듯이 EntityManager( 혹은 Persistence context)가 짧은 생명 주기를 가지는 (주로 트랜잭션 단위당 생성하고 삭제한다.) 이유이기도 하다.
여러 스레드에서는 EntityManagerFactory(SessionFactory)를 공유하면서 EntityManager를 생성해서 사용하면 된다.

"repeatable read(반복가능한 읽기)" 란?

JPA에서 동일한 엔티티 객체(Id가 동일한 엔티티 객체)를 동일 트랙잭션에서 여러 번 호출할 때, 최초에 한 번 DB에 쿼리를 해서 데이터를 조회하면 Persistence context에 객체를 저장했다가 그 이후 부터는 Persistence context내에서 조회하는데. 이 때, 일종의 캐시 역할을 하게된다.
이것을 "repeatable read(반복가능한 읽기)"라고 표현하고 있다.

 

원문
Session (org.hibernate.Session)
A single-threaded, short-lived object conceptually modeling a "Unit of Work" (PoEAA). In JPA nomenclature, the Session is represented by an EntityManager.

Behind the scenes, the Hibernate Session wraps a JDBC java.sql.Connection and acts as a factory for org.hibernate.Transaction instances. It maintains a generally "repeatable read" persistence context (first level cache) of the application domain model.

 

Transaction (org.hibernate.Transaction)

싱글 스레드로 동작해야하는, 짧은 생명주기의, 애플리케이션의 개별 물리적인 트랜잭션 영역을 구분짓는 오브젝트이다.

JPA의 EntityTransaction과 동일하고 사용 중인 트랜잭션 시스템(JDBC 또는 JTA)에서 애플리케이션 격리하기위한 추상적인 API로 동작한다.


필자 해설
아래는 EntityManager에서 Transaction을 직접 호출해서 트랜잭션 영역을 구분짓는 코드이다.
Transaction 객체를 사용해서 애플리케이션에서 트랜잭션을 적용하고 싶은 영역을 구분할 수 있는 것이다.
그리고 시스템에서 사용하고 있는 DB와 관계없이 추상화되어서 적용이 된다.
    EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
    EntityManager em = factory.createEntityManager();

 // Creating and mutating object BEFORE begin() is called
    Todo todo = new Todo();
    todo.setSummary("sum");
    todo.setDescription("desc");

    // create new todo
    em.getTransaction().begin();
    em.persist(todo);
    em.getTransaction().commit();
그리고 만약, Spring Data JPA를 사용한다면 비즈니스 로직(주로 Service 객체의 메소드)에 @Transactional 어노테이션을 표기하면 애플리케이션에 트랜잭션이 적용된다. 이렇게 물리적인 트랜잭션 시스템에서 애플리케이션이 분리되고 추상적으로 동작하는 것이다.

원문
Transaction (org.hibernate.Transaction)
A single-threaded, short-lived object used by the application to demarcate individual physical transaction boundaries. EntityTransaction is the JPA equivalent and both act as an abstraction API to isolate the application from the underlying transaction system in use (JDBC or JTA).

 

Spring boot에서 EntityManager 가져오기

Spring boot + Spring Data Jpa를 조합해서 사용하면 EntityManager를 직접 가져와서 사용하는 경우는 별로 없기는하다.

하지만 소프트웨어 요구사항은 광범위하고 직접 EntityManager를 호출해서 커스텀 레포지토리를 만드는 상황이 올 수도 있다.

Spring boot에서는 application.properties 혹은 application.yml 파일에 db 관련 정보를 아래와 같이 넣으면 DataSource 객체를 만들어서 EntityManager를 알아서 생성해준다.

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=mysqluser
spring.datasource.password=mysqlpass
spring.datasource.url=jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true

 

위 properties 파일을 입력한 것은 아래 코드와 같이 DataSource 객체를 생성하는 과정을 추상화한 것이라고 보면 된다.

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();

    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUsername("mysqluser");
    dataSource.setPassword("mysqlpass");
    dataSource.setUrl(
      "jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true"); 
    
    return dataSource;
}

 

위와 같은 설정을 다 했다면 @PersistenceContext 라는 어노테이션을 통해서 스프링 빈으로 등록된 EntityManager를 주입 받을 수 있다.

아래와 같은 꼴이다.

    @PersistenceContext
    EntityManager em;

좀 더 상세한 예제로 DAO를 EntityManager를 이용해 만든 예제이다.

The DAO with JPA and Spring에서 가져왔다.

 

public abstract class AbstractJpaDAO< T extends Serializable > {

   private Class< T > clazz;

   @PersistenceContext
   EntityManager entityManager;

   public final void setClazz( Class< T > clazzToSet ){
      this.clazz = clazzToSet;
   }

   public T findOne( long id ){
      return entityManager.find( clazz, id );
   }
   public List< T > findAll(){
      return entityManager.createQuery( "from " + clazz.getName() )
       .getResultList();
   }

   public void create( T entity ){
      entityManager.persist( entity );
   }

   public T update( T entity ){
      return entityManager.merge( entity );
   }

   public void delete( T entity ){
      entityManager.remove( entity );
   }
   public void deleteById( long entityId ){
      T entity = findOne( entityId );
      delete( entity );
   }
}

자세한 사용예나 해설은 원문을 참고하길 바란다.

 

혹시 Spring에서 EntityManager 설정을 하길 원하는 독자는 아래 글을 참고하길 바란다.

https://www.baeldung.com/the-persistence-layer-with-spring-and-jpa

 


Hibernate User Guide 원문

https://docs.jboss.org/hibernate/orm/5.5/userguide/html_single/Hibernate_User_Guide.html#architecture


참고자료

https://stackoverflow.com/questions/4161558/what-is-the-difference-between-data-access-layer-and-data-access-object

https://4legs-study.tistory.com/167

https://perfectacle.github.io/2018/01/14/jpa-entity-manager-factory/?utm_source=pocket_mylist

 

entityTransaction

https://stackoverflow.com/questions/31432191/what-is-the-purpose-of-jpa-entitytransaction

https://stackoverflow.com/questions/42846240/jpa-entitytransaction-creating-and-persisting-a-new-object

 

datasource

https://www.baeldung.com/the-persistence-layer-with-spring-and-jpa

https://perfectacle.github.io/2018/01/14/jpa-entity-manager-factory/?utm_source=pocket_mylist

반응형