728x90

 

https://yozm.wishket.com/magazine/detail/2160/

 

Entity데이터베이스 테이블과 매핑되는 자바 객체를 의미합니다. 각각의 Entity 클래스는 테이블의 레코드(row)와 일치하는 필드를 가지고 있으며, JPA를 사용하여 이러한 Entity 객체들을 데이터베이스에 저장, 조회, 수정, 삭제 등의 작업을 수행합니다. Entity 클래스에는 @Entity 어노테이션을 사용하여 선언합니다.

예를 들어, Member라는 Entity 클래스로 Member 테이블과 매핑 후 id, name, age를 필드값으로 선언한다면 아래와 같이 작성할 수 있습니다.

        
        
        	@Entity
            public class Member {

                @Id
                @GeneratedValue
                private Long id;

                private String name;
                private int age;

                // Getter, Setter, 생성자 등의 메서드들은 생략
            }

 

📋 EntityManager는 JPA에서 가장 중요한 인터페이스 중 하나로, Entity와 데이터베이스를 연결하여 CRUD(Create, Read, Update, Delete) 작업을 수행하는 역할을 담당합니다. EntityManager는 JPA에서 영속성 컨텍스트(Persistence Context)를 통해 Entity 객체의 상태를 관리합니다.

EntityManager는 보통 트랜잭션 단위로 사용됩니다. 트랜잭션을 시작하면 EntityManager를 생성하여 작업을 수행하고, 트랜잭션을 커밋하거나 롤백하면 변경된 Entity 객체들을 데이터베이스에 반영합니다.

EntityManager의 주요 메서드로는 다음과 같은 것들이 있습니다:

  1. persist(Object entity): Entity 객체를 영속성 컨텍스트에 추가합니다. 데이터베이스에 저장되지는 않습니다.

  2. find(Class<T> entityClass, Object primaryKey): 주어진 기본 키에 해당하는 Entity 객체를 조회합니다.

  3. merge(T entity): Entity 객체의 변경사항을 영속성 컨텍스트에 병합합니다.

  4. remove(Object entity): Entity 객체를 영속성 컨텍스트에서 삭제합니다.

📋 EntityManagerFactory는 JPA의 핵심적인 요소 중 하나로, EntityManager를 생성하는 역할을 담당합니다. 애플리케이션이 시작될 때 한 번 생성되며, 보통 애플리케이션 전체에서 하나의 EntityManagerFactory 인스턴스를 공유하여 사용합니다.

EntityManagerFactory는 데이터베이스에 연결하고 Entity와 테이블의 매핑 정보를 로딩하여 EntityManager를 생성할 때 필요한 정보들을 제공합니다. 따라서 EntityManagerFactory를 통해 EntityManager를 여러 개 생성하여 여러 개의 트랜잭션을 동시에 처리할 수 있습니다.

EntityManagerFactory는 보통 애플리케이션 컨텍스트(ApplicationContext)에서 관리되며, 사용이 끝나면 애플리케이션 종료 시점에 명시적으로 닫아주어야 합니다.

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        종료시점에는 em.close(); / emf.close(); 를 실행

 

위와 같이 EntityManagerFactory를 생성한 후, createEntityManager() 메서드를 호출하여 EntityManager를 생성합니다.

요약하면, Entity는 데이터베이스 테이블과 매핑되는 자바 객체를 나타내고, EntityManager는 Entity 객체의 영속성 상태를 관리하고 CRUD 작업을 수행하며,
EntityManagerFactory는 EntityManager를 생성하는 데 사용되는 팩토리 역할을 합니다. 이들은 JPA를 통해 데이터베이스와 자바 객체를 효율적으로 관리하는 데 중요한 역할을 수행합니다.

 


 

JPQL이란 ❓

 

JPQL(Java Persistence Query Language)은 JPA(Java Persistence API)에서 사용하는 객체 지향 쿼리 언어입니다. 데이터베이스 테이블이 아닌 엔티티 객체를 대상으로 쿼리를 작성할 수 있어서 객체 지향적인 접근을 할 수 있습니다. JPQL은 SQL과 유사한 구문을 가지고 있지만, 테이블이 아닌 엔티티와 필드를 대상으로 쿼리를 작성하는 차이가 있습니다.

JPQL은 데이터베이스 특정 SQL 문법에 종속되지 않고, JPA가 지원하는 모든 데이터베이스에서 사용할 수 있습니다. 따라서 JPA를 사용하는 애플리케이션은 데이터베이스에 상관없이 일관성 있는 쿼리를 작성할 수 있습니다.

