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 기본 생성자는 필수이다.
- @Entity : JPA가 사용하는 객체라는 의미로 JPA가 인식할 수 있도록 하는 어노테이션이다.
@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을 만들어서 실행하고 결과를 객체로 변환해준다.
- em.find() : JPA에서 엔티티 객체를 PK 기준으로 조회할 때 find()를 사용한다.
- findAll()
- JPQL (Java Persistence Query Language) : JPA에서 제공하는 객체지향 쿼리 언어로 주로 여러 데이터를 복잡한 조건으로 조회할 때 사용한다.
- SQL이 테이블을 대상이라면 JPQL은 엔티티 객체를 대상으로 SQL을 실행한다.
- 엔티티 객체를 대상으로 하기에 JPQL문에서 from 다음 엔티티 객체이름이 들어간다.
- 엔티티 객체와 속성의 대소문자는 구분해야한다.
- JPQL에서 파라미터는 :필드명 형태로 나타낸다.
- 파라미터 바인딩은 setParameter("필드명", 값)를 사용한다.
- JPA를 사용해도 동적 쿼리가 복잡하기에 Querydsl을 함께 사용하면 좋다.
- JPQL (Java Persistence Query Language) : JPA에서 제공하는 객체지향 쿼리 언어로 주로 여러 데이터를 복잡한 조건으로 조회할 때 사용한다.
- JPA의 모든 동작은 엔티티 매니저를 통해서 이뤄진다.
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