no image
[JPA] 영속성 관리, 영속성 컨텍스트
웹 어플리케이션에 엔티티매니저 팩토리는 하나만 만들고, 요청이 들어올때마다 엔티티 매니저를 생성한다. 그리고 DB 내부적으로 connectionPool을 사용한다. 영속성 컨텍스트(Persistence Context) ? 영속성 컨텍스트란 "엔티티를 영구 저장하는 환경"이라는 뜻이다. 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.EntityManager.persist(entity);사실 위 코드는 DB에 엔티티를 저장한다기 보다는 영속성 컨텍스트를 통해 엔티티를 영속화 한다는 의미이다. ( 즉, 영속성 컨텍스트에 저장 한다는 의미이다. ) 영속성 컨텍스트는 논리적인 개념이며, 눈에 보이지 않는다. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근한..
2024.05.03
JPA
[JPA] Entity와 EntityManager, EntityManagerFactory
EntityDB에 member라는 테이블이 있고, 해당 테이블의 스키마가 다음과 같다고 해보자.create table Member (id bigint not null, name varchar(255), primary key(id));그렇다면 엔티티는 다음과 같이 만들 수 있다. 엔티티는 JPA가 관리할 객체이며, DB의 테이블과 매핑된다.import jakarta.persistence.Entity;import jakarta.persistence.Id;@Entitypublic class Member { @Id private Long id; private String name; //Getter, Setter ...}기본적으로 JPA는 모든 필드를 불러오게 끔 구현되어 있다. ..
2024.05.03
JPA
no image
[JPA] 데이터베이스 방언, Dialect
JPA는 특정 데이터베이스에 종속적이지 않은 기술이다. 따라서 다른 데이터베이스로 손쉽게 교체할 수 있다.  SQL은 표준 SQL인 ANSI SQL이 있으며, ANSI SQL 이외에 각 DBMS Vendor인 MS-SQL, Oracle, MySQL, PostgreSQL에서 자신만의 기능을 추가한 SQL이 있다. ANSI SQL이 모든 DBMS에서 공통적으로 사용가능한 핵심 표준 SQL이지만, 여러 제품의 DBMS에서는 자신만의 독자적인 기능을 위해 추가적인 SQL을 만들었다. 즉,  각 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다르다는 문제점이 있다. 예를 들면 다음과 같은 차이점들이 존재한다. 데이터 타입 : 가변 문자 타입으로 MySQL은 VARCHAR, 오라클은 VARCHAR2를 사용한다..
2024.05.03
JPA

웹 어플리케이션에 엔티티매니저 팩토리는 하나만 만들고, 요청이 들어올때마다 엔티티 매니저를 생성한다. 그리고 DB 내부적으로 connectionPool을 사용한다.

 

영속성 컨텍스트(Persistence Context) ?

 

영속성 컨텍스트란 "엔티티를 영구 저장하는 환경"이라는 뜻이다. 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

EntityManager.persist(entity);

사실 위 코드는 DB에 엔티티를 저장한다기 보다는 영속성 컨텍스트를 통해 엔티티를 영속화 한다는 의미이다. ( 즉, 영속성 컨텍스트에 저장 한다는 의미이다. )

 

영속성 컨텍스트는 논리적인 개념이며, 눈에 보이지 않는다. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근한다. 

 

엔티티의 생명주기

 

엔티티에는 4가지 상태가 존재한다.

  • 비영속 ( new/transient ) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
  • 영속 ( managed ) : 영속성 컨텍스트에 관리 되는 상태       // persist 하고 나면 영속 상태가 된다.
  • 준영속 ( detached ) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제 ( removed ) : 삭제된 상태

엔티티의 생명 주기

 

비영속

 

엔티티 객체를 생성하면, 이는 순수한 객체 상태이며 아직 저장하지 않았기 때문에 영속성 컨텍스트와 관련이 없다. 이를 비영속 상태라고 한다.

//EntityManager.persist(member)를 호출하기 전, 비영속 상태이다.
            Member member = new Member();
            member.setId(100L);
            member.setName("HelloJPA");

비영속

영속

엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장한다. 영속성 컨텍스트가 관리하는 엔티티를 영속 상태라고 한다. 결국 영속 상태라는 것은 영속성 컨텍스트에 의해 관리된다는 뜻이다. 그리고 EntityManager.find()나 JPQL을 사용해서 조회한 엔티티도 영속성 컨텍스트가 관리하는 영속 상태이다. 

//객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername(“회원1”);

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

//객체를 저장한 상태 (영속 상태)
em.persist(member);

차영속 상태 ( 영속성 컨텍스트에 의해 관리되는 상태 )

준영속

영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 된다. 

엔티티를 준영속 상태로 만드려면, 엔티티 매니저의 detach() 메서드를 호출하면 된다. 아니면 엔티티 매니저의 close() 메서드를 호출해서 영속성 컨텍스트를 완전히 닫아버리는 방법도 있다. 아니면 clear()를 호출해서 영속성 컨텍스트를 초기화 하는 방법도 있다.

삭제

엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.

EntityManager.remove(member); //객체를 삭제한 상태 (삭제)

 

영속성 컨텍스트의 특징

 

영속성 컨텍스트와 식별자 값

영속성 컨텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본 키와 매핑한 값)으로 구분한다. 따라서 영속상태는 식별자 값이 반드시 있어야 한다. 식별자 값이 없다면 예외가 발생한다.

 