JPQL 쿼리는 엔티티 객체에 대한 조회, 필터링, 정렬, 집계 등 다양한 작업을 수행할 수 있습니다. JPQL은 SELECT, UPDATE, DELETE 등의 작업을 지원하며, 파라미터 바인딩 등의 기능도 제공합니다.

JPQL은 엔티티 클래스와 필드를 대상으로 작성되기 때문에 테이블과 컬럼의 이름이 변경되어도 쿼리를 수정할 필요가 없습니다. 또한, JPQL을 사용하면 런타임 시점에 쿼리의 오타나 오류를 미리 파악할 수 있어서 안전성이 높습니다.

JPQL 사용 예시

       
       
       // JPQL 쿼리 예시: 회원 이름이 '푸바오'인 회원을 조회하는 쿼리
        String jpql = "SELECT m FROM Member m WHERE m.name = '푸바오'";

        // JPQL 쿼리를 실행하기 위해 EntityManager를 이용하여 Query 객체를 생성
        Query query = entityManager.createQuery(jpql);

        // 결과를 가져오기 위해 getResultList() 메서드를 호출하여 List<Member> 형태로 결과를 반환
        List<Member> members = query.getResultList();

 

 


 

📋 영속성 컨텍스트

영속성 컨텍스트(Persistence Context)JPA(Java Persistence API)에서 관리하는 엔티티(Entity)들의 상태를 관리하는 공간입니다. 영속성 컨텍스트는 엔티티의 생명주기를 관리하고,
데이터베이스와의 상호작용을 최적화하는 데 도움이 됩니다. JPA를 이해하는데 가장 중요한 논리적 개념이고 눈에 보이지 않으며,엔티티 매니저를 통해 영속성 컨텍스트에 접근이 가능합니다.

엔티티의 생명주기는 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계없는 새로운 상태
→ new 연산자로 객체를 생성한 상태를 의미
영속 (managed) : 영속성 컨텍스트에 관리되는 상태
준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
삭제 (removed) : 삭제된 상태

 

 

🎈 영속 상태

 

                EntityManagerFactory emf = PersistenceDelegate.createEntityManagerFactory ("hello");

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

                //비영속
                Member member = New Member();
                member.setId(100L);
                member.setName("HelloJPA");

                //영속성 (DB에 저장되는 것은 아님)     
                    System.out.println("=====em.persist(영속처리 하기 전)=======");
                    em.persist(member);
                    System.out.println("=====em.persist(영속처리 하기 후)=======");

                    tx.commit();


         <----- ↓ 로그에 출력된 결과물 ----->

        =====em.persist(영속처리 하기 전)=======
        =====em.persist(영속처리 하기 후)=======
        Hibernate: 
            /* insert for
                com.study.jpa.Member */insert 
            into
                MBR (name,id) 
            values
                (?,?)

        tx.commit();  --> 이 커밋이 실행되면서 바로 쿼리문이 전달되게 됩니다!

 

✅ 영속성 컨텍스트의 장점

  1. 엔티티의 관리
    • 영속성 컨텍스트는 엔티티를 관리하고, 엔티티의 상태를 추적합니다. 즉, 엔티티의 변경 사항을 감지하고, 데이터베이스에 자동으로 반영합니다.
    • 엔티티를 영속성 컨텍스트에 저장하면, 영속 상태(Persistent)가 됩니다. 이후 변경 사항을 감지하고, 트랜잭션을 커밋할 때 데이터베이스에 반영됩니다.
  2. 1차 캐시
    • 영속성 컨텍스트 내부에 1차 캐시를 가지고 있습니다. 1차 캐시는 엔티티를 메모리에 저장하여 데이터베이스 조회를 최소화하고 성능을 향상시킵니다. 1차 캐시는 @Id(키)와 Entity(값)으로 만들어져있고 Id는 Pk값으로 매핑한것, 값은 Entity객체 자체가 값이 됩니다.
    • em.find(”member1”)을 찾으면 바로 DB를 찾는 것이 아니라 1차 캐시를 먼저 조회하고, 해당 데이터가 있다면 영속성 컨텍스트에서 해당 데이터를 조회합니다.
    • 같은 엔티티를 여러 번 조회해도, 영속성 컨텍스트 내에 이미 캐싱되어 있는 엔티티가 있다면 데이터베이스를 다시 조회하지 않고 캐시된 엔티티를 반환합니다. 만약 1차 캐시에 없는 데이터를 조회한다면 DB에서 조회하여 가져온 후 1차 캐시에 저장한 후 반환합니다.
  3. 트랜잭션 단위의 변경 관리
    • 영속성 컨텍스트는 트랜잭션 단위로 변경 사항을 관리합니다. 트랜잭션을 커밋할 때 변경 사항이 데이터베이스에 반영됩니다.
    • 트랜잭션 롤백이 발생하면, 영속성 컨텍스트에 저장된 변경 사항도 롤백되어 데이터베이스와 일관성을 유지합니다.
  4. 지연 로딩
    • 영속성 컨텍스트는 지연 로딩(Lazy Loading)을 지원합니다. 연관된 엔티티를 실제로 사용할 때에만 데이터베이스에서 조회하게 됩니다.
    • 이를 통해 성능이 향상되고, 필요하지 않은 데이터를 불러오지 않아도 되므로 효율적인 데이터베이스 사용이 가능합니다.
  5. Dirty Checking(더티 체킹)
    • 영속성 컨텍스트는 엔티티의 상태 변화를 감지하여 변경된 엔티티만 데이터베이스에 반영합니다. 이를 Dirty Checking이라고 합니다.

 


 

