JPA 활용

바그다드·2023년 3월 27일
0

JPA

ORM매핑

  • 객체와 테이블을 매핑해보자
@Entity
//@Table(name = "item")
public class Item {

    //strategy = GenerationType.IDENTITY - pk값을 데이터베이스에서 생성하는 방식
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // length : jpa로 테이블을 생성할 때 컬럼의 길이 값으로 활용
    @Column(name = "item_name", length = 10)
    private String itemName;
    private Integer price;
    private Integer quantity;

    //JPA는 기본생성자가 필수!!
    public Item() {
    }

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}
  • JPA는 @Entity를 이용하여 매핑한다
  • @Column은 생략이 가능한데 생략할 경우 필드의 이름을 카멜케이스에서 스네이크 케이스로 바꿔 매핑을 해준다.
  • @GeneratedValue라는 어노테이션을 사용하면 컬럼의 값을 DB에서 임의로 부여해준다.
@Repository
@Transactional
public class JpaItemRepositoryV1 implements ItemRepository {
	private final EntityManager em;
    
	public JpaItemRepositoryV1(EntityManager em) {
		this.em = em;
}
  • 이건 Repository를 일부분 가져온 것인데 EntityManager를 통해 JPA의 모든 동작이 이루어진다.
  • 또한 JPA의 모든 데이터 변경은 트랜잭션 안에서 이루어져야 하고, insert나 update같은 데이터 변경이 일어나는 경우 비즈니스 로직을 시작하는 서비스 계층에서 트랜잭션을 걸어준다.
  • 원래는 트랜잭션 메니저, 데이터 소스 등 설정을 해줘야 하는 것들이 있으나 스프링 부트가 자동화를 해준다!!

JPQL

  • Java Persistence Query Language로 객체지향 쿼리 언어를 뜻한다.
  • 복잡한 조건의 쿼리를 사용해야 할 때 사용한다.
String jpql = "select i from Item i";
  • 언뜻 보면 sql문 같지만 item(소문자)이 테이블을 뜻하는게 아니라 객체 Item(대문자)을 뜻한다.
  • 결과적으로 JPQL 안에 포함된 엔티티 객체 매핑 정보를 통해 sql을 만든다.

JPA 단점

  • 동적 쿼리를 사용할 때 문제가 생긴다 너무 복잡하다ㅜㅜ
    아래의 코드를 보자
public List<Item> findAll(ItemSearchCond cond) {
        // 여기서 Item은 테이블 item이 아니라 객체 Item을 말한다
        // Item객체를 가지고 온다
        String jpql = "select i from Item i";

        Integer maxPrice = cond.getMaxPrice();
        String itemName = cond.getItemName();

        if (StringUtils.hasText(itemName) || maxPrice != null) {
            jpql += " where";
        }
        boolean andFlag = false;
        if (StringUtils.hasText(itemName)) {
            jpql += " i.itemName like concat('%',:itemName,'%')";
            andFlag = true;
        }
        if (maxPrice != null) {
            if (andFlag) {
                jpql += " and";
            }
            jpql += " i.price <= :maxPrice";
        }
        log.info("jpql={}", jpql);
        TypedQuery<Item> query = em.createQuery(jpql, Item.class);
        if (StringUtils.hasText(itemName)) {
            query.setParameter("itemName", itemName);
        }
        if (maxPrice != null) {
            query.setParameter("maxPrice", maxPrice);
        }

        return query.getResultList();
    }
  • ':itemName'은 MyBatis에서 #{itemName}으로 사용한 것처럼 파라미터를 지정할 때 사용한다.
  • 조건에 따라 조건에 맞는 객체를 가지고 오는 메서드인데, 조건이 있느냐 없느냐에 따라 조건이 바뀌기 때문에 이를 처리하는게 골치아프다.
  • 때문에 실무에서는 Querydsl을 사용한다고 한다.

JPA 예외 변환

  • 그렇다면 JPA에서 예외가 발생하면 어떻게 될까?
    JPA는 스프링과 관계 없는 순수 JPA기술이기 때문에 예외가 발생하더라도 JPA예외를 발생시킨다.
  • @Repository를 사용하면 스프링이 예외 변환 AOP 적용 대상으로 관리하여 프록시를 생성하고 여기서 JPA예외 변환기를 이용해 스프링 데이터 접근 예외로 변환해준다.

Spring Data JPA

  • JPA를 편하게 사용할 수 있게 도와주는 라이브러리
  • 공통 인터페이스 기능 뿐만 아니라
  • 쿼리 메서드 기능도 지원해준다
  • JpaRepository를 상속받는 인터페이스를 만들면 Spring Data Jpa가 프록시 기술을 이용해 해당 인터페이스의 구현체를 만들고 스프링 빈으로 등록해준다.
// 예시
public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {

    List<Item> findByItemNameLike(String itemName);

    List<Item> findByPriceLessThanEqual(Integer price);

    //쿼리 메서드
    List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);

    //쿼리 직접 실행
    @Query("select i from Item i where i.itemName like :itemName and i.price <= :price")
    List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);

}
  • 메소드 이름에는 특정 규칙이 있는데 규칙을 따라 메서드 이름을 지으면 그에 적합한 실제 쿼리문을 작성해준다

  • 자세한건 아래 공식 문서를 확인하자!
    링크텍스트
    링크텍스트

  • 하지만 메서드 이름을 보면 너무 복잡하다. 때문에 이런 경우 @Query를 사용하는게 좋다.

  • jpql문법을 활용하여 쿼리문을 작성하고, 변수(?)부분은 매개변수 부분에 @Param을 사용하여 이름을 일치시켜주면 된다.

@RequiredArgsConstructor
public class JpaItemRepositoryV2 implements ItemRepository {
	private final SpringDataJpaItemRepository repository;
  • 코드를 확인해보면 JpaItemRepositoryV2는 인터페이스인 SpringDataJpaItemRepository를 사용하는데 여기에는 스프링데이터JPA가 만들어준 프록시 객체가 주입된다.

예외 변환

  • 스프링 데이터 JPA에서는 스프링 데이터 JPA가 만들어주는 프록시에서 예외 변환을 해주기 때문에 @Repository가 없어도 예외 변환이 이루어진다!!!

그래도 여전히 동적 쿼리 문제는 해결되지 않았다,,,
이를 해결하기 위해 Querydsl이 등판하였다!!

profile
꾸준히 하자!

0개의 댓글