12월 14일 목 TIL

장숭혁·2023년 12월 14일
0

TIL작성

목록 보기
31/60

FetchType과 fetch 옵션은 JPA(Java Persistence API)에서 엔티티 간 관계를 정의하고 로딩하는 방식을 제어하는 데 사용된다. 데이터 베이스에서 데이터를 가져올 때 연관된 엔티티들을 어떻게 함께 가져올지 결정하는데 영향을 미친다.

FetchType : 엔티티 간의 관계에서 데이터를 로딩할 때 사용된다. 기본적으로 LAZY(지연 로딩)으로 설정되어 있다. EAGER(즉시 로딩)로 설정하면 해당 엔티티를 로딩할 때 즉시 관련 엔티티들도 함께 가져온다.

@Entity
public class ParentEntity {
    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) // Lazy로 설정
    private List<ChildEntity> children;
    
    
    // 작가 정보 로딩하는 메소드는 필요할 때 호출
    public Author lazyLoadAuthor() {
        if (author == null) {
            // 작가 정보를 필요한 시점에 로딩
            // 예시: author = authorRepository.findAuthorById(authorId);
        }
        return author;
    }
    // 다른 필드와 메소드들...
}

ParentEntity는 ChildEntity와 OneToMany 관계를 맺고 있다. 이 설정은 부모 엔티티를 조회할 때 연관된 자식 엔티티를 즉시 가져오지 않고, 부모 엔티티에 대한 접근이 일어날 때 자식 엔티티를 가져오지 않는다는 의미이다. Parent를 조회한 후에 children 필드를 접근할 때 실제로 데이터베이스에서 children을 가져온다.

참고

@Entity
public class ChildEntity {
    @ManyToOne(fetch = FetchType.LAZY) // ParentEntity와의 관계 설정
    private ParentEntity parent;
    // 다른 필드와 메소드들...
}
@Entity
public class ParentEntity {
   @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER) // Eager로 설정
   private List<ChildEntity> children;
   
   // 작가 정보를 이미 함께 로딩하여 사용할 수 있음
   public Author getAuthor() {
       return author;
   }
   // 다른 필드와 메소드들...
}

부모 엔티티를 가져올 때 연관된 자식 엔티티도 즉시 가져오게 된다.
FetchType을 설정함으로써, 어떤 방식으로 연관된 엔티티들을 로딩할지를 JPA에게 알려줄 수 있다.

두 엔티티간 다른 타입 가령 FetchType.EAGER , FetchType.LAZY 엇갈리게 설정되는것은 허용되지 않는다

FetchJoin : JPA에서 엔티티 그래프 로딩을 수행할 때 사용되는 방법 중 하나이다. JPQL(Java Persistence Query Language)이나 JPA의 Criteria API에서 사용되며, EAGER 로딩과 유사한 동작을 수행한다. 쿼리를 실행할 때 한 번의 쿼리로 모든 연관된 엔티티들을 함께 가져오기 위해 사용된다. 지연 로딩으로 인한 N+1 문제를 방지하고 성능을 향상시킬 수 있다.