📋 JPA에서의 Entity 등록 ( 버퍼링(Buffering) 지원)

JPA에서 엔티티를 영속성 컨텍스트에 등록하는 방법은 크게 두 가지가 있습니다.

  1. 즉시 등록(Immediate Persisting)
    • 엔티티를 생성하거나 조회한 후 즉시 영속성 컨텍스트에 등록합니다. 이때, 트랜잭션을 커밋하기 전에도 데이터베이스에 즉시 등록됩니다.
    • 일반적으로 persist() 메서드를 사용하여 엔티티를 즉시 등록합니다.
  2. 지연 등록(Deferred Persisting)
    • 엔티티를 영속성 컨텍스트에 등록하는 것을 지연시킵니다. 즉, 엔티티를 생성하거나 조회한 후, 트랜잭션을 커밋할 때까지 영속성 컨텍스트에 등록하지 않습니다.
    • 지연 등록을 사용하면 한 트랜잭션 안에서 여러 개의 엔티티를 생성하고 수정할 때, 트랜잭션 커밋 시점에 한 번에 데이터베이스에 등록하여 성능을 최적화할 수 있습니다.
    • 일반적으로 JPA는 지연 등록을 기본 전략으로 사용합니다.

 

            
            	// 즉시 등록
                EntityManager em = ...; // EntityManager 생성
                EntityTransaction tx = em.getTransaction();

                tx.begin();
                Member member = new Member("John");
                em.persist(member); // 즉시 등록
                tx.commit();

                // 지연 등록
                EntityManager em = ...; // EntityManager 생성
                EntityTransaction tx = em.getTransaction();

                tx.begin();
                Member member1 = new Member("Alice");
                Member member2 = new Member("Bob");
                em.persist(member1); // 지연 등록
                em.persist(member2); // 지연 등록
                tx.commit(); // 트랜잭션 커밋 시점에 등록

 

위의 예시에서 persist() 메서드를 호출할 때, 엔티티는 바로 데이터베이스에 등록되는 것이 아니라 영속성 컨텍스트에 등록(1차 캐시)됩니다. 트랜잭션이 커밋될 때, 영속성 컨텍스트에 등록된 엔티티들이 한 번에 데이터베이스에 등록되게 됩니다. 이렇게 함으로써 데이터베이스와의 상호작용을 최소화하여 성능을 향상시킬 수 있습니다.

 


 

💡 배치(Batch)란 ?

 

JPA에서의 "배치(batch)"는 일괄 처리 또는 일괄 업데이트를 의미합니다. 배치 처리는 여러 개의 엔티티를 한 번에 데이터베이스에 영속화하는 기법으로, 엔티티를 하나씩 처리하는 것보다 성능을 향상시키고 데이터베이스와의 라운드 트립 횟수를 줄일 수 있습니다.

JPA에서 배치를 사용하려면 EntityManager의 flush()와 clear() 메서드를 사용합니다. 트랜잭션 내에서 엔티티를 영속화 또는 업데이트하면, 해당 엔티티들은 영속성 컨텍스트에 의해 관리되며, 트랜잭션이 커밋될 때 자동으로 데이터베이스와 동기화됩니다.