영속성 컨텍스트와 데이터베이스 저장

영속성 컨텍스트에 엔티티를 저장하면 바로 데이터 베이스에 저장되는 것이 아니다. 보통은 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영하는데 이것을 플러시(flush)라고 한다.

 

영속성 컨텍스트의 이점 -> 애플리케이션과 DB 사이에 중간 계층이 하나 있는 것과 같다.

  • 1차 캐시
  • 동일성 보장 
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지 (Dirty Checking)
  • 지연 로딩 (Lazy Loading)

 

엔티티 조회, 1차 캐시

 

영속성 컨텍스트는 내부에 1차 캐시라는 것이 존재한다. 영속상태의 엔티티는 모두 1차 캐시에 저장된다. DB의 PK값을 @Id로, Entity 객체 값을 Map 형태로 1차 캐시에 저장하고 있는 것이다. 아래 코드를 실행하면, 1차 캐시에 아래와 같이 저장되게 된다.

//엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//엔티티를 영속, 1차 캐시에 저장됨
em.persist(member);

그리고 추후에 해당 객체를 찾게 된다면, JPA는 DB에서 값을 찾아오는 것이 아니라, 1차 캐시에서 값을 찾아온다.

//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");

만약, 1차 캐시에 찾는 값이 없다면?

Member findMember2 = em.find(Member.class, "member2");

엔티티 매니저는 데이터 베이스를 조회해서 엔티티를 생성한다. 그리고 이를 바로 반환하는 것이 아니라, 1차 캐시에 저장하고 영속 상태의 엔티티를 반환한다. 따라서 이후에 또 member2 엔티티를 조회한다면, 1차 캐시에서 바로 불러올 수 있다. 

 

그러나 데이터 베이스 하나의 트랜잭션에서만 이점을 얻을 수 있기에 사실 이 기능은 큰 도움이 되지 않는다. 왜냐하면 엔티티 매니저는 하나의 요청 단위로 생성하게 되며, 요청이 끝나면 엔티티 매니저를 지우게 된다. 엔티티 매니저를 지우게 되면 해당 영속성 컨텍스트도 지워지기 때문에 큰 도움이 되지 않는 것이다.

import jakarta.persistence.*;

import java.util.List;

public class JpaMain {

