JVM/JPA 47

PostgreSQL Array 타입 JPA 연동

Hibernate는 기본적으로 PostgreSQL의 Array 데이터 타입을 지원해 주지 않는다. vladmihalcea 라이브러리를 추가 implementation("com.vladmihalcea:hibernate-types-60:2.20.0") type-60은 하이버네이트 6.0 이상부터 지원하는 라이브러리다. 각자 상황의 맞는 버전을 추가하면 된다. 스키마 create table job_offer ( job_offer_id bigserial primary key, days varchar(50)[] not null, ); 엔티티 @Entity @Builder @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class JobOffer ext..

JVM/JPA 2023.11.12

2차 캐시

애플리케이션 범위에서 공유하는 캐시를 Shared Cache, Secon Level Cache, L2 Cache라고 부른다. DB에서 조회한 데이터는 2차 캐시에 보관되고, 2차 캐시는 해당 엔티티의 복사본을 만들어 1차 캐시에 반환한다. 하지만 스프링이 지원하는 캐시를 서비스 계층에서 사용하는 것이 더 효과적이다. Entity에 @Cacheable을 붙여주면 캐시 모드를 설정할 수 있다. spring: jpa: properties: javax: persistence: sharedCache: mode: ENABLE_SELECTIVE hibernate: generate_statistics: true format_sql: true cache: use_second_level_cache: true region:..

JVM/JPA 2023.07.05

낙관적 락과 비관적 락

JPA는 데이터베이스에 대한 동시 접근으로부터 엔티티에 대한 무결성을 유지할 수 있게 해주는 동시성 제어 메커니즘을 지원한다. 이 메커니즘에는 낙관적 락과 비관적 락이 존재한다. JPA는 데이터베이스의 트랜잭션 격리 레벨을 READ COMMITTED 정도로 가정한다. 낙관적 락 (Optimistic Lock) 대부분의 트랜잭션이 충돌이 발생하지 않을 것이라고 낙관적으로 가정하는 방법이다. 따라서 데이터베이스가 제공하는 락 기능을 사용하지 않고, 엔티티의 버전을 통해 동시성을 제어한다. 즉, 애플리케이션 레벨에서 지원하는 락이다. @Version JPA는 @Version 어노테이션을 제공하는데, 이를 사용하여 엔티티의 버전을 관리할 수 있다. @Version 적용이 가능한 타입은 Long(long), In..

JVM/JPA 2023.07.05

트랜잭션을 지원하는 쓰기 지연

JDBC가 제공하는 SQL 배치 기능을 사용하면 쓰기 SQL을 모아서 데이터베이스에 한번에 보낼 수 있다. hibernate.jdbc.batch_size 속성의 값을 50으로 주면 최대 50건씩 모아서 SQL 배치를 실행한다. 하지만 SQL 배치는 같은 SQL일 때만 유효하다. 중간에 다른 처리가 들어가면 SQL 배치를 다시 시작한다. JPA의 쓰기 지연 기능은 데이터베이스 락이 걸리는 시간을 최소화해서 동시에 더 많은 트랜잭션을 처리할 수 있는 장점이 있다.em.persist(new Member()); //1 em.persist(new Member()); //2 em.persist(new Member()); //3 em.persist(new Member()); //4 em.persist(new Chil..

JVM/JPA 2023.07.04

SQL 쿼리 힌트 사용

SQL 힌트(데이터베이스 벤더에게 제공하는 힌트)를 사용하려면 하이버네이트를 직접 사용해야 한다. 오라클이 아닌 다른 데이터베이스에서 SQL 힌트를 사용하려면 각 방언에서 org.hibernate.dialect.Dialect.getQueryHintString() 메소드를 오버라이딩해서 기능을 구현해야 한다. Session session = em.unwrap(Session.class); List list = session.createQuery("SELECT m FROM Member m") .addQueryHint("FULL (MEMBER)") .list();

JVM/JPA 2023.07.04

읽기 전용 쿼리의 성능 최적화

