Spring/[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술]

[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술] 데이터 접근 기술 - JPA

h2boom 2024. 11. 23. 17:14

JPA

  • JPA (Java Persistence API) : 자바에서 제공하는 표준 ORM 데이터 접근 기술이다.
    • 기본적이 SQL을 JPA가 작성하고 처리해준다.
  • ORM (Object Relational Mapping) : 객체 관계 매핑으로 객체는 객체대로 설계하고 관계형 DB는 관계형 DB대로 설계를 해서 ORM이 중간에서 매핑을 해준다.

JPA 설정

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  • JPA 사용을 위해 build.gradle에서 의존 관계 추가

 

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
  • JPA 로깅을 위한 application.properties 설정
    • ~ SQL = DEBUG : 하이버네이트가 생성하고 실행하는 SQL을 확인
    • ~ BasicBinder = TRACE : SQL에 바인딩되는 파라미터 확인
    • spring.jpa.show-sql=true : 이 설정은 로그가 아닌 System.out 콘솔을 통해서 SQL이 출력되므로 권장하지 않는다.

JPA 적용 - 개발, 레포지토리 분석

  • JPA에서 가장 중요한 부분은 객체와 테이블을 매핑하는 것으로 JPA가 제공하는 어노테이션을 사용해서 매핑한다.

 

@Data
@Entity
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "item_name", length = 10)
    private String itemName;
    
    private Integer price;
    
    private Integer quantity;

    public Item() {
    }

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}
  • JPA 엔티티 예제
    • @Entity : JPA가 사용하는 객체라는 의미로 JPA가 인식할 수 있도록 하는 어노테이션이다.
      • @Entity가 붙은 객체를 JPA에서 엔티티라고 한다.
    • @Id : 테이블의 PK와 해당 필드를 매핑한다.
    • @GeneratedValue (strategy = GenerationType.IDENTITY) : PK 값을 DB에서 생성하는 IDENTITY 방식을 사용한다는 것을 의미
      • ex) MySQL auto increment
    • @Column : 필드와 테이블 컬럼을 매핑한다.
      • length = 10 : JPA 매핑 정보로 DDL(create table)도 생성할 수 있는데 그때 컬럼의 길이 값으로 사용
      • @Colmun을 생략하는 경우 필드명을 테이블의 컬럼명으로 사용한다.
        • 스프링 부트를 사용하는 경우 필드명을 테이블 컬럼명으로 변경할 때 카멜케이스 형태를 언더스코어 형태로 자동 변환해준다. (필드명 itemName => 컬럼명 item_name)
    • JPA에서 프록시 기술을 사용할 때 필요하므로 public / protected 기본 생성자는 필수이다.

 

@Slf4j
@Repository
@Transactional
public class JpaItemRepository implements ItemRepository {

    private final EntityManager em;

    public JpaItemRepository(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);
    }

    @Override
    public List<Item> findAll(ItemSearchCond cond) {
        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();
    }
}
  • JPA Repository 예제
    • JPA의 모든 동작은 엔티티 매니저를 통해서 이뤄진다.
      • 엔티티 매니저는 내부에 데이터소스를 가지고 있고 DB에 접근할 수 있다.
    • @Transactional : JPA의 모든 데이터 변경 (등록, 수정, 삭제)은 트랜잭션 안에서 이뤄져야 하기 때문에 해당 어노테이션이 필요하다.
      • JPA에서 데이터 변경 시 트랜잭션은 필수이다.
      • 조회의 경우에는 트랜잭션이 필요 없이 가능하다.
      • 일반적으로 레포지토리가 아닌 서비스 계층에서 트랜잭션을 시작하기 때문에 문제가 없다.
    • save()
      • em.persist() : JPA에서 객체를 테이블에 저장할 때는 엔티티 매니저가 제공하는 persist()를 사용한다.
    • update()
      • JPA는 트랜잭션이 커밋되는 시점에 변경된 엔티티 객체가 있는지 확인하고 변경된 엔티티가 있으면 UPDATE SQL을 실행한다.
    • findById()
      • em.find() : JPA에서 엔티티 객체를 PK 기준으로 조회할 때 find()를 사용한다.
        • 파라미터로 조회 타입과 PK 값을 주면 조회 SQL을 만들어서 실행하고 결과를 객체로 변환해준다.
    • findAll()
      • JPQL (Java Persistence Query Language) : JPA에서 제공하는 객체지향 쿼리 언어로 주로 여러 데이터를 복잡한 조건으로 조회할 때 사용한다.
        • SQL이 테이블을 대상이라면 JPQL은 엔티티 객체를 대상으로 SQL을 실행한다.
        • 엔티티 객체를 대상으로 하기에 JPQL문에서 from 다음 엔티티 객체이름이 들어간다.
          • 엔티티 객체와 속성의 대소문자는 구분해야한다.
        • JPQL에서 파라미터는 :필드명 형태로 나타낸다.
          • 파라미터 바인딩은 setParameter("필드명", 값)를 사용한다.
      • JPA를 사용해도 동적 쿼리가 복잡하기에 Querydsl을 함께 사용하면 좋다.

