스프링 DB 2 - JPA

동동주·2024년 5월 26일
0

인프런_스프링 DB

목록 보기
2/3

1. JPA

  • JdbcTemplate이나 MyBatis 같은 SQL 매퍼 기술은 SQL을 개발자가 직접 작성
  • JPA를 사용하면 SQL도 JPA가 대신 작성하고 처리해준다.
  • 실무에서는 JPA를 더욱 편리하게 사용하기 위해 스프링 데이터 JPA와 Querydsl이라는 기술을 함께 사용한다.

❓JPA 쓰는 이유

  • 엔티티 신뢰 문제, 모든 객체를 미리 로딩할 수는 없다
  • 진정한 의미의 계층 분할이 어렵다
    객체를 자바 컬렉션에 저장하듯이 DB에 저장할 수는 없을까!

2. application.properties

#JPA log
#1.
logging.level.org.hibernate.SQL=DEBUG
#2.
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

#1.는 하이버네이트가 생성하고 실행하는 SQL을 확인할 수 있다.
#2.는 SQL에 바인딩되는 파라미터를 확인할 수 있다.

3. JPA 적용 코드

  • JPA에서 가장 중요한 부분은 객체와 테이블을 매핑하는 것이다. JPA가 제공하는 애노테이션을 사용해서 'Item' 객체와 테이블을 매핑하는 걸 예시로 보자.
 @Slf4j
 @Repository
 @Transactional
 public class JpaItemRepositoryV1 implements ItemRepository {
 private final EntityManager em;
 public JpaItemRepositoryV1(EntityManager em) {
 this.em = em;
    }
    @Override
 public Item save(Item item) {
        em.persist(item);
 return item;
    }
    @Override
 public void update(Long itemId, ItemUpdateDto updateParam) {
 Item findItem = em.find(Item.class, itemId);
        findItem.setItemName(updateParam.getItemName());
        findItem.setPrice(updateParam.getPrice());
        findItem.setQuantity(updateParam.getQuantity());
    }
    @Override
 public Optional<Item> findById(Long id) {
 Item item = em.find(Item.class, id);
 return Optional.ofNullable(item);
    }

🔖private final EntityManager em

생성자를 보면 스프링을 통해 엔티티 매니저(EntityManager)라는 것을 주입받은 것을 확인할 수 있다. JPA의 모든 동작은 엔티티 매니저를 통해서 이루어진다. 엔티티 매니저는 내부에 데이터소스를 가지고 있고, 데이터베이스에 접근할 수 있다.

🔖@Transactional

JPA의 모든 데이터 변경(등록, 수정, 삭제)은 트랜잭션 안에서 이루어져야 한다. 조회는 트랜잭션이 없어도 가능하다. 변경의 경우 일반적으로 서비스 계층에서 트랜잭션을 시작하기 때문에 문제가 없다. 하지만 이번 예제에서는 복잡한 비즈니스 로직이 없어서 서비스 계층에서 트랜잭션을 걸지 않았다. JPA에서는 데이터 변경시 트랜잭션이 필수다. 따라서 리포지토리에 트랜잭션을 걸어주었다. 다시한번 강조하지만 일반적으로는 비즈니스 로직을 시작하는 서비스 계층에 트랜잭션을 걸어주는 것이 맞다.

🔖Optional

  • Null이나 Null이 아닌 값을 저장하는 컨테이너 클래스인 Optional이 추가되었다.
  • Optional은 null을 반환하면 오류가 발생할 가능성이 매우 높은 경우에 '결과 없음'을 명확하게 드러내기 위해 메소드의 반환 타입으로 사용되도록 매우 제한적인 경우로 설계되었다는 것이다.

💡[ 올바른 Optional 사용법 가이드 ]

  • Optional 변수에 Null을 할당하지 말아라
  • 값이 없을 때 Optional.orElseX()로 기본 값을 반환하라
    - 가급적이면 isPresent()로 검사하고 get()으로 값을 꺼내기 보다는 orElseGet 등을 활용해 처리하도록 하자.
private String findDefaultName() {
    return ...;
}

// AVOID
public String findUserName(long id) {

    Optional<String> optionalName = ... ;

    if (optionalName.isPresent()) {
        return optionalName.get();
    } else {
        return findDefaultName();
    }
}

// PREFER
public String findUserName(long id) {

    Optional<String> optionalName = ... ;
    return optionalName.orElseGet(this::findDefaultName);
}
  • 단순히 값을 얻으려는 목적으로만 Optional을 사용하지 마라
  • 생성자, 수정자, 메소드 파라미터 등으로 Optional을 넘기지 마라
  • Collection의 경우 Optional이 아닌 빈 Collection을 사용하라
  • 반환 타입으로만 사용하라

4. jpql

JPA에서 단순히 PK를 기준으로 조회하는 것이 아닌, 여러 데이터를 복잡한 조건으로 데이터를 조회하려면 어떻게 하면 될까?

💡 findAll - 목록 조회 코드

public List<Item> findAll(ItemSearchCond cond) {
String jpql = "select i from Item i";
//동적 쿼리 생략
TypedQuery<Item> query = em.createQuery(jpql, Item.class);
return query.getResultList();
}
  • JPA는 JPQL(Java Persistence Query Language)이라는 객체지향 쿼리 언어를 제공한다. 주로 여러 데이터를 복잡한 조건으로 조회할 때 사용한다.
  • SQL이 테이블을 대상으로 한다면, JPQL은 엔티티 객체를 대상으로 SQL을 실행한다고 생각하면 된다.
  • 결과적으로 JPQL을 실행하면 그 안에 포함된 엔티티 객체의 매핑 정보를 활용해서 SQL을 만들게 된다

💣 동적 쿼리 문제

JPA를 사용해도 동적 쿼리 문제가 남아있다. 동적 쿼리는 뒤에서 설명하는 Querydsl이라는 기술을 활용하면 매우 깔끔하게 사용할 수 있다. 실무에서는 동적 쿼리 문제 때문에, JPA 사용할 때 Querydsl도 함께 선택하게 된다.

5. JPA 예외 반환

  • EntityManager는 순수한 JPA 기술이고, 스프링과는 관계가 없다. 따라서 엔티티 매니저는 예외가 발생하면 JPA 관련 예외를 발생시킨다.
    - 추가로 JPA는 IllegalStateException, IllegalArgumentException을 발생시킬 수 있다.

6. 공부 부족한 부분


출처:

0개의 댓글