이러한 옵션들은 애플리케이션의 성능과 데이터 접근 패턴에 맞게 선택되어야 한다.EAGER 로딩은 한 번에 많은 양의 데이터를 가져와서 성능 이슈를 발생시킬 수 있으므로 신중하게 사용해야 한다. LAZY 로딩은 필요한 시점에만 데이터를 가져오기 때문에 필요한 경우에만 연관된 데이터를 로드하여 불필요한 데이터베이스 쿼리를 줄일 수 있다.

  • Author와 그가 쓴 모든 Book을 함께 로딩하는 경우 FetchJoin을 사용할 수 있다.

    @Entity
    public class Author {
       @OneToMany(mappedBy = "author")
       private List<Book> books;
    
       // 다른 필드와 메소드들...
    
       public List<Book> getBooks() {
           return books;
       }
    }
    `
    @Entity
    public class Book {
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
    
       private String title;
    
       @ManyToOne(fetch = FetchType.LAZY)
       @JoinColumn(name = "author_id")
       private Author author;
    
    	public Author lazyLoadAuthor() {
           if (author == null) {
               // 작가 정보를 필요한 시점에 로딩
               // 예시: author = authorRepository.findAuthorById(authorId);
           }
           return author;
       }
       // 다른 필드와 메소드들...
       // Getter와 Setter 등...
       }
       `String jpql = "SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = 
       :authorId";Author author = entityManager.createQuery(jpql, Author.class)
       .setParameter("authorId", 1L)
       .getSingleResult();
       //JPQL을 사용하여 FetchJoin을 적용하는 방법.
    
  • 사용 시 주의해야 할 점은, 데이터 양이 많은 경우 모든 연관 엔티티들을 함께 로딩하면 데이터베이스 성능에 부담을 줄 수 있다는 점이다.

  • JPQL : JPQL은 Java Persistence Query Language의 약자로, 데이터베이스에 독립적인 객체 지향 쿼리 언어이다. JPA(Java Persistence API)에서 엔티티 객체를 대상으로 쿼리를 작성하고 실행하는 데 사용된다. JPQL은 엔티티와 그와 연관된 객체들에 대한 쿼리를 작성할 수 있다. 이는 객체 지향적인 관점에서 데이터베이스를 조작할 수 있게 해준다. 엔티티의 속성과 관계를 사용하여 쿼리를 작성할 수 있으며, 데이터베이스에 종속적이지 않기 때문에 사용하는 데이터베이스의 유형에 상관없이 동일한 쿼리를 사용할 수 있다.
String jpql = "SELECT b FROM Book b WHERE b.genre = 'Fantasy'";
List<Book> fantasyBooks = entityManager.createQuery(jpql, Book.class).getResultList();

이 쿼리는 EntityManager를 통해 실행되고, 결과로 Fantasy 장르의 책들을 포함하는 Book 엔티티의 리스트를 얻게 된다.

  • Criteria API : Criteria API는 JPA(Java Persistence API)에서 제공하는 동적 쿼리를 작성하기 위한 자바 기반의 쿼리 작성 방법이다. 자바 코드로 쿼리를 작성할 수 있도록 해준다. 주로 문자열 기반의 쿼리(JPQL)보다 안전하고 유지보수가 쉬운 쿼리 작성을 위해 사용된다. Criteria API는 컴파일 시점에 타입 안전성을 제공하고, 오타나 문법 오류 등의 실수를 컴파일 시간에 확인할 수 있게 해준다. 동적으로 쿼리를 조립하고, 복잡한 조건을 구성하기에 용이하다. CriteriaBuilder를 사용하여 쿼리를 만들고 CriteriaQuery를 통해 실행한다. 여러가지 조건을 추가하고, 조인을 수행하며, 결과를 정렬하는 등 다양한 쿼리 작성 기능을 제공한다.
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Book> criteriaQuery = criteriaBuilder.createQuery(Book.class);
    Root<Book> root = criteriaQuery.from(Book.class);

criteriaQuery.select(root)
.where(criteriaBuilder.equal(root.get("genre"), "Fantasy"));

List fantasyBooks = entityManager.createQuery(criteriaQuery).getResultList();

  Root를 통해 쿼리의 시작점인 엔티티를 지정한다. where 조건을 추가하여 장르가 'Fantasy'인 책들을 조회하는 쿼리를 작성한다.  Criteria API는 동적으로 쿼리를 생성하고 타입 안전성을 제공하여 쿼리 작성의 안정성과 가독성을 높일 수 있다.

  - N+1 문제 : 데이터베이스 쿼리에서 발생하는 성능 문제 중 하나로, 일괄 쿼리 실행 대신 반복적으로 여러 번의 추가 쿼리를 발생시켜 성능 저하를 유발하는 현상이다. 관계형 데이터베이스에서 ORM(Object-Relational Mapping)을 사용할 때 발생한다. 부모 엔티티를 조회한 후에 그와 연관된 자식 엔티티들을 가져와야 하는 상황에서 발생할 수 있다.
  