    public static void main(String[] args) {

        //factory는 애플리케이션 로딩 시점에 딱 한번 만들어야 됨.
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence-unit에서 지정함 이름을 넘긴다.
        EntityManager em = emf.createEntityManager(); //엔티티 매니저 하나 받음 ( 마치 자바 컬렉션이라고 생각 ( 내 객체를 대신 저장해주는 ))
        //code
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {

            Member findMember1 = em.find(Member.class, 100L);
            Member findMember2 = em.find(Member.class, 100L);

            tx.commit();//트랜잭션 커밋 시, 쿼리가 날라가게 된다.
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

이 경우에도, 새로운 영속성 컨텍스트가 만들어졌기 때문에 첫번째 find 호출 시, findMember1의 경우 1차 캐시에 값이 없기 때문에 DB에서 값을 찾아오게 된다. 따라서 select 쿼리가 나가게 되고, 찾아온 값을 1차 캐시에 저장후 반환하게 된다.

 

이후부터는 똑같은 값을 조회할 경우, 1차 캐시에 등록되어 있기 때문에 select 쿼리가 나가지 않는다.

영속 엔티티의 동일성 보장

동일성이란, 두 객체의 인스턴스가 같은 것 즉, 참조값이 같음을 의미한다. em.find(Member.class, "member1")를 아무리 반복해서 호출해도 JPA는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환한다. 따라서 a와 b는 같은 인스턴스이고 결과는 당연히 true를 반환한다. 따라서 영속성 컨텍스트는 엔티티의 동일성을 보장한다.

 

1차 캐시로 반복가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다는 장점이 있다. ( 단, 같은 트랜잭션 안에서 실행시 ) -> 트랜잭션 레벨이 Read Commited 여도 1차 캐시를 이용해 Repeatable read 등급의 이점을 챙길 수 있다. 트랜잭션 격리 수준에 대해 알아보면 좋을 것 같다..

 

엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연

 

엔티티 매니저는 트랜잭션을 커밋하기 전까지 데이터베이스에 엔티티를 저장하지 않고내부 쿼리 저장소에 INSERT SQL을 차곡차곡 모아둔다. 그리고 트랜잭션을 커밋할때  모아둔 쿼리를 데이터베이스에 보내는데 이것을 트랜잭션을 지원하는 쓰기 지연이라 한다. -> 쿼리를 커밋하는 시점에 한번에 날리는 것을 의미한다.

우선 회원 A를 영속화한 후, 영속성 컨텍스트는 1차 캐시에 회원 엔티티를 저장하면서 동시에 회원 엔티티 정보로 등록 쿼리를 만든다. 그리고 만들어진 등록 쿼리를 쓰기 지연 SQL 저장소에 보관한다.

 

그 다음으로는 회원 B를 영속화한 후, 마찬가지로 회원 엔티티 정보로 등록 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보관한다. 현재 쓰기 지연 SQL 저장소에 등록 쿼리가 2건 저장된 것이다.

 

 

 

트랜잭션을 커밋하면 엔티티 매니저는 우선 영속성 컨텍스트를 플러시한다. 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업이다. 이때 등록, 수정, 삭제한 엔티티를 데이터베이스에 반영한다.

쓰기 지연 SQL 저장소에 모인 쿼리를 데이터 베이스에 보내는 것이다. 이렇게 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화한 후에 실제 데이터베이스 트랜잭션을 커밋한다.

 

 

변경 감지 ( Dirty Checking )

 

Jpa는 따로 수정을 위한 메서드가 존재하지 않는다. 그냥 엔티티를 수정하면 된다. (set 메서드 등을 이용해서.. ) 그리고 이것을 변경 감지(Dirty Checking)이라고 한다. 

 

JPA는 트랜잭션을 커밋하는 시점에 내부적으로 flush를 호출하게 된다. flush를 호출하게 되면 엔티티와 스냅샷을 비교하게 된다. 1차 캐시에 스냅샷이라는 것도 있다. ( 값을 읽어온 최초 시점에 그 상태를 스냅샷 떠논 것이다. ) 값을 비교해서 수정된게 있다면, SQL을 만들어서 지연 저장소에 저장해두게 된다. 그리고 그것을 데이터베이스에 쿼리를 날려서 변경하고 최종적으로 커밋을 호출하게 된다.

 

 

플러시

 

영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것을 말한다. ( 영속성 컨텍스트의 쿼리들을 DB에 날리는 것을 의미한다. ) 보통 데이터베이스 커밋이 일어나게 되면, 플러시가 실행된다.

 

영속성 컨텍스트를 플러시하는 방법

 

1. em.flush() //직접 호출
2. 트랜잭션 커밋  //플러시 자동 호출된다.
3. JPQL 쿼리 실행 //플러시 자동 호출

import jakarta.persistence.*;

import java.util.List;

public class JpaMain {

    public static void main(String[] args) {

        //factory는 애플리케이션 로딩 시점에 딱 한번 만들어야 됨.
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence-unit에서 지정함 이름을 넘긴다.
        EntityManager em = emf.createEntityManager(); //엔티티 매니저 하나 받음 ( 마치 자바 컬렉션이라고 생각 ( 내 객체를 대신 저장해주는 ))
        //code
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            //영속
            Member member = new Member(200L, "member200");
            em.persist(member);

            em.flush(); //플러시 강제 호출

            System.out.println(" ==================== ");
            tx.commit();//트랜잭션 커밋 시, 쿼리가 날라가게 된다.
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

이렇게 하게 된다면 강제로 플러시를 호출하게 된다. 이 경우 커밋 시점시 쿼리를 날리는게 아니라 ====== 전에 쿼리가 날라가게 됨을 콘솔창에서 확인할 수 있다.

1차 캐시에서 조회

플러시를 호출하게 되면, 플러시에 있는 내용들이 사라질까? 아니다. 지연 저장소에 있는 쿼리문들이 날라가는 것이다.

'JPA' 카테고리의 다른 글

[JPA] Entity와 EntityManager, EntityManagerFactory  (0) 2024.05.03
[JPA] 데이터베이스 방언, Dialect  (0) 2024.05.03

Entity

DB에 member라는 테이블이 있고, 해당 테이블의 스키마가 다음과 같다고 해보자.

create table Member (
id bigint not null,
    name varchar(255),
    primary key(id)
);

그렇다면 엔티티는 다음과 같이 만들 수 있다. 엔티티는 JPA가 관리할 객체이며, DB의 테이블과 매핑된다.

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Member {
    @Id
    private Long id;
    private String name;
    //Getter, Setter ...
}

기본적으로 JPA는 모든 필드를 불러오게 끔 구현되어 있다. 하지만, 모든 케이스에서 id, name 필드를 전부 다 다루지 않을 수 있다. 이 경우에는 id 필드만 다루는 경우도 있을 수 있다. 그런 경우를 대비해서 엔티티 클래스를 하나 더 만들 수 있다.

따라서 테이블은 한 개지만, 엔티티는 경우에 따라서 여러 개를 만들 수 있다고도 한다.

 

 

EntityManagerFactory 생성

 

JPA를 시작하려면 우선 persistence.xml의 설정 정보를 사용해서 엔티티 매니저 팩토리를 생성해야 한다. 이때 Persistence 클래스를 사용한다. 이 클래스는 엔티티매니저팩토리를 생성해서 JPA를 사용할 수 있게 해 준다.

   //factory는 애플리케이션 로딩 시점에 딱 한번 만들어야 됨.
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence-unit에서 지정함 이름을 넘긴다.
        EntityManager em = emf.createEntityManager(); //엔티티 매니저 하나 받음 ( 마치 자바 컬렉션이라고 생각 ( 내 객체를 대신 저장해주는 ))

 

이렇게 하면 META-INF/persistence.xml에서 이름이 hello인 영속성 유닛(persistence-unit)을 찾아서 엔티티매니저팩토리를 생성한다.  아래는 persistence.xml 파일의 일부이다. 빌드 시스템을 gradle로 할 경우  src/main/resources/META-INF 폴더를 만들어 persistence.xml 파일을 만들고 아래 내용으로 설정을 해주면 된다. 그리고 엔티티를 class로 명시해 주어야 하다.

 

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    <persistence-unit name="hello">
        <class>Member</class>
        <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"/>
            <!--현재 사용할 데이터베이스의 방언을 선택한다. 이 부분만 바꾸면 자바코드를 바꾸지 않아도 다른 회사의 DB를 쓸 수 있다.-->
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
 
            <!--옵션, 이지만 JPA가 어떤 SQL을 만들어내는지 보고 싶으면 넣자.-->
            <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.id.new_generator_mappings" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

 

그리고 maven 프로젝트로 할 경우 댜음과 같이 설정해 주면 된다. 

 

<persistence-unit name="hello"> <!-- 이름이 hello인 영속성 유닛 -->
        <properties>
            <!-- 필수 속성 -->
            <!--데이터베이스 접근 정보-->
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>
            <property name="jakarta.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>

 

즉 이렇듯, gradle 시스템에서나, maven 빌드 시스템에서나 JPA는 persistencexml을 통해 필요한 설정 정보를 관리한다.persistence.xml의 정보를 읽어서 JPA를 동작시키기 위한 객체를 만들고 JPA 구현체에 따라서는 커넥션 풀도 생성해야 하기 때문에 엔티티 매니저를 생성하는 비용은 매우 크다. 따라서 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유해야 한다. 

 

 

EntityManager 생성

엔티티매니저팩토리를 생성했다면, 이를 이용해서 엔티티 매니저를 생성해야 한다. 

엔티티 매니저를 이용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회할 수 있다. 따라서 JPA의 대부분의 기능은 엔티티 매니저가 제공한다고 볼 수 있다.

엔티티 매니저는 내부의 데이터베이스 커넥션을 유지하면서 데이터베이스와 통신한다. 따라서 스레드 간에 공유하거나 재사용하면 안 된다. ( 사용하고 버려야 한다. )

import jakarta.persistence.*;

public class JpaMain {

    public static void main(String[] args) {

        //factory는 애플리케이션 로딩 시점에 딱 한번 만들어야 됨.
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence-unit에서 지정함 이름을 넘긴다.
        EntityManager em = emf.createEntityManager(); //엔티티 매니저 하나 받음 ( 마치 자바 컬렉션이라고 생각 ( 내 객체를 대신 저장해주는 ))
        //code
        EntityTransaction tx = em.getTransaction();		//트랜잭션 API
        tx.begin();	//트랜잭션 시작
        try {
			//비즈니스 로직
            Member member = new Member();
            member.setId(2L);
            member.setName("HelloB");
            em.persist(member);
            tx.commit();	//트랜잭션 커밋

        } catch (Exception e) {
            tx.rollback();	//예외 발생 시 트랜잭션 롤백
        } finally {
            em.close();		//엔티티 매니저 종료
        }
        emf.close();		//엔티티 매니저 팩토리 종료
    }
}

비즈니스 로직을 보면 엔티티매니저를 통해서 수행되는 것을 알 수 있다.

 

 

저장

//비즈니스 로직
            Member member = new Member();
            member.setId(2L);
            member.setName("HelloB");
            em.persist(member);

엔티티를 저장하려면, 엔티티 매니저의 persist() 메서드에 저장할 엔티티를 넘겨주면 된다. 위의 코드는 member 엔티티를 생성하고, em.persist(member)를 실행해 엔티티를 저장한다. JPA는 회원 엔티티의 매핑정보를 분석해서 SQL을 만들어 데이터베이스에 전달한다.

INSERT INTO MEMBER (ID, NAME) VALUES ('2L','HelloB')

 

 

수정

 Member findMember = em.find(Member.class, 2L);
 findMember.setName("nameB");

JPA는 어떤 엔티티가 변경되었는지 추적하는 기능을 갖추고 있다. 따라서 member.setName()처럼 엔티티의 값을 변경하면 알아서 UPDATE 쿼리를 작성해서 데이터베이스에 값을 반영한다.

 

 

삭제

em.remove(findMember);

엔티티를 삭제할 때는 엔티티 매니저의 remove() 메서드에 삭제하려는 엔티티를 넘겨주면 된다. 그러면 DELETE 쿼리를 생성해서 실행한다.

 

 

한건 조회

 Member findMember = em.find(Member.class, 2L);

find() 메서드는 조회할 엔티티 타입과, @Id로 데이터베이스 테이블의 기본키와 매핑한 식별자 값으로 엔티티 하나를 조회하는 메서드이다. 이 메서드를 사용하면 SELECT 쿼리를 생성해서 데이터베이스에 결과를 조회한다. 그리고 조회한 값으로 엔티티를 생성해서 반환한다.

 

 

종료

마지막으로 사용이 끝난 엔티티 매니저는 꼭 종료해주어야 한다.

그리고 그다음으로 애플리케이션을 종료할 때 엔티티 매니저 팩토리도 다음처럼 종료해야 한다.

em.close();
emf.close();

 

정리

엔티티 매니저 팩토리의 특징

* 엔티티 매니저를 만드는 역할을 수행한다.
* 생성 비용이 크다
* 하나의 프로젝트 내에서 한개만 만들어서 사용한다. ( 하나의 어플리케이션에서 하나 생성 후 공유 )
* 여러 스레드가 동시에 접근해도 안전하게 설계

엔티티 매니저의 특징

* 생성 비용이 적다.
* 여러 스레드가 동시에 접근하면 동시성 문제가 발생한다. -> 엔티티 매니저 팩토리와는 달리 공유하지 않는다.
* DB 커넥션 풀에서 연결이 꼭 필요한 시점에 커넥션을 얻어서 사용한다. ( 사용자 요청에 의해 트랜잭션을 시작할 때 )

 

 

 

Reference

[1] 인프런 김영한 님의 강의 - https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 | 김영한 - 인프런

김영한 | JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 실무에서도

www.inflearn.com

[2] 김영한 - 자바 ORM 표준 JPA 프로그래밍

[3] https://jays-lab.tistory.com/35 

 

JPA 엔티티 매니저 팩토리와 엔티티 매니저, 영속성 컨텍스트

엔티티 매니저 팩토리와 엔티티 매니저 엔티티 매니저는 엔티티를 저장, 수정, 삭제, 조회등 엔티티와 관련된 모든 일을 처리. 엔티티 매니저 팩토리를 생성하는 코드. EntityManagerFactory emf = Persist

jays-lab.tistory.com

[4] https://psvm.kr/posts/tutorials/jpa/2-start-jpa-with-gradle

 

JPA 프로젝트 Gradle로 시작하기

IntelliJ 커뮤니티 버전을 사용해 JPA 5 버전 프로젝트를 Gradle로 설정하자.

psvm.kr

 

'JPA' 카테고리의 다른 글

[JPA] 영속성 관리, 영속성 컨텍스트  (0) 2024.05.03
[JPA] 데이터베이스 방언, Dialect  (0) 2024.05.03

JPA는 특정 데이터베이스에 종속적이지 않은 기술이다. 따라서 다른 데이터베이스로 손쉽게 교체할 수 있다. 

 

SQL은 표준 SQL인 ANSI SQL이 있으며, ANSI SQL 이외에 각 DBMS Vendor인 MS-SQL, Oracle, MySQL, PostgreSQL에서 자신만의 기능을 추가한 SQL이 있다. ANSI SQL이 모든 DBMS에서 공통적으로 사용가능한 핵심 표준 SQL이지만, 여러 제품의 DBMS에서는 자신만의 독자적인 기능을 위해 추가적인 SQL을 만들었다. 즉,  각 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다르다는 문제점이 있다. 예를 들면 다음과 같은 차이점들이 존재한다.

 

  • 데이터 타입 : 가변 문자 타입으로 MySQL은 VARCHAR, 오라클은 VARCHAR2를 사용한다.
  • 다른 함수명 : 문자열을 자르는 함수로 SQL 표준은 SUBSTRING()을 사용하지만 오라클은 SUBSTR()을 사용한다.
  • 페이징 처리 : MySQL은 LINIT를 사용하지만 오라클은 ROWNUM을 사용한다.
  • 기본키 할당 : MySQL은 AUTO_INCREMENT 오라클은 SEQUENCE 를 사용한다.

 

이처럼 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언(Dialect)라고 한다. 데이터 베이스에 종속되는 기능을 많이 사용하게 되면 나중에 데이터베이스를 교체하기 힘들다. JPA는 직접 SQL을 작성하고 실행하는 형태이기 때문에 별도 Dialect 설정을 해주면 JPA가 DBMS에 맞는 쿼리를 생성한다. 

따라서 JPA가 제공하는 표준 문법에 맞추어 JPA를 사용하면 되고, 특정 데이터베이스에 의존적인 SQL은 데이터베이스 방언이 처리해 준다. 따라서 데이터베이스가 변경되어도 애플리케이션 코드를 변경할 필요 없이 데이터베이스 방언만 교체하면 된다. JPA에서는 Dialect라는 추상화된 방언 클래스를 제공하고, 각 벤더에 맞는 구현체를 제공하고 있기 때문이다.

 

 

JPA에서는 설정에 맞게 Dialect만 설정해주면 해당 Dialect를 참고하여 그에 알맞은 쿼리를 작성해 준다. 따라서 개발 시에 Oracle DB에 맞게 설정하고 애플리케이션을 개발하다가 실제 고객의 환경이 SQL SERVER를 사용 중이라면 설정만 SQLServerDialect로 변경해 줌으로써 불필요한 변경에 대한 자원 소모를 줄일 수 있다.

 

 

하이버네이트 Dialect 설정과 종류

- Maven ( META-INF/persistence.xml)

프로젝트를 Maven으로 설정할 경우, Maven에서 설장 파일 기본 템플릿은 다음과 같다.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence">
    <persistence-unit name="이름">
        <properties>
        
        
        
        
        </properties>
    </persistence-unit>

</persistence>

 

설정파일인 persistence.xml의 hibernate.dialect 설정값만 바꾸어 주면 된다. 

  <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

이 부분이 Dialect 를 설정해준 부분인데 데이터베이스 dialect를 H2로 설정한 것이다.  이밖에도 JPA를 사용할 때 필수적으로 설정해야 하는 정보는 다음과 같다. ( H2 데이터베이스 사용한다고 가정. )

            <!-- 필수 속성 -->
            <!--데이터베이스 접근 정보-->
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

그리고 만약, H2가 아니라, 다른 DB를 사용한다면, 아래와 같이 속성값을 변경하면 된다.

  • H2 : org.hibernate.dialect.H2Dialect
  • Oracle 10g : org.hibernate.dialect.Oracle10gDialect
  • MySQL : org.hibernate.dialect.MySQL5InnoDBDialect

 

하이버네이트의 경우 설정 파일인 persistence.xml의 설정값을 다음과 같이 설정했다.

H2 데이터베이스를 사용했기 때문에 다음과 같이 hibernate.dialect 속성을 org.hibernate.dialect.H2 Dialect로 설정했다

<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />

 

 

Hibernate 전용 속성

참고로, 하이버네이트를 사용할 경우, 다음과 같은 속성들이 존재한다.

hibernate.show_sql : 하이버네이트가 실행한 SQL을 출력

hibernate.format_sql : 하이버네이트가 실행한 SQL을 출력할 때 보기 쉽게 정렬

hibernate.use_sql_comments : 쿼리를 출력할때 주석도 함께 출력

hibernate.id.new_generator_mappings : JPA 표준에 맞춘 새로운 키 생성 전략을 사용

 	<!-- 옵션 -->
            <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" />

 

-Gradle ( application.properties )

gradle을 사용한다면 JPA 방언 설정은 주로 프로퍼티 파일이나 설정 클래스를 통해서 할 수 있다. ( application.yml, application.properties 파일 )

gradle 프로젝트에서 src/main/resources 디렉토리 하위에 application.properties 파일을 생성하고,

다음과 같이 spring.jpa.properties.hibernate.dialect 를 설정할 수 있다.

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

만약 application.yml 파일을 사용한다면 다음과 같이 설정할 수 있다.

spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.H2Dialect

 

 

dialect 예시

 

Dialect는 보통 데이터베이스 이름 + 버전으로 이루어져 있다

H2 : org.hibernate.dialect.H2Dialect.

오라클 10g : org.hibernate.dialect.Oracle10gDialect.

MySQL : org.hibernate.dialect.MySQL55Dialect // 5.5 버전
MySQL : org.hibernate.dialect.MySQL57Dialect //5.7 버전

 

 

 

 

Reference

[1] https://055055.tistory.com/83 

 

JPA Dialect

김영한님의 강의 내용 정리, oracle dialect 상속 및 사용자 함수 추가 Dialect? 표준 SQL인 ANSI SQL외에, DBMS인 Oracle, MySQL, MS-SQL, PostgreSQL마다 문법과 함수가 조금씩 다른 경우가 있다. 이러한 SQL 표준을

055055.tistory.com

[2] https://dololak.tistory.com/465

 

[JPA - Hibernate] Dialect(방언)이란? 하이버네이트 Dialect 종류

Dialect(방언)이란? 표준 ANSI SQL과 방언 SQL SQL은 다음과 같이 표준 SQL인 ANSI SQL이 있으며, ANSI SQL 이외에 각 DBMS Vendor(벤더, 공급업체)인 MS-SQL, Oracle, MySQL, PostgreSQL 에서 자신만의 기능을 추가한 SQL이

dololak.tistory.com

[3]https://www.inflearn.com/questions/1026626/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B0%A9%EC%96%B8-%EC%84%A4%EC%A0%95

 

데이터베이스 방언 설정 - 인프런

[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]자바 ORM 표준 JPA 프로

www.inflearn.com

[4] https://velog.io/@security-won/JPA-Dialect-DB-%EB%B0%A9%EC%96%B8-%EC%84%A4%EC%A0%95 

 

JPA Dialect, DB 방언 설정

JPA는 특정 데이터베이스에 종속되어있지 않다. 그ㄹ래서 DB를 MySQL을 사용하다가 Oracle로 변경을 해도 문제가 없어야한다. 그렇게 때문에 JPA를 사용할때 사용할 DB의 방언으로 설정을 해줘야한다.

velog.io

[5] 인프런 김영한 님의 강의 - https://www.inflearn.com/course/ORM-JPA-Basic/dashboard 

'JPA' 카테고리의 다른 글

[JPA] 영속성 관리, 영속성 컨텍스트  (0) 2024.05.03
[JPA] Entity와 EntityManager, EntityManagerFactory  (0) 2024.05.03