슈퍼코딩/주특기(JAVA)

2024.06.04(화) 슈퍼코딩 신입연수원 10주차 Day 2 후기 - JPA, PSA

곰돌이볼 2024. 6. 4. 12:53
  • 강의
    • 102강(스프링 부트와 JPA v1) ~ 103강* 스프링 부트와 JPA v2)

JPA


  • 기본 SQL 사용 구문의 불편한 점
    • rowMapper 사용, 비슷한 SQL문 직접 작성
    • 발생 원인
      • SQL → 데이터 지향, 관계 지향, 선언적
      • Java → 객체 지향적, 행위 지향 패러다임
    • 해결방법 : ORM

ORM

  • Object-Relation-Mapping
  • 객체지향 언어와 RDB 변환을 자동으로 처리하는 기술
  • 기존 entity의 간접 매핑 → "테이블 영속화" 변경
  • 기술 적용
    • Python → django ORM, SQLAlchemy
    • Node.js → Sequelize.js
    • Java → JPA

JPA

  • Java Persistence API(JPA) → Java ORM 규약
  • 장점
    • 객체 지향 실현 가능
    • 재사용 용이
    • 코드 생산성 향상
  • 단점
    • 복잡한 요구에 대한 JPA 한계
    • 초반/고급 러닝커브 ↑
  • 내부 동작 원리
    • JPA는 내부에 JDBC 사용
  • 구성요소
    • Entity Transaction
    • Entity Manager
    • Query
    • Persistence
    • Entity Manager Factory
  • 구현체
    • Hibernate
    • EclipsLink
    • OpenJPA

Hibernate

  • 장점
    • 가장 널리 사용됨
    • 완벽한 JPA 지원
    • 다양한 성능 최적화 기능 제공
  • 설정
    • gradle 의존성 추가
      • implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    • application.yml 로그 설정
      • logging:
           config: classpath:logback-spring-local.xml
           level:
              org:
                  hibernate:
                      SQL: DEBUG
    • JPA 관련 bean 등록(entity manager, transaction manager)
package com.github.supercodingspring.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableJpaRepositories(
        basePackages = {"com.github.supercodingspring.repository.items", "com.github.supercodingspring.repository.storeSales"},
        entityManagerFactoryRef = "entityManagerFactoryBean1",
        transactionManagerRef = "tmJpa1"
)
public class JpaConfig1 {

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean1(@Qualifier("dataSource1")DataSource dataSource){
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.github.supercodingspring.repository.items", "com.github.supercodingspring.repository.storeSales");

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);

        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        properties.put("hibernate.format_sql", "true");
        properties.put("hibernate.use_sql_comment", "true");

        em.setJpaPropertyMap(properties);
        return em;
    }

    @Bean(name = "tmJpa1")
    public PlatformTransactionManager transactionManager1(@Qualifier("dataSource1") DataSource dataSource){
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean1(dataSource).getObject());
        return transactionManager;
    }

}

 

  • JPA Entity 만들기
    • @Entity
    • @Table(name = "실제 DB 테이블명") : 사용하지 않으면 클래스명을 entity 이름으로 함
    • @Column : 컬럼
  • JPA Repository 기본 메서드 사용하기
    • extends JpaRepository<entity, id_type>
    • 기초 CRD 제공
      • save, findById, delete 등
    • entity 업데이트 : 더티 체킹
    • 자동 쿼리 메서드
    • pagination 구현
// 페이지네이션

// request
http://localhost:8080/api/items-page?size=10&page=0

// controller
@ApiOperation("pagination 지원")
@GetMapping("/items-page")
public Page<Item> findItemsPagination(Pageable pageable){
    return electronicStoreItemService.findAllWithPageable(pageable);
}

// service
public Page<Item> findAllWithPageable(Pageable pageable) {
    Page<ItemEntity> itemEntities = electronicStoreItemJpaRepository.findAll(pageable);
    return itemEntities.map(ItemMapper.INSTANCE::itemEntityToItem);
}

// repository
@Repository
public interface ElectronicStoreItemJpaRepository extends JpaRepository<ItemEntity, Integer> {

    List<ItemEntity> findItemEntitiesByTypeIn(List<String> types);

    List<ItemEntity> findItemEntitiesByPriceLessThanEqualOrderByPriceAsc(Integer maxValue);

    Page<ItemEntity> findAllByTypeIn(List<String> types, Pageable pageable);

}

 

관계 매핑

  • 고려사항
    • 관계의 다중성
    • 관계의 방향
    • 관계의 주인
  • 단방향
    • 1:N 관계
      • 1) N의 객체에 @ManyToOne, @JoinColumn을 선언하고, 1인 객체에 필드 X
      • 2) 1의 객체에 @OneToMany, @JoinColumn 을 선언하고, N인 객체에 필드 X
    • 1:1 관계
      • @OneToOne , @JoinColumn
    • N:M 관계
      • 매핑하는 연결 테이블 생성
  • 다방향
    • @ManyToOne, @JoinColumn
    • @OneToMany
  • 연관 관계 객체 가져오기
    • Lazy(Fetch.Lazy) → 권장
      • 2번의 select문을 통해서 가져옴
      • 실제로 연관객체를 사용할 때 가져옴(실제로 필요할 때 사용)
    • Eager(Fetch.Eager)
      • Join문을 이용해서 가져옴 → 객체 조회 시 연관객체까지 가져옴

JPQL

  • Java Persistance Query Language → JPA의 객체지향 쿼리
  • @Query 이용

대표적인 JPA 문제 : N+1 문제

  • 연관 관계 설정된 Entity 조회 시, 조회된 갯수 만큼 조회 쿼리가 추가로 실행되는 현상
  • 해결방법
    • 1) Fetch.Eager 사용 → 항상 해결되는 것은 아님
    • 2) JPQL 이용 : Fetch JOIN 이용


PSA


  • Portable service Abstraction, 휴대용 서비스 추상화
  • 특징
    • 특정 기술에 접근하지 않고, 추상화를 통해서 코드 이식성, 유연성 ↑
  • 이유(+예시)
    • DB마다 문법이 다름
    • JPA가 DB를 바꿀 경우, 코드의 변경이 필요함
    • => JPQL Dialect가 DB에 따라서 기술 변경
  • 특정 기술에 얽매이지 않는 코드 철학