JPA 적용 - 예외 변환

  • EntityManager는 순수한 JPA 기술로 스프링과는 관계가 없기에 예외 발생 시 JPA 관련 예외를 발생시킨다.
    •  JPA는 PersistenceException과 하위 예외를 발생시키며 추가로 IllegalStateException과 IllegalArgumentException을 발생시킬 수 있다.
  • JPA 예외를 스프링 예외 추상화(DataAccessException)로 변환하는 방법
    • @Repository를 통한 예외 변환

 

  • @Repository 기능
    • @Repository가 붙은 클래스는 컴포넌트 스캔의 대상이 된다.
    • @Repository가 붙은 클래스는 예외 변환 AOP의 적용 대상이 된다.
      • 스프링과 JPA를 함께 사용하는 경우 스프링은 JPA 예외 변환기(PersistenceExceptionTranslator)를 등록한다.
      • 예외 변환 AOP 프록시는 JPA관련 예외가 발생하면 JPA 예외 변환기를 통해 발생한 예외를 스프링 데이터 접근 예외로 변환한다.


정리

  • JPA (Java Persistence API) : 자바에서 제공하는 표준 ORM 데이터 접근 기술이다.
    • 기본적이 SQL을 JPA가 작성하고 처리해준다.
  • ORM (Object Relational Mapping) : 객체 관계 매핑으로 객체는 객체대로 설계하고 관계형 DB는 관계형 DB대로 설계를 해서 ORM이 중간에서 매핑을 해준다.

 

  • JPA에서 프록시 기술을 사용할 때 필요하므로 public / protected 기본 생성자는 필수이다.
  • JPA의 모든 데이터 변경 (등록, 수정, 삭제)은 트랜잭션 안에서 이뤄져야 하기 때문에 @Transaction이 필요하다.

 

  • JPQL (Java Persistence Query Language) : JPA에서 제공하는 객체지향 쿼리 언어로 주로 여러 데이터를 복잡한 조건으로 조회할 때 사용한다.
    • SQL이 테이블을 대상이라면 JPQL은 엔티티 객체를 대상으로 SQL을 실행한다.
    • 엔티티 객체를 대상으로 하기에 JPQL문에서 from 다음 엔티티 객체이름이 들어간다.
      • 엔티티 객체와 속성의 대소문자는 구분해야한다.
    • JPQL에서 파라미터는 :필드명 형태로 나타낸다.
      • 파라미터 바인딩은 setParameter("필드명", 값)를 사용한다.

 

  • EntityManager는 순수한 JPA 기술로 스프링과는 관계가 없기에 예외 발생 시 JPA 관련 예외를 발생시킨다.
    • @Repository를 사용하면 JPA 예외 변환기를 통해 JPA 예외를 스프링 데이터 접근 예외로 변환해준다.

출처 : [인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술]

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard

 

스프링 DB 2편 - 데이터 접근 활용 기술 강의 | 김영한 - 인프런

김영한 | 백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔드

www.inflearn.com