Skip to content

태그: JPA

Cascade
jpa
cascade 옵션은 jpa를 사용할때 @OneToMany나 @ManyToOne에 옵션으로 줄 수 있는 값이다. cacade 옵션을 사용하면 부모 엔티티에 상태 변화가 생길 때 그 엔티티와 연관되어있는 엔티티에도 상태 변화를 전이시킬 수 있다. 즉, 자식 엔티티의 생명주기를 관리할 수 있다. cascade 타입의 종류 PERSIST 부모 엔티티를 저장하면 자식 엔티티까지 함께 저장한다. 다시말해, 명시적으로 부모엔티티와 연관관계를 가진 자식 엔티티 영속화시킬때 따로 명시할 필요 없이 부모.자식 = 자식 인스턴스 과 같이 적으면 자식 엔티티도 데이터베이스에 자동으로 저장된다. MERGE 데이터베이스에서 가져온 부모 객체를 통해 자식 엔티티의 정보를 수정하여 병합했을때 변경 결과가 자식 엔티티에 반
GenerateValue Column에 값을 넣는다면
jpa
Hexagonal architecture를 사용해서 구현하다 문제가 생겼다. 해당 프로젝트에선 기본 PK로 UUID를 사용했고, 새로 생성한 도메인 모델은 id 값을 UUID(0, 0)으로 설정해서 사용했다. (여기서 중요하진 않지만 생성 전략은 IdentifierGenerator로 TimeBasedUUID를 생성할 수 있도록 직접 정의된 상태였다.) UUID(0, 0)으로 설정해주다 보니 기존에는 새 insert문을 날려야하는 경우 isNew가 Persistable 기본 전략에 따라 false로 들어갔고, merge가 호출되었다. 여기에서 알아봤던 것 처럼 영속성 컨텍스트에 등록되지 않은 객체에 대해 merge를 호출하면, 어떤 update문(혹은 insert문)을 날려야하는지를 알아야 하기 때문에 sel
Hibernate dialect
jpa
하이버네이트가 데이터베이스와 통신을 하기 위해 사용하는 언어를 Dialect라고 한다. 모든 데이터베이스에는 각자의 고유한 SQL언어가 있는데, 관계형 데이터베이스끼리 형태나 문법이 어느정도 비슷하긴 하지만, 완전히 똑같지는 않다. 예를 들어 Oracle 쿼리 구문과 MySQL 쿼리구문은 다르다. 하지만, 하이버네이트는 한 데이터베이스관리시스템(DBMS)에 국한되지않고, 다양한 DBMS에 사용 가능하다. 즉 내부적으로 각자 다른 방법으로 처리하고 있는 것이다. 그렇기 때문에특정 벤더(DBMS)에 종속적이지 않고, 얼마든지 대체가능하다. JPA에서는 아래와 같이 Dialect라는 추상화된 언어 클래스를 제공하고 각 벤더에 맞는 구현체를 제공하고 있다. spring: datasource:
JDBC Object Mapping Fundamentalsentity
jpa
스프링 데이터가 객체를 매핑할 떄 담당하는 핵심 역할은 도메인 객체 인스턴스를 생성하고, 여기에 저장소 네이티브 데이터 구조를 매핑하는 일이다. 여기에는 두 가지의 핵심적인 단계가 있다. 노출된 생성자중 하나로 인스턴스 생성하기. 나머지 프로퍼티 채우기. 엔티티(Entity) 생성 Spring Data JDBC에서 엔티티 객체를 생성하는 알고리즘은 3가지이다. 기본 생성자가 있는 경우 기본생성자를 사용한다. 다른 생성자가 존재해도 무시하고 우선적으로 기본생성자를 사용한다. 매개변수가 존재하는 생성자가 하나만 존재한다면 해당 생성자를 사용한다. 매개변수가 존재하는 생성자가 여러개 있으면 @PersistenceConstructor 어노테이션이 적용된 생성자를 사용한다. 만약 @PersistenceCons
FetchJoin
jpql
fetch join은 JPQL에서 성능 최적화를 위해 제공하는 기능이다. 페치 조인은 연관된 엔티티나 컬렉션을 한 번에 같이 조회해준다. 일반적으로 연관관계가 맺어진 엔티티를 조회할때, N+1문제를 해결하기 위해서 FetchType.LAZY를 사용하는 경우가 있다. 하지만 두 엔티티를 모두 조회해야하는 경우엔 두개의 쿼리를 날려야하기 때문에 N+1문제가 동일하게 발생한다. 이럴 때 fetch join을 사용하면 하나의 쿼리로 두 엔티티를 한 번에 조회할 수 있다. 1:N 조인에서 fetch join을 사용하면 결과의 갯수가 늘어날 수 있다.(‘1’쪽에서 조회해야하는 엔티티의 총 갯수가 아닌, ‘1’쪽의 엔티티 갯수만큼의 결과가 조회된다.) 이 경우 JPQL의 DISTINCT를 사용해서 결과의 갯수를 줄일
경로표현식
jpql
자바에서 인스턴스화된 객체로 클래스의 변수나 메서드에 접근할 때 .을 이용해 접근하는 것 처럼, jpql 쿼리에서 .으로 객체의 값에 점을 찍어 객체 그래프를 탐색하는 것을 경로 표현식이라고 한다. 경로 유형별 동작 상태 필드(State Field) 상태 필드는 일반적인 값을 저장하기 위한 필드이다. 즉, int, varchar등의 자료형을 가지는 기본적인 데이터를 저장한다. 상태필드는 더 나아갈 경로가 존재하지 않으므로 jpql에서 부가적인 조인 등의 탐색 또한 일어나지 않는다. 연관 필드(Association field) 연관 필드는 연관관계가 맺어진 외래 테이블의 값을 위한 필드이다. 단일 값 연관 필드와 컬렉션 값 연관 필드로 나뉜다. 단일 값 연관 필드 @ManyToOne, @OneToOne
OrphanRemoval
jpa
cascade 옵션을 사용하면 부모 엔티티에 상태 변화가 생길 때 그 엔티티와 연관되어있는 엔티티에도 상태 변화를 전이시킬 수 있다. 그러나 cascade 옵션을 사용한다고 해서 부모 엔티티에서 자식 엔티티의 생명주기를 완전히 통제할 수 있는 것은 아니다. cascade의 REMOVE 옵션은, 부모 엔티티를 삭제했을때 그 엔티티를 참조하고 있는 자식 엔티티(해당 부모 엔티티를 Foreign Key로 가지고있는 엔티티)도 함께 삭제해준다. 하지만 부모 엔티티에서 자식 엔티티를 삭제했을때에는 그 참조를 끊어줄 뿐, 그 자식 엔티티를 같이 삭제해주진 않는다. (심지어 FK에 NotNull 제약조건을 걸어놨으면 아무 변화도 생기지 않는다) 그럴 때 사용할 수 있는 것이 OrphanRemoval이다
Paging
querydsljpa
Querydsl에서 페이징하는 방법을 알아보자. Page는 인터페이스이기 떄문에, Page를 반환하기 위해선 Page를 구현한 구체클래스를 생성해야한다. 그렇기 때문에 아래 코드에선 스프링 데이터에 있는 PageImpl 클래스를 사용하여 return 하도록 한다. fetchResults()를 사용하여 total count쿼리와 결과 리스트를 한 코드로 조회하도록 할 수도 있지만, fetchResults()와 fetchCount()가 특정 상황에서 제대로 동작하지 않는 이슈때문에 depercated 되었으므로 따로 count를 조회하여 반환하는 방식을 택했다. 원하는 컬럼을 Dto로 만들어서 조건에 따라 조회한 후 반환하는 예제 코드이다. public Page&x3C;MemberTeamDto> sear
QuerydslJpa와 QClass
querydsljpa
Spring Data JPA가 기본적으로 제공해주는 CRUD 메서드 및 쿼리 메서드 기능을 사용하더라도 원하는 조건의 데이터를 수집하기 위해선 JPQL을 작성해야한다.JPQL로 간단한 로직을 작성하는데는 큰 문제가 없지만, 복잡한 로직의 경우 쿼리 문자열이 상당히 길어진다. 또, 정적 쿼리인 경우엔 어플리케이션 로딩 시점에 JPQL 문자열의 오타나 문법적인 오류를 발견할 수 있지만, 그 외는 런타임 시점에서 에러가 발생한다는 문제가 있다. 이러한 문제를 해결하기 위한 프레임워크가 바로 QueryDSL-jpa이다. QueryDSL을 사용하면 문자가 아닌 코드로 쿼리를 작성할 수 있기 때문에 컴파일 시점에 문법 오류를 쉽게 확인할 수 있고, 타입 안정성(type-safe)을 지키면서 동적인 쿼리를 편리하게
ReadOnlyQuery 최적화
jpa
JPA의 영속성 컨텍스트는 변경 감지를 위해 스냅샷 인스턴스를 보관하는 특징이 있다. 하지만 엔티티를 단순 조회하는 경우에는 컨텍스트에 있는 엔티티를 다시 꺼내오거나 수정할 필요가 없기 때문에 스냅샷 인스턴스를 저장하는 것이 메모리적으로 낭비이다. 이럴 경우 아래의 방법으로 메모리 사용량을 최적화할 수 있다. 스칼라 타입으로 조회 스칼라 타입은 영속성 컨텍스트가 관리하지 않기 떄문에, 엔티티가 아닌 스칼라 타입으로 조회하면 읽기 전용 쿼리에서 메모리를 절약할 수 있다. SELECT u.id, u.name, u.age FROM user u 읽기 전용 쿼리 힌트 사용 하이버네이트 전용 힌트인 org.hibernate.readOnly를 사용하면 엔티티를 읽기 전용으로 조회할 수 있다. 읽기 전용이므로
1차캐시
캐싱
캐시란 무엇일까? Cache는 간단히 말해서 나중의 요청에 대한 결과를 미리 저장했다가 빠르게 사용하는 것이다. 컴퓨터에서 중요한 메인 데이터들은 전원이 꺼져도 저장되는 SSD, HDD 등의 Secondary Memory에 저장된다. Secondary Memory에 저장된 데이터들은 장기적으로 안정적이게 저장될 수 있지만, 기술 한계상 데이터를 IO 하는 속도가 느리다. 그렇기 때문에 컴퓨터에서는 데이터 처리에 바로 필요한 데이터를 Main Memory인 RAM에 끌어놓거나, CPU에서 빠르게 접근할 수 있는 CPU Registers, Cache Memory의 형태로 저장하여 더 빠르고 쉽게 접근할 수 있도록 한다. 애플리케이션 서버(Spring JPA?)가 DB에 접근할 때를 예로 들어보면, DB에 연결
2차캐시
캐싱
애플리케이션에서 공유하는 캐시를 JPA는 공유 캐시(Shared Cache)또는 2차 캐시 (Second Level Cache, L2 Cache)라 부른다. 분산 캐시나 클러스터링 환경의 캐시는 애플리케이션보다 더 오래 유지 될 수도 있다. 2차 캐시를 적절히 활용하면 데이터베이스 조회 횟수를 획기적으로 줄일 수 있다. 1차캐시는 못하고 2차캐시는 할 수 있는 것 1차캐시는 영속성 컨텍스트에서 관리되는 캐시로, 트랜잭션이 시작하고 종료할 때 까지만 유지된다. 그렇기 때문에 같은 트랜잭션에서 한 엔티티를 여러번 가져와야할때 효과를 볼 수 있다. 하지만 만약에 여러 유저가 한 엔티티의 정보를 필요로 한다면 어떨까? 여러 요청들에서 공통적으로 같은 엔티티의 데이터가 필요한 경우에는, 1차캐시가 효용을 미치지 못한
영속성 컨텍스트
캐싱
영속성 컨텍스트랑 말 그대로 엔티티를 영속화(영구저장) 시켜주는 환경이다. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스(1차캐시) 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다. 영속성 컨텍스트는 엔티티 매니저를 생성할 때 하나 만들어지고, 엔티티 매니저를 통해 영속성 컨텍스트에 접근하고 관리할 수 있다. 영속성 컨텍스트는 엔티티를 여러가지 상태로 저장한다. 엔티티의 생명주기 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태 영속(managed): 영속성 컨텍스트에 저장된 상태 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태 삭제(removed): 삭제
트랜잭션 전파 설정
jpa
Spring에서 사용하는 어노테이션 @Transactional은 해당 메서드를 하나의 트랜잭션 안에서 진행할 수 있도록 만들어주는 역할을 한다. 이때 트랜잭션 내부에서 트랜잭션을 또 호출한다면 스프링에서는 어떻게 처리하고 있을까? 새로운 트랜잭션이 생성될 수도 있고, 이미 트랜잭션이 있다면 부모 트랜잭션에 합류할 수도 있을 것이다. 진행되고 있는 트랜잭션에서 다른 트랜잭션이 호출될 때 어떻게 처리할지 정하는 것을 ‘트랜잭션의 전파 설정’이라고 부른다. 전파 설정 옵션 트랜잭션의 전파 설정은 @Transactional의 옵션 propagation을 통해 설정할 수 있다. 각 옵션은 아래와 같다. REQUIRED (default) 부모 트랜잭션이 존재한다면 부모 트랜잭션으로 합류하고, 부모 트랜잭션이 없다면 새