하지만 대량의 엔티티를 영속화하거나 업데이트하는 경우, 각 엔티티마다 개별적인 데이터베이스 작업을 수행하는 것은 비효율적일 수 있습니다. 이런 경우 배치를 사용하여 여러 개의 엔티티 변경을 하나의 배치로 묶어서 실행할 수 있습니다.

JPA에서 배치 처리가 작동하는 방식은 다음과 같습니다:

  1. 엔티티 변경 모으기: 트랜잭션 내에서 영속성 컨텍스트를 통해 여러 개의 엔티티를 영속화 또는 업데이트합니다.
  2. 영속성 컨텍스트 플러시: entityManager.flush()를 호출하면 영속성 컨텍스트가 데이터베이스와 동기화되고, 엔티티 변경에 대한 SQL 문이 생성되지만, 변경 사항은 아직 커밋되지 않습니다.
  3. 배치 업데이트 실행: SQL 문을 즉시 실행하는 대신, JPA는 일정 개수의 변경 사항 (배치 크기) 또는 일정 시간이 경과할 때까지 배치 업데이트를 대기합니다. 이렇게 하여 여러 개의 변경 사항을 하나의 배치로 데이터베이스에 보냅니다.
  4. 영속성 컨텍스트 초기화: 배치 업데이트가 실행된 후 entityManager.clear()를 호출하여 영속성 컨텍스트를 초기화하고 관리되는 엔티티에 대한 메모리를 해제할 수 있습니다. 대량의 엔티티를 처리할 때 메모리 문제를 피하는 데 도움이 됩니다.
  5. 트랜잭션 커밋: 마지막으로 트랜잭션이 커밋되면, 모든 배치 처리된 변경 사항이 데이터베이스에 일괄 처리로 기록됩니다.

배치를 사용하면 대량의 데이터를 영속화하거나 업데이트할 때 개별적인 데이터베이스 작업의 오버헤드를 크게 줄여 성능을 향상시킬 수 있습니다. 하지만 특정 상황과 데이터베이스 설정에 따라 적절한 배치 크기를 선택하는 것이 중요하며, 성능 문제를 피하기 위해 신중하게 결정해야 합니다.

 

📋 JPA에서의 Entity 수정 시 Dirty Checking(더티 체킹)

JPA의 목적은 자바 컬렉션과 유사하게 객체를 다루고자 합니다. JPA에서는 값을 찾아 불러온 후 값만 변경해주면, 마치 자바 컬렉션을 다루듯이 변경을 감지하여 데이터를 변경하기 위한 쿼리문을 작성합니다.

Entity Dirty Checking은 JPA(Java Persistence API)의 영속성 컨텍스트(Persistence Context)에서 엔티티(Entity)의 변경 상태를 감지하는 메커니즘입니다. 이를 통해 JPA는 엔티티가 변경되었는지를 자동으로 감지하고, 변경된 내용을 데이터베이스에 동기화할 필요가 있는지 판단합니다.

일반적으로 JPA에서 엔티티를 조회하면 해당 엔티티는 영속성 컨텍스트에 올라가게 됩니다. 그리고 트랜잭션이 끝나기 전까지 영속성 컨텍스트에서 관리되며, 이때 엔티티의 상태를 주기적으로 확인하여 변경 여부를 감지합니다. 이렇게 변경된 엔티티를 찾아내는 기능을 Dirty Checking이라고 합니다.

Dirty Checking은 다음과 같은 방식으로 동작합니다:

JPA는 데이터 베이스 트랜잭션을 커밋하는 순간 내부적으로 flush()가 호출되며 Entity와 스냅샷을 비교합니다. 스냅샷은 영속성 컨텍스트에 들어온 데이터가 1차 캐시에 담긴 시점을 의미합니다. 내부 데이터의 값이 변경되면 JPA가 데이터베이스가 트랜잭션이 되는 시점에 Entity와 스냅샷들을 비교하고, 변경된 데이터가 있다면 지연 등록에 Update 쿼리를 만들어 둔 후 DB로 전달합니다.

  1. 트랜잭션 시작 시, 엔티티의 스냅샷을 만듭니다. 스냅샷은 엔티티가 영속성 컨텍스트에 올라가기 이전 상태를 저장하는 것으로, 엔티티의 초기 상태를 기록합니다.

  2. JPA는 데이터 베이스 트랜잭션을 커밋하는 순간 내부적으로 flush()가 호출되며 Entity와 스냅샷을 비교합니다. 트랜잭션 도중 엔티티가 변경되면, 영속성 컨텍스트는 이전 스냅샷과 현재 엔티티 상태를 비교하여 변경 여부를 감지합니다.

  3. 변경된 엔티티가 발견되면, 해당 엔티티를 데이터베이스에 자동으로 동기화합니다. 즉, 변경된 내용을 UPDATE 쿼리로 데이터베이스에 반영합니다.

