3 minute read

JPA

프록시

  • 연관된 객체를 처음부터 DB에서 조회하지 않고, 실제 사용하는 시점에 DB에서 조회할 수 있다.
  • 엔티티 하나를 조회할 때에는 EntityManager.find()를 사용하는데 이 메소드는 영속성 컨텍스트에 엔티티가 존재하지 않으면 DB를 조회한다.
    • 엔티티 사용 유무에 관계없이 DB 조회
  • EntityManager.getReference()를 사용해 엔티티의 실제 사용시점까지 DB를 미룰 수 있다.
  • 프록시 클래스는 실제 클래스를 상속받아 만들어져, 겉보기에는 실제 클래스와 동일하다.

특징

  • 프록시 객체는 처음 사용되는 시점에 한번만 초기화된다.
  • 초기화를 통해 실제 엔티티에 접근이 가능해진다. (실제 엔티티로 바뀌는 것이 아니다.)
  • 타입 체크에 주의해 사용해야 한다. (프록시 객체는 원본이 아닌 원본 엔티티를 상속받은 객체임을 잊지 말자)
  • 영속성 컨텍스트에 조회하려는 엔티티가 이미 있으면 프록시를 호출해도 실제 엔티티를 반환한다.
  • 초기화는 영속성 컨텍스트의 도움을 받아야 가능하다.

초기화

  • isLoaded(Object entity) 를 사용하여 프록시 인스턴스의 초기화 여부를 확인할 수 있다.

고아객체

  • 고아 객체 제거
    • JPA에서는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능을 제공한다.
    • 엔티티 매칭시 옵션에서 orphanRemoval = true 옵션을 주어 기능을 사용할 수 있다.
    • 주의점 : 참고하는 곳이 하나일때만 사용하게 해야 한다.
      • 왜 : 삭제한 엔티티를 다른 곳에서도 참조한다면 문제가 생길 수 있다
    •  @OneToOne@OneToMany에만 사용이 가능하다.

단방향 / 양방향 매핑

  • 연관관계 매핑: 객체의 참조와 테이블의 외래 키를 매핑하는 것

  • 고려사항
    • 방향 : 단방향, 양방향의 존재 // 단방향인지 양방향인지
    • 다중성 : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)
    • 연관관계의 주인 : 연관관계의 주인을 정해야 함
  • 객체 연관관계 vs 테이블 연관관계 정리
    • 객체는 참조(주소)로 연관관계를 맺는다.
    • 테이블은 외래 키로 연관관계를 맺는다.
    • 참조를 사용하는 객체의 연관관계는 단방향이다.
    • 외래 키를 사용하는 테이블의 연관관계는 양방향이다. A JOIN B , B JOIN A 둘다 가능하며 결과값도 같다.
  • 연관관계 매핑의 필요성
    • 테이블에서는 외래키를 통해 연관관계를 맺을 수 있지만 객체간의 관계를 표현하기 위해서는 외래키로 관계를 형성하면 객체지향적이지 않다.
    • -> 연관 관계 매핑을 통해 관계를 나타내자.

N+1 문제

  • 연관 관계에서 발생하는 이슈
  • 연관 관계가 설정된 엔티티를 조회할 경우, 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 되는 문제

해결 방안

  • Fetch Join
    • 실제 실행시 outer join 사용
    • JPQL로 작성한다.
    • 연관관계의 연관관계가 있을 경우에도 하나의 쿼리문으로 표현이 가능해진다.
    • 페이징 쿼리와 FetchType를 사용할 수 없으므로 신중하게 사용해야 한다.
@Query("select o from Owner o join fetch o.cats")
List<Owner> findAllJoinFetch();
  • EntityGraph
    • Eager 조회로 가져오며, 연관관계를 여기에 설정하여 사용할 수 있다.
    • 실제 실행시 outer join 사용
@EntityGraph(attributePaths = "cats")
@Query("select o from Owner o")
List<Owner> findAllEntityGraph();
  • 두가지 해결방법 모두 JPQL JOIN문 호출을 이용한다.
  • 중복된 데이터가 컬렉션 내에 존재하지 않도록 주의해야 한다.
    • Set 컬렉션을 사용한다.
    • JPQL 작성시 distinct 명령어를 사용한다.
  • BatchSize
    • org.hibernate.annotations.Batch@BatchSize Annotation을 사용한다.
    • 연관된 엔티티 조회시 지정 사이즈만킁의 SQL IN절을 사용해 조회한다.
  • QueryBuilder (추천 해결 방법)
    • Mybatis, QueryDSL, JOOQ, JDBC Template 등의 플러그인에서 지원
    • 플러그인을 사용해 구현하면 된다.

Test

DDD, TDD

  • TDD(Test Driven Development) : 테스트 주도 개발
    • 소스코드보다 테스트 케이스를 먼저 작성하는 순서로 개발을 진행하는 방법론
    • 짧은 개발 서클을 반복하는 소프트웨어 개발 프로세스.
    • 장점
      • 개발 비용 절감, 깔끔한 코드 작성
      • 신규 기능 추가 시 이전 기능 대비 성능 평가 가능
      • 개발시 작동 기능에 대한 사전 고려가 가능하다.
  • DDD(Domain Driven Design) : 도메인 주도 개발
    • 비즈니스의 도메인 패턴을 중심에 놓고 설계하는 방식
    • 도메인 모델부터 코드까지 함께 움직이는 구조의 모델 지향

JUnit 4 vs JUnit 5

기능/특징 JUnit 4 JUnit 5
애노테이션 기반 테스트 @Test, @Before, @After, @RunWith 등 사용 @Test, @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @DisplayName 등 사용
확장 모델(Extension) 없음 @ExtendWith를 통해 커스텀 확장을 구현하거나 @Disabled, @Tag 등을 활용할 수 있음
동적 테스트 생성 어려움 @TestFactory를 통해 동적으로 테스트를 생성할 수 있음
조건에 따른 테스트 Assume 클래스 활용 @EnabledOnOs, @EnabledIfSystemProperty, @EnabledIfEnvironmentVariable 등을 사용 가능
파라미터화 테스트 @Parameters@RunWith(Parameterized.class) 사용 @ParameterizedTest@ValueSource, @CsvSource, @MethodSource 등 사용
테스트 인스턴스 관리 기본적으로 각각의 테스트마다 인스턴스 생성 @TestInstance를 통해 테스트 인스턴스의 생명주기를 조정할 수 있음
테스트 이름 표시 직접 구현해야 함 @DisplayName을 사용하여 테스트 이름을 보다 명시적으로 표시할 수 있음
태깅 및 필터링 Categories@RunWith(Categories.class) 사용 @Tag를 사용하여 테스트를 태그하고 필터링할 수 있음
생명주기 훅 없음 TestExecutionListener를 통해 테스트 실행 전후에 추가 로직을 구현할 수 있음
Java version Java 5 이상 Java 8 이상
구조 All-in-one 구조 JUnit Platform, JUnit Jupiter, JUnit Vintage를 합한 구조

Leave a comment