@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Child> children;

// 다른 필드와 메소드들...

}

@Entity
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private Parent parent;

// 다른 필드와 메소드들...

}

  - Parent 엔티티는 Child 엔티티와 일대다 관계를 맺고 있다. 
  - Parent를 조회할 때 children 필드가 FetchType.LAZY로 설정되어, 자식 엔티티들은 즉시 로딩되지 않는다.
  - 실제 호출 예시
  

Parent parent = entityManager.find(Parent.class, parentId);
List children = parent.getChildren(); // 이 부분에서 추가 쿼리 발생 가능성

  
  - entityManager.find(Parent.class, parentId)를 호출하여 Parent 엔티티를 가져온다.
  - children 필드는 FetchType.LAZY로 설정되어, 실제로는 자식 엔티티들이 로딩되지 않는다.
  - parent.getChildren()과 같은 코드에서 children을 접근할 때마다 자식 엔티티들을 가져오는 추가 쿼리가 발생할 수 있다.
  -  N개의 자식 엔티티가 있다면, 각각의 접근마다 자식 엔티티를 가져오기 위한 추가 쿼리가 발생하게 된다.
  -  이로 인해 총 N+1번의 쿼리가 발생하게 되는 것이다.(부모 엔티티를 가져오는 쿼리 이후에 자식 엔티티를 가져오는 N개의 추가 쿼리가 발생)
  - EAGER로 설정되어 있다면, 부모 엔티티를 조회할 때 연관된 자식 엔티티들도 함께 즉시 로딩된다. 이는 한 번의 쿼리로 부모 엔티티와 연관된 모든 자식 엔티티를 가져오므로 N+1 문제를 해결할 수 있다.
  
  ### @JoinColumn
  - 양방향 연관관계에서 외래 키를 지정하기 위해 사용된다. @OneToMany와 @ManyToOne 관계에서 서로를 가리키는 연관관계에서 외래 키를 정의 할 때 사용된다. 단방향 관계에서는 사용하지 않아도 된다. 일반적으로, 양방향 연관 관계일 때는 @JoinColumn을 사용하여 외래 키를 명시적으로 지정해준다.단방향 연관 관계에서는 JPA가 기본적으로 관례에 따라 외래 키를 자동으로 생성하므로 @JoinColumn을 명시적으로 사용하지 않아도 된다. @OneToMany 측에서 mappedBy 속성을 사용하여 어떤 속성이 이 관계의 주인(Owner)인지를 나타내어야 한다. 주인 엔티티는 외래 키(Foreign Key)를 관리하고, 데이터베이스에 실제로 반영된다. 이것은 양쪽 엔티티 중 하나가 다른 쪽을 참조하는 관계에서 발생한다.
  

@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "parent_id")
private List<Child> children;
// 다른 필드와 메소드들...

}

@Entity
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 다른 필드와 메소드들...
}

  - 'Parent' 엔티티가 관계의 주인이며, 'Child' 엔티티는 'Parent' 엔티티를 참조하지 않는다. 즉, 'Parent'에서 'Child'로만 접근이 가능하고, 'Child'에서 'Parent'로의 접근은 불가능한다.
  - 데이터베이스에는 Parent' 엔티티는 'Child' 엔티티를 참조하기 위해 'Child' 테이블에 'parent_id'라는 외래 키를 만든다.  이 외래 키는 'Parent' 테이블의 'id'를 참조한다.
  - Child' 테이블의 각 레코드는 'parent_id' 필드를 통해 자신이 어떤 'Parent'에 속하는지 식별할 수 있다. 하지만, 'Child' 엔티티는 'Parent' 엔티티를 직접 참조하지 않기 때문에, 'Child' 엔티티에서 'Parent' 엔티티를 찾으려면 추가적인 조인 연산이 필요합니다. 이는 단방향 관계의 특징이다.
profile
코딩 기록

0개의 댓글

관련 채용 정보