JPA - 2

웰시고기·2021년 2월 17일
0

Persistence Context

  • 프로그램의 모든 Entity instance들과 데이터베이스 사이의 관계를 나타낸다.
  • Persistence Context Entity States
    - Transient : 아직 ID를 부여받지 않고 Persistence context에 연관되지 않음
    - Managed : Persistence context에 의해 관리되고 해당 entity의 변경은 데이터베이스에 반영됨
    - Detached : 이전에 관리받았지만 현재는 관리되지 않음
    - Removed : Database에서 곧 지워질 예정. 아직 객체와 ID는 존재

Entity Manager

  • Entity의 Persistence State를 변경, 관리한다.
  • 명령어
    - Persist : 아직 Managed되지 않은 Entity를 Managed로 바꾼다.
    - Find : id를 통해 Managed인 Entity를 찾는다.
    - Merge : Detached인 Entity를 업데이트하고 Managed인 Entity를 반환한다. 만일 데이터베이스에 Entity를 못 찾으면 새로 만들어서 Persist한다.
    - Remove : Entity를 detach하고 데이터베이스에서 지운다.

Lazy Loading

  • fetch type을 수정해서 Entity가 불필요하게 연관된 데이터를 로딩하는 것을 방지할 수 있다.
  • FetchType.Eager : 항상 Entity를 조회할 때 연관된 데이터를 모두 같이 가져온다.
  • FetchType.Lazy : 연관된 데이터가 참조될 때까지 가져오지 않고 기다린다.
  • 기본으로 @OneToMany, @ManyToMany는 FetchType.Lazy, @ManyToOne, @OneToOne은 FetchType.Eager이다.
  • 아래는 FetchType.Lazy를 사용한 예시이다.
@Entity
public class Person {

   @Id
   @GeneratedValue
   Long id;

   @OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
   List<Outfit> outfits;

   private String name;
   private int age;
   private String favoriteComposer;

   /* rest of class */
}

Cascading

  • CascadeType을 지정해서 하나의 Entity를 변경할 때 Persistence 명령어가 연관된 다른 Entity에게도 전달되게 할 수 있다.
  • 아래는 CascadeType.ALL을 사용한 예시이다.
@Entity
public class Person {

   @Id
   @GeneratedValue
   Long id;

   @OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
   List<Outfit> outfits;

   private String name;
   private int age;
   private String favoriteComposer;

   /* rest of class */

쿼리를 작성하는 방법

  • JPQL
    - EntityManager를 통해 쿼리를 전달하고 원하는 객체를 반환하게 해준다.
    - 다음은 JPQL을 통해 쿼리를 생성하고 데이터를 가져오는 예시이다.
private static final String FIND_PERSON_BY_COMPOSER =
       "select p from Person p " +
       "where p.favoriteComposer like :favoriteComposer";

public Person findPersonByFavoriteComposer(String favoriteComposer){
   TypedQuery<Person> query = entityManager.createQuery(FIND_PERSON_BY_COMPOSER, Person.class);
   query.setParameter("favoriteComposer", favoriteComposer);
   return query.getSingleResult();
}
  • NamedQuery
    - NamedQuery를 통해 미리 쿼리를 구성하고 컴파일 검사를 하게 할 수 있다.
    - name은 전역적으로 유일해야하며 보통 각 Entity class 위에 정의해 둔다.
@NamedQueries({
 @NamedQuery(
  name = "Outfit.findByHat", 
  query = "select o from Outfit o where o.hat = :hat"),
 @NamedQuery(
  name = "Outfit.findBySock", 
  query = "select o from Outfit o where o.sock = :sock")
})
  • criteria builder
    - 다음과 같이 criteria builder를 활용할 수도 있다.
List<Humanoid> findHumanoidByOutfitCriteria(Outfit o) {
   CriteriaBuilder cb = entityManager.getCriteriaBuilder();
   CriteriaQuery<Humanoid> criteria = cb.createQuery(Humanoid.class);
   Root<Humanoid> root = criteria.from(Humanoid.class);

   criteria.select(root).where(cb.isMember(o, root.get("outfits")));
   return entityManager.createQuery(criteria).getResultList();
}

Repsitory Pattern

  • Repository pattern은 데이터베이스를 collection으로 보는 방법이다.
  • 다음은 코드 예시이다.
@Repository
public class HumanoidRepositoryImpl implements HumanoidRepository {
   @Autowired
   EntityManager entityManager;

   @Override
   public Humanoid save(Humanoid h) {
       if(h.getId() == null || h.getId() <= 0) {
           entityManager.persist(h);
       } else {
           h = entityManager.merge(h);
       }
       return h;
   }

   @Override
   public Humanoid findById(Long id) {
       return entityManager.find(Humanoid.class, id);
   }

   @Override
   public void delete(Humanoid h) {
       if (entityManager.contains(h)) {
           entityManager.remove(h);
       } else {
           entityManager.remove(entityManager.merge(h));
       }
   }

Spring Data JPA

  • JPA를 쉽게 사용하기 위해 함수 이름만으로 코드를 작성해준다.
  • 단순히 Spring Data interface를 상속 받으면 사용할 수 있다.
  • 아래는 예제 코드로 구현 없이 함수 이름만 있으면 된다.
@Repository
public interface HumanoidRepository extends JpaRepository<Humanoid, Long> {
   //you can reference associations and attributes by chaining
   //attribute names. Here we reference Humanoid.outfits.hat
   List<Humanoid> findAllByOutfitsHat(String hat);

   //you can provide specific JPQL Queries
   @Query("select h from Humanoid h where :outfit member of h.outfits ")
   List<Humanoid> findAllByOutfit(@Param("outfit") Outfit outfit);

   //does the same as above
   List<Humanoid> findAllByOutfitsContaining(Outfit outfit);

   //automatically uses query named Humanoid.findAllNamedQuery
   List<Humanoid> findAllNamedQuery(Outfit outfit);

}

Flushing and Transaction

  • Flushing : Persistence Context의 상태를 데이터베이스에 동기화하는 과정
  • Transaction : 일련의 실패 혹은 성공한 과정
  • Flushing을 통해 Persistence Context는 Level 1 캐시의 역할을 한다.
  • 다음과 같이 @Transactional을 통해 사용할 수 있다.
@Transactional
public void createOutfitForPerson(Outfit outfit, Long personId) {
   outfitRepository.save(outfit);

   //getOne throws EntityNotFoundException if personId doesn't exist!
   humanoidRepository.getOne(personId).getOutfits().add(outfit);
}

0개의 댓글