Dirty Checking을 사용하면 개발자가 직접 변경 감지 로직을 구현할 필요가 없으며, 효율적인 데이터베이스 업데이트를 자동으로 처리할 수 있습니다. 따라서 JPA를 사용하는 경우 엔티티의 상태 변경과 관련된 로직을 보다 간단하고 안정적으로 구현할 수 있습니다.

💥 flush란?

**flush()**는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업을 수행하는 메서드입니다. 데이터 베이스에 트랜잭션 커밋이 되는 순간 바로 flush()가 자동으로 발생합니다.

영속성 컨텍스트는 엔티티를 관리하며, 엔티티의 상태가 변경되면 해당 변경 내용은 먼저 영속성 컨텍스트에 저장됩니다. 그러나 데이터베이스와의 실제 동기화는 일반적으로 트랜잭션이 커밋되는 시점에 일괄적으로 수행됩니다.

하지만 때에 따라서는 트랜잭션이 커밋되기 전에 데이터베이스와 영속성 컨텍스트를 동기화해야 하는 상황이 발생할 수 있습니다. 이 때 **flush()를 호출하면 변경 내용이 데이터베이스에 즉시 반영**됩니다. 즉, flush()를 호출하면 영속성 컨텍스트의 변경 내용이 데이터베이스에 쓰여지고, 트랜잭션이 커밋되지 않아도 데이터베이스에는 변경된 내용이 반영됩니다.

**flush()**를 사용할 때 주의할 점은, 변경 내용이 데이터베이스에 반영되기 때문에 롤백이 불가능해지고, 데이터베이스와 영속성 컨텍스트의 상태가 일치하지 않을 수 있습니다. 따라서 적절한 시점에 **flush()**를 호출하여 사용하는 것이 중요합니다. 보통 **flush()**를 명시적으로 호출할 일은 적지만, 필요한 경우에는 이를 적절하게 활용하여 **데이터베이스와 영속성 컨텍스트를 동기화(영속성 컨텍스트를 비우는 것은 아님)**할 수 있습니다.

flush()가 실행된다고 해도, 영속성 컨텍스트를 비우는 기능은 없습니다. 트랜잭션의 작업 단위가 중요하며, 커밋 직전에만 변경 내용을 DB에 전달하면 됩니다.


준영속 상태

준영속 상태(Detached)란 영속성 컨텍스트에서 분리된 상태를 말합니다. 이 상태에서는 영속성 컨텍스트가 더 이상 해당 엔티티를 관리하지 않으며, 데이터베이스와의 동기화가 이루어지지 않습니다.

준영속 상태로 변환되면 해당 엔티티는 영속성 컨텍스트가 더 이상 관리하지 않기 때문에 변경 내용을 추적하지 않습니다. 따라서 데이터베이스와의 동기화도 이루어지지 않습니다. 그러나 해당 엔티티는 여전히 식별자를 가지고 있으며, 이를 통해 다시 영속 상태로 변경하거나 데이터베이스에서 조회할 수 있습니다.

일반적으로 영속 상태의 엔티티가 준영속 상태로 전환되는 경우는 다음과 같습니다:

  1. 트랜잭션이 커밋되어 영속성 컨텍스트가 종료되는 경우.

  2. EntityManager의 detach() 메서드를 호출하여 명시적으로 엔티티를 준영속 상태로 전환하는 경우.

  3. EntityManager의 clear() 메서드를 호출하여 영속성 컨텍스트를 완전히 초기화하는 경우.

  4. 영속 상태의 엔티티를 직렬화하여 세션 등에 저장한 뒤, 이후에 역직렬화하여 사용하는 경우.

준영속 상태에서는 엔티티의 변경이 데이터베이스에 영향을 주지 않기 때문에 주의해야 합니다. 만약 다시 영속 상태로 전환하고자 할 때는 EntityManager의 merge() 메서드를 사용하여 데이터베이스와의 동기화를 수행해야 합니다.

728x90
반응형

+ Recent posts