JPA 관련 기술 파트 요약 및 의문점

2022. 6. 23. 21:13·개발/Spring DB
반응형
본 게시글은 인프런 김영한 선생님 강의 스프링 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

Spring Data Jpa와 Jpa는 이전에 한번 학습했으므로 가볍게 중요 내용을 요약하겠으며, Querydsl을 좀 더 중점적으로 다루겠습니다.
물론 이번 스프링 DB 2편에서는 가볍게 경험하는 정도이기에 나중에 더 자세한 포스팅을 올리겠습니다!

JPA

  • implementation 'org.springframework.boot:spring-boot-starter-data-jpa'을 build.gradle에 추가한다.
    이러면 Spring Data Jpa와 Jpa 설정이 완료된다.
  • spring-boot-starter-data-jpa 는 spring-boot-starter-jdbc 도 함께 포함(의존)한다.
    따라서 해당 라이브러리 의존관계를 제거해도 된다.
  • Jpa는 public 또는 protected 기본 생성자가 필수다. 동적 프록시 기술 때문인 것 같다.
  • Repository를 작성했는데 생성자를 보면 스프링을통해 엔티티 매니저가 주입받는 것을 알 수 있다.
    JPA의 모든 동작은 엔티티 매니저를 통해 이루어지며, 엔티티 매니저는 내부에 데이터소스를 가지고 있고, DB에 접근할 수 있다.
  • JPA의 모든 데이터 변경(등록, 수정, 삭제)은 트랜잭션 안에서 이루어져야 한다. 조회는 트랜잭션이 없어도 가능하다.
  • JPA를 설정하려면 EntityManagerFactory , JPA 트랜잭션 매니저( JpaTransactionManager ), 데이터소스 등등
    다양한 설정을 해야 한다
    . 스프링 부트는 이 과정을 모두 자동화 해준다. 스프링 빈으로 모두 등록해준다.
    결국 엔티티 매니저도 자동으로 스프링 빈으로 등록된다.
  • JPA는 JPQL이라는 객체지향 쿼리 언어를 사용한다.

예외 변환

  • JPA의 경우에는 예외가 발생하면 JPA 에러가 발생한다.
  • EntityManager는 순수한 JPA 기술이고, 스프링과는 관계가 없다.
    따라서 엔티티 매니저는 예외가 발생하면 JPA 예외를 발생시킨다.
  • 그렇다면 JPA 예외를 스프링 예외 추상화로 어떻게 변환할 수 있을까?
  • @Repository를 사용하면 가능하다.
@Repository 사용

@Repository의 기능

  • @Repository 가 붙은 클래스는 컴포넌트 스캔의 대상이 된다.
  • @Repository 가 붙은 클래스는 예외 변환 AOP의 적용 대상이 된다.
  • 스프링과 JPA를 함께 사용하는 경우 스프링은 JPA 예외 변환기 ( PersistenceExceptionTranslator )를 등록한다.
  • 예외 변환 AOP 프록시는 JPA 관련 예외가 발생하면 JPA 예외 변환기를 통해 발생한 예외를 스프링 데이터 접근 예외로 변환한다.
  • 결과적으로 리포지토리에 @Repository 애노테이션만 있으면 스프링이 예외 변환을 처리하는 AOP를 만들어준다.

Spring Data Jpa

  • 동적 프록시 기능을 사용해 스프링 데이터 JPA가 인터페이스만 작성해도 구현체가 자동으로 스프링 빈으로 등록된다.
    별도로 개발자가 @Component, @Repository를 작성할 필요가 없다.
  • 가장 대표적인 기능은 공통 인터페이스 기능과 쿼리 메서드 기능이다.
  • 쿼리 메서드 기능 대신에 직접 JPQL을 사용하고 싶을 때는 @Query 와 함께 JPQL을 작성하면 된다.
    이때는 메서드 이름으로 실행하는 규칙은 무시된다.
  • 스프링 데이터 JPA는 JPQL 뿐만 아니라 JPA의 네이티브 쿼리 기능도 지원하는데, JPQL 대신에 SQL을 직접 작성할 수 있다.
  • 역시 동적 쿼리에 약하다.
  • 실습은 기존에 인터페이스를 사용하기 위해 어댑터 패턴을 사용했다.
  • 결론은 JPA 기초를 잘 알아야 Spring Data JPA도 잘 쓸 수 있다!

Querydsl

Querydsl 설정

dependencies {
    //Querydsl 추가
    implementation 'com.querydsl:querydsl-jpa' 
    annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
 }