영속성 컨텍스트는 변경 감지를 위해 스냅샷 인스턴스를 보관하므로 더 많은 메모리를 사용하는 단점이 있다. 다시 조회할 일도 수정할 일도 없이 딱 한 번만 읽어서 화면에 출력하면 될 때는 읽기 전용으로 엔티티를 조회하면 메모리 사용량을 최적화할 수 있다. 메모리를 최적화하려면 하이버네이트가 제공하는 읽기 전용 쿼리 힌트를 사용하고, 플러시 호출을 막아서 속도를 최적화하려면 읽기 전용 트랜잭션을 사용하는 것이 가장 효과적이다. 스칼라 타입으로 조회 스칼라 타입은 영속성 컨텍스트가 결과를 관리하지 않는다. SELECT o.id, o.name, o.price FROM Order o 읽기 전용 쿼리 힌트 사용 하이버네이트 전용 힌트인 org.hibernate.readOnly를 사용하면 읽기 전용으로 조회할 수 있..

JVM/JPA 2023.07.04

N+1 문제

N+1 문제 연관된 엔티티를 조회할 때 SQL문이 실행되는 것을 N+1 문제라고 한다. 즉시 로딩을 사용할 때도 JPQL은 JPQL만 사용해서 SQL을 생성하기에 N+1 문제가 발생할 수 있다. 모두 지연 로딩으로 설정하고 성능 최적화가 꼭 필요한 곳에는 JPQL 페치 조인을 사용하자. 페치 조인 사용 N+1 문제를 해결하는 가장 일반적인 방법은 페치 조인을 사용하는 것이다. 이때 조심해야 할 것은 일대다 조인일 경우 중복된 결과가 나타날 수 있으므로 JPQL의 DISTINCT를 사용해서 중복을 제거하는 것이다. 하이버네이트 @BatchSize 연관된 엔티티를 조회할 때 지정한 size만큼 SQL의 IN 절을 사용해서 조회한다. 만약 조회한 회원이 10명인데 size=5로 지정하면 2번의 SQL만 추가로..

JVM/JPA 2023.07.04

영속성 컨텍스트와 프록시

영속 엔티티의 동일성 보장 해당 코드에 refMember와 findMember는 모두 프록시 객체로 같은 인스턴스이다. 이는 영속 엔티티의 동일성을 보장하기 위함이다. 반대로 엔티티를 먼저 조회후 프록시를 조회하면 모두 엔티티이다. @Test void 영속성컨텍스트와_프록시() { Member member = new Member("member1", "회원1"); em.persist(member); em.flush(); em.clear(); Member refMember = em.getReference(Member.class, "member1"); Member findMember = em.find(Member.class, "member1"); Assertions.assertSame(refMember, fi..

JVM/JPA 2023.07.03

JPA 예외 처리

JPQ 표준 예외 javax.persistence.PersistenceException의 자식 클래스다. 그리고 이 예외 클래스는 RuntimeException의 자식이다. 트랜잭션 롤백을 표시하는 예외는 심각한 예외이므로 복구해선 안 된다. 이 예외가 발생하면 트랜잭션을 강제로 커밋해도 트랜잭션이 커밋되지 않고 대신에 javax.persistence.RollbackException 예외가 발생한다. 트랜잭션 롤백을 표시하지 않는 예외는 심각한 예외가 아니다. 따라서 개발자가 트랜잭션을 커밋할지 롤백할지를 판단하면 된다. 트랜잭션 롤백을 표시하는 예외 트랜잭션 롤백을 표시하는 예외 설명 EntityExistsException 저장 중에 이미 같은 엔티티가 있으면 발생 EntityNotFoundExcept..

JVM/JPA 2023.07.03

엔티티 그래프

엔티티를 조회할 때 연관된 엔티티를 함께 조회할 필요가 있으면 JPQL의 페치 조인을 사용한다. 그런데 페치 조인을 사용하면 같은 JPQL을 중복해서 작성하는 경우가 많다. 이는 JPQL이 엔티티를 조회할 뿐 아니라 연관된 엔티티를 함께 조회하는 기능도 제공하기 때문이다. JPA 2.1에 추가된 엔티티 그래프 기능을 사용하면 엔티티를 조회하는 시점에 함께 조회할 연관된 엔티티를 선택할 수 있다. 따라서 JPQL은 데이터를 조회하는 기능만 수행하면 되고 연관된 엔티티를 함께 조회하는 기능은 엔티티 그래프를 사용하면 된다. 그러므로 엔티티 그래프 기능을 적용하면 다음 JPQL만 사용하면 된다. SELECT o FROM Order o WHERE o.status = ? Named 엔티티 그래프 @NamedEnt..

JVM/JPA 2023.07.03