- 객체 지향 프로그래밍의 장점을 활용할 수 있음
- 이를 통해, 비즈니스 로직 구현 및 테스트 구현이 편리함
- 각종 디자인 패턴 사용하여 성능 개선 가능
- 코드 재사용
그러나 생각처럼 쉽지않다.. 여러가지 문제가 발생한다
왜냐하면 객체와 RDB의 특성이 너무 다르다.
- 객체 : 객체간에 멤버변수나 상속관계를 맺을 수 있다.
- RDB : 테이블들은 상속관계가 없고 모두 독립적으로 존재한다.
해결방법 : 매핑정보에 상속정보를 넣어준다. (@OneToMany
,@ManyToOne
)
- 객체 : 참조를 통해 관계를 가지며 방향을 가진다. (다대다 관계도 있음)
- RDB : 외래키(FK)를 설정하여 Join 으로 조회시에만 참조가 가능하다. (즉, 다대다는 매핑 테이블 필요)
해결방법 : 매핑정보에 방향정보를 넣어준다. (@JoinColumn
,@MappedBy
)
- 객체 : 참조를 통해 다른 객체로 순차적 탐색이 가능하며 콜렉션도 순회한다.
- RDB : 탐색시 참조하는 만큼 추가 쿼리나, Join 이 발생하여 비효율적이다.
해결방법 : 매핑/조회 정보로 참조탐색 시점을 관리한다.(@FetchType
,fetchJoin()
)
- 객체 : 멤버 객체크기가 매우 클 수 있다.
- RDB : 기본 데이터 타입만 존재한다.
해결방법 : 크기가 큰 멤버 객체는 테이블을 분리하여 상속으로 처리한다. (@embedded
)
- 객체 : 객체의 hashCode 또는 정의한 equals() 메소드를 통해 식별
- RDB : PK 로만 식별
해결방법 : PK 를 객체 Id로 설정하고 EntityManager는 해당 값으로 객체를 식별하여 관리 한다.(@Id
,@GeneratedValue
)
- 1차 캐시, 2차 캐시를 사용하여 최적화를 했다.
- 캐싱 기능은 객체지향 프로그래밍이 가진 가장 큰 장점이다.
- 영속성 컨텍스트 내부에는 엔티티를 보관하는 저장소가 있는데 이를 1차 캐시라고 한다.
- 일반적으로 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효하다.
- 1차 캐시는 한 트랜잭션 계속해서 원본 객체를 넘겨준다.
- 애플리케이션 범위의 캐시로, 공유 캐시라고도 하며, 애플리케이션을 종료할 때 까지 캐시가 유지된다.
- 2차 캐시는 캐시 한 객체 원본을 넘겨주지 않고 복사본을 만들어서 넘겨준다.
- 복사본을 주는 이유는 여러 트랜잭션에서 동일한 원본객체를 수정하는일이 없도록 하기 위해서이다.
- Entity에
@Cacheable
적용 후, yml파일에 설정을 추가해주면 사용할 수 있다.# application.yml spring.jpa.properties.hibernate.cache.use_second_level_cache: true # 2차 캐시 활성화합니다. spring.jpa.properties.hibernate.cache.region.factory_class: XXX # 2차 캐시를 처리할 클래스를 지정합니다. spring.jpa.properties.hibernate.generate_statistics: true # 하이버네이트가 여러 통계정보를 출력하게 해주는데 캐시 적용 여부를 확인할 수 있습니다.
sharedCache.mode
설정을 통해서도 가능하다# appplication.yml spring.jpa.properties.javax.persistence.sharedCache.mode: ENABLE_SELECTIVE
cache mode 종류
ALL 모든 엔티티를 캐시합니다. NONE 캐시를 사용하지 않습니다. ENABLE_SELECTIVE Cacheable(true)로 설정된 엔티티만 캐시를 적용합니다. DISABLE_SELECTIVE 모든 엔티티를 캐시하는데 Cacheable(false)만 캐시하지 습니다. UNSPECIFIED JPA 구현체가 정의한 설정을 따릅니다
오늘은 ORM이 등장하면서 어떠한 문제가 있었고, 이를 어떻게 해결했는지를 알아보았다.
공부하다 보니 평소에 JPA에서 쓰고있던 어노테이션들이 이러한 이유로 사용된다는걸 알아서 신기했다.
공부하면 할 수록 느낀점은 관계형 데이터베이스를 객체처럼 쓸 수 있게 해주는 JPA가 정말 편리한거 같다.