//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
clean {
      delete file('src/main/generated')
}
  • 위는 Querydsl 설정을 위해 build.gradle에 작성한 코드다.
  • Querydsl은 인텔리제이가 버전업하거나, Gradle 설정이 버전업하면서 적용 방법이 조금씩 달라진다고 한다.
  • 그래서 설정에 수고로움이 많다고 하는데, querydsl gradle로 검색해서 그때그때 대안을 찾아야한다고 한다.
  • 빌드 툴이 IntelliJ 일때랑 gradle일 때 Q타입 생성 확인 방법이 다르다.

메서드 예시 코드

@Repository
@Transactional
public class JpaAndQuerydslItemRepository implements ItemRepository {

    private final EntityManager em;
    private final JPAQueryFactory query;

    public JpaAndQuerydslItemRepository(EntityManager em) {
        this.em = em;
        this.query = new JPAQueryFactory(em);
    }
    
   //생략

    @Override
    public List<Item> findAll(ItemSearchCond itemSearch) {
        String itemName = itemSearch.getItemName();
        Integer maxPrice = itemSearch.getMaxPrice();

        List<Item> result = query
                .select(item)
                .from(item)
                .where(likeItemName(itemName), maxPrice(maxPrice)) //and 조건으로 인식됨.
                .fetch();

        return result;

    }

    private BooleanExpression likeItemName(String itemName){
        if(StringUtils.hasText(itemName)){
            return item.itemName.like("%" + itemName + "%");
        }
        return null;
    }

    private BooleanExpression maxPrice(Integer maxPrice){
        if (maxPrice != null) {
            return item.price.loe(maxPrice);
        }
        return null;
    }



}
  • Querydsl을 사용하려면 JPAQueryFactory 가 필요하다.
  • JPAQueryFactory 는 JPA 쿼리인 JPQL을 만들기 때문에 EntityManager 가 필요하다.
  • 참고로 JPAQueryFactory 를 스프링 빈으로 등록해서 사용해도 된다.
  • Querydsl 은 별도의 스프링 예외 추상화를 지원하지 않는다. 대신에 JPA에서 학습한 것 처럼 @Repository 에서 스프링 예외 추상화를 처리해준다.
  • 동적 쿼리 작성이 매우 간편해졌다.
  • Querydsl은 결과적으로 JPQL 빌더다. JPQL에 대해 잘 알아야 한다.

데이터 접근 기술 활용 방안

  • 좋은 이야기를 많이 들을 수 있었던 파트다.
  • 어댑터 구조(DI, OCP) VS 동적 프록시로 만들어진 레포지토리에 의존인가에 대한 고민이 있었다.
  • 어댑터 구조는 코드를 설계적으로는 좋지만 코드가 복잡하고, 프록시 객체에 의존은 코드도 간편하고 빠른 개발이 가능하지만 설계적으로 별로다.
  • 여러 오픈 커뮤니티에서도 다양한 의견을 봤는데, 본인은 일단 빠르게 개발하고 필요하다면 리팩토링을 하는 것이 좀 더 나은 방안이라고 생각한다.
  • 마지막 실습은 Spring Data JPA와 Querydsl을 같이 사용했다. 각각의 레포지토리를 만들고 개발을 했다.

JpaTransactionManager의 지원

  • JpaTransactionManager는  DataSourceTransactionManager 가 제공하는 기능도 대부분 제공한다.
  • JPA라는 기술도 결국 내부에서는 DataSource와 JDBC 커넥션을 사용하기 때문이다.
  • 따라서 JdbcTemplate , MyBatis 와 함께 사용할 수 있다.
  • 결과적으로 JpaTransactionManager 를 하나만 스프링 빈에 등록하면, JPA, JdbcTemplate, MyBatis 모두를
    하나의 트랜잭션으로 묶어서 사용할 수 있다.
  • 물론 함께 롤백도 할 수 있다.
  • 나중에 JdbcTemplate와 JPA를 같이 쓰면 플러시라는 기능을 잘 사용해야 한다.
    필자가 느끼기에는 플러시는 커밋과 비슷한 것 같다.

마지막 실습은 필자가 별도로 진행한 실습이다.

스프링 부트가 자동으로 편리하게 많은 빈들을 등록해준다.

우리 애플리케이션이 뜰 때 어떤 빈들이 있는지 궁금해서 아래와 같은 테스트 코드를 작성했다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
class ItemServiceApplicationTests {
	@Autowired
	DefaultListableBeanFactory beanFactory;

	@Test
	@DisplayName("모든 빈 출력하기")
	void findAllBean() {
		String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
		for (String beanDefinitionName : beanDefinitionNames) {
			Object bean = beanFactory.getBean(beanDefinitionName);
			System.out.println("name=" + beanDefinitionName + " object=" + bean);
		}
	}

	@Test
	@DisplayName("애플리케이션 빈 출력하기") void findApplicationBean() {
		String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
		for (String beanDefinitionName : beanDefinitionNames) {
			BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);

			//Role ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
			if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
				Object bean = beanFactory.getBean(beanDefinitionName);
				System.out.println("name=" + beanDefinitionName + " object=" + bean);
			}
		}
	}
}

정말 수 많은 스프링 빈들이 출력됐다.

그동안 배운 트랜잭션 매니저, 데이터소스, 우리가 등록한 서비스와 레포지토리, 엔티티 매니저 팩토리 등이 있었다.

그런데 엔티티 매니저는 왜 안보이는 걸까? 분명 빈으로 등록되야 스프링이 의존 관계를 주입해줄텐데..

EntityManagerCreator, EntityManagerFactory만 보인다.

 

일단 보이는 LocalContainerEntityManagerFactoryBean이 EntityMangerFactory를 생성하는 클래스라고 한다.

그러면 EntityMangerFactory는 요청이 들어올 때마다 엔티티 매니저를 생성한다고 하는데 이 때 엔티티 매니저를 주입해주는 것일까?

아마 엔티티 팩토리가 무슨 마법을 부리는 것이 아닐까??

뭔가 의문이 늘었다..이에 관해서는 나중에 질문이나 자료를 찾아봐서 정리해봐야겠다.

 

 

작성하고 10분 계속 자료를 찾는 중 일단 아래와 같은 질문을 발견했다.

프록시로 가짜 엔티티 매니저를 주입 받는다. 이러면 의문점이 해소되었다.

https://www.inflearn.com/questions/262952

 

EntityManager thread-safe 문제 - 인프런 | 질문 & 답변

안녕하세요 영한님 !! EntityManager 생성시에, final로 주입받으시거나 @PersistenceContext를 사용해서 주입받으시거나 둘 중에 하나로 코드를 작성하시는 것을 보았는데요!! 제가 알기로는 Entitymanager의 t

www.inflearn.com

https://www.inflearn.com/questions/387369

 

엔티티매니저와 영속성컨텍스트에 관해서 - 인프런 | 질문 & 답변

여러 질문 답변을 찾아보고 내용을 종합해서 이해해본 결과  엔티티매니저와 영속성컨텍스트에 관해서 제가 현재 이해하고 있는게 맞는지 확인 부탁드립니다 ..   엔티티 매니저 1. 언제 생성

www.inflearn.com

 

이상으로 포스팅을 마칩니다. 감사합니다.

 

반응형
'개발/Spring DB' 카테고리의 다른 글
  • 스프링 트랜잭션 전파
  • 스프링 트랜잭션
  • MyBatis
  • 예외와 예외 처리
Debin
Debin
공부 기록을 남기며 게시글 리팩토링을 진행하는 블로그입니다.
  • Debin
    리팩토링하는 블로그
    Debin
  • 전체
    오늘
    어제
    • 분류 전체보기
      • DB
        • DB 기초
        • MySQL
        • SQL 튜닝
      • OS
      • Network
      • Git
      • 디지털콘텐츠기획
      • 소프트웨어공학
      • 코딩테스트
        • 프로그래머스
        • 백준
        • 인프런
      • 공부 일지
      • 독서
        • 클린코드
        • 일상 속 사물이 알려주는 웹 API 디자인
        • 토비의 스프링
        • 객체지향의 사실과 오해
        • 자바 잘 읽는 법
      • 기록 및 회고
      • Cloud
        • AWS
      • 개발
        • Java
        • Spring Core
        • Spring MVC
        • Spring DB
        • Spring Boot
        • Spring Security
        • Spring Batch
        • JPA
        • Test
        • Android
      • 대외활동
        • UMC SERVER
        • 카엔프 SW 아카데미
      • 프로젝트
      • Docker
      • Gradle
      • ELK
      • 실무 이야기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

    • 본인 깃허브입니다!
  • 인기 글

  • 태그

    ORM
    spring
    프록시
    docker
    트랜잭션
    spring mvc
    AWS
    데이터베이스
    운영체제
    인덱스
    스프링
    redis
    자바
    mysql
    리눅스
    JPA
    토비의 스프링
    Java
    객체
    container
    SQL
    컨테이너
    spring boot
    AOP
    innodb
    test
    스프링 부트
    도커
    코딩 #개발자 #노마드북클럽 #노개북
    객체지향
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Debin
JPA 관련 기술 파트 요약 및 의문점
상단으로

티스토리툴바