본문 바로가기

학습 기록 (Learning Logs)/CS Study

JPA-troubleShooting

 

 


공통 질문

트러블슈팅 및 실무

  1. JPA 사용 시 발생할 수 있는 LazyInitializationException의 원인과 해결 방법은 무엇인가요?
  2. 영속성 컨텍스트와 데이터베이스의 동기화 타이밍은 언제 이루어지나요?
  3. EntityManager와 Spring Data JPA의 Repository 간의 차이점은 무엇인가요?
  4. @Transactional의 동작 원리와 JPA에서의 역할은 무엇인가요?
  5. 실무에서 JPA를 사용할 때 주의해야 할 점과 흔히 발생하는 실수는 무엇인가요?

 

  1. JPA 사용할 Native Query 사용하는 이유와 주의점은 무엇인가요?
  2. JPA 대규모 데이터를 처리할 고려해야 점은 무엇인가요?
  3. 읽기 전용 트랜잭션(ReadOnly Transaction)에서 JPA 어떻게 동작하는지 설명해주세요.
  4. JPA Auditing 기능(@CreatedDate, @LastModifiedDate ) 원리와 사용 사례를 설명해주세요.
  5. JPA에서 다중 데이터베이스(Multi-Database) 설정하려면 어떻게 해야 하나요?

 

 


 

  1. JPA 사용 시 발생할 수 있는 LazyInitializationException의 원인과 해결 방법은 무엇인가요?

원인

  • FetchType.LAZY로 설정된 연관 엔티티를 **영속성 컨텍스트(EntityManager)**가 종료된 이후에 접근하려고 할 때 발생합니다.
  • 예: @ManyToOne(fetch = FetchType.LAZY)로 설정된 필드에 데이터가 로드되지 않았는데, 트랜잭션 밖에서 접근하려 하면 예외 발생.

 

해결 방법

Fetch Join 사용: JPQL이나 QueryDSL에서 JOIN FETCH로 필요한 연관 데이터를 한 번에 가져옵니다.

 
@Query("SELECT p FROM Product p JOIN FETCH p.category WHERE p.id = :id")
Product findProductWithCategory(@Param("id") Long id);

 

 

@Transactional로 트랜잭션 유지: Lazy 로딩이 필요한 부분에서 트랜잭션을 유지합니다.

@Transactional
public Product findProduct(Long id) {
    Product product = productRepository.findById(id).orElseThrow();
    product.getCategory().getName(); // Lazy 로딩 처리
    return product;
}

 

 

EntityGraph 사용: 연관 엔티티를 즉시 로딩하도록 지정합니다.

@EntityGraph(attributePaths = {"category"})
Product findById(Long id);

 

DTO Projection

  • 필요한 데이터만 조회하도록 설계합니다.
@Query("SELECT new com.example.dto.ProductDto(p.name, s.stockQuantity) " +
       "FROM Product p JOIN p.stock s WHERE p.id = :id")
ProductDto findProductDtoById(@Param("id") Long id);

@Query("SELECT new com.example.dto.ProductDto(p.name, c.name) 
FROM Product p JOIN p.category c WHERE p.id = :id")
ProductDto findProductDto(@Param("id") Long id);

 

Hibernate.initialize() 사용

  • 강제로 초기화합니다.
Product product = productRepository.findById(id).orElseThrow();
Hibernate.initialize(product.getStock());

 

 

 


2. 영속성 컨텍스트와 데이터베이스의 동기화 타이밍은 언제 이루어지나요?

 

  • em.flush() 호출 시: 영속성 컨텍스트의 변경 사항이 데이터베이스에 반영됩니다.
  • 트랜잭션 커밋 시: flush()가 자동으로 호출됩니다.
  • JPQL 쿼리 실행 시: JPQL 실행 전에 flush()가 호출되어 변경 내용을 반영합니다.

flush 동작

  1. 변경된 엔티티 감지(Dirty Checking).
  2. 변경 내용을 SQL로 변환.
  3. SQL을 데이터베이스에 전달.

 

 


3. EntityManager와 Spring Data JPA의 Repository 간의 차이점은 무엇인가요?

 

 

 


4. @Transactional의 동작 원리와 JPA에서의 역할은 무엇인가요?

동작 원리

  • Spring의 AOP(Aspect-Oriented Programming)를 사용해 메서드 실행 전후에 트랜잭션을 관리합니다.
  • 프록시 객체를 생성하여 트랜잭션 시작 및 종료 로직을 삽입합니다.
  • 메서드가 성공적으로 완료되면 커밋, 예외가 발생하면 롤백합니다.

JPA에서의 역할

 

  • 트랜잭션이 시작될 때 영속성 컨텍스트를 초기화합니다.
  • 트랜잭션 커밋 시 영속성 컨텍스트의 변경 사항을 데이터베이스에 반영합니다.
  • 지연 로딩을 지원합니다. 트랜잭션 범위 내에서만 Lazy Loading이 동작합니다.
  • 트랜잭션 범위 내에서 영속성 컨텍스트 관리.
  • 트랜잭션 종료 시 플러시(Flush)와 커밋 수행.
  • 예외 발생 시 롤백 처리.

5. 실무에서 JPA를 사용할 때 주의해야 할 점과 흔히 발생하는 실수는 무엇인가요?

N+1 문제: 연관 엔티티를 Lazy 로딩으로 설정하고 반복문에서 접근 시 발생.

  • 해결: Fetch Join, EntityGraph 사용.

LazyInitializationException

  • 트랜잭션 범위에서 데이터를 처리하거나, 필요한 데이터를 명시적으로 로딩합니다.

영속성 컨텍스트 누수:

  • @Transactional 범위가 길 경우 메모리 누수 가능.

 

대규모 데이터 처리

  • 페이징 처리, 대량 삭제/업데이트 시 JPQL 또는 Native Query를 사용합니다.

성능 최적화 부족:

  • 대량 데이터 삽입 시 Batch Insert/Update 사용.
  • hibernate.default_batch_fetch_size 설정으로 지연 로딩 최적화.

성능 문제

  • 쿼리 성능 분석 및 캐싱 전략(EhCache, Redis 등) 활용.

DTO와 엔티티 혼용:

  • 비즈니스 로직에 엔티티 직접 사용은 지양. DTO 사용 권장.

필드 접근 문제:

  • 무조건 Getter/Setter 사용. 필드 직접 접근 금지.

 


1. Native Query를 사용하는 이유와 주의점

사용 이유

  1. 복잡한 SQL 쿼리 작성이 필요할 때.
  2. 성능 최적화가 필요할 때.
  3. JPA에서 지원하지 않는 특정 데이터베이스 기능을 사용할 때.

주의점

  1. 데이터베이스 종속성 증가: 특정 DB에 종속될 가능성.
  2. 조회 결과 매핑: 엔티티 매핑이 어렵거나 복잡할 수 있음.
  3. SQL Injection 방지를 위해 반드시 파라미터 바인딩을 사용해야 함.
  4. JPQL과 통합 어려움: JPQL에서 제공하는 객체지향적 기능 사용 불가.

 


7. JPA로 대규모 데이터를 처리할 때 고려해야 할 점은 무엇인가요?

 

페이징 처리

  • Pageable을 사용하거나, LIMIT/OFFSET을 활용합니다.

배치 처리

  • Hibernate의 Batch Insert/Update를 활용합니다.
spring.jpa.properties.hibernate.jdbc.batch_size=30
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

메모리 관리

  • em.clear()를 주기적으로 호출하여 영속성 컨텍스트를 비웁니다.

Streaming:

  • 대량 데이터를 한 번에 조회하지 않고 스트림 형태로 처리.
@Query("SELECT p FROM Product p")
Stream<Product> findAllByStream();

 

 

 


 

8. 읽기 전용 트랜잭션(ReadOnly Transaction)에서 JPA가 어떻게 동작하는지 설명해주세요.

 

@Transactional(readOnly = true):

  • 데이터베이스에서 쓰기 작업을 막습니다.
  • Hibernate는 영속성 컨텍스트를 초기화하지만, 변경 감지(Dirty Checking)를 비활성화하여 성능을 향상시킵니다.

특징

  • 읽기 작업만 수행하며, 변경 감지(Dirty Checking) 비활성화.
  • 성능 최적화: 영속성 컨텍스트가 최소한으로 동작.
@Transactional(readOnly = true)
public List<Product> findProducts() {
    return productRepository.findAll();
}

 


9. JPA Auditing 기능(@CreatedDate, @LastModifiedDate 등)의 원리와 사용 사례를 설명해주세요.

 

 

  • Spring Data JPA의 Auditing 기능을 통해 엔티티 생성 및 수정 시점에 자동으로 날짜를 기록합니다.
  • @CreatedDate, @LastModifiedDate 등을 사용하여 엔티티의 생명주기 이벤트에 따라 데이터를 설정합니다.
  • Spring Data JPA의 AuditingEntityListener가 엔티티의 생성/수정 시점을 자동으로 기록.
  • Hibernate의 라이프사이클 이벤트를 활용.
@EntityListeners(AuditingEntityListener.class)
@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

 

 

 

 

 

 

 


 

10. JPA에서 다중 데이터베이스(Multi-Database)를 설정하려면 어떻게 해야 하나요?

각 데이터베이스에 대한 DataSource 설정

 
@Bean
@Primary
@ConfigurationProperties("app.datasource.primary")
public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties("app.datasource.secondary")
public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
}

 

EntityManagerFactory 설정, EntityManager 설정

@Bean
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
    return builder
        .dataSource(primaryDataSource())
        .packages("com.example.primary")
        .persistenceUnit("primary")
        .build();
}

@Bean
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
    return builder
        .dataSource(secondaryDataSource())
        .packages("com.example.secondary")
        .persistenceUnit("secondary")
        .build();
}

@Primary
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManager(
    EntityManagerFactoryBuilder builder,
    @Qualifier("primaryDataSource") DataSource dataSource) {
    return builder.dataSource(dataSource).packages("com.example.primary").build();
}

 

트랜잭션 설정

@Primary
@Bean
public PlatformTransactionManager primaryTransactionManager(EntityManagerFactory primaryEntityManagerFactory) {
    return new JpaTransactionManager(primaryEntityManagerFactory);
}

@Bean
public PlatformTransactionManager secondaryTransactionManager(EntityManagerFactory secondaryEntityManagerFactory) {
    return new JpaTransactionManager(secondaryEntityManagerFactory);
}

 

Repository 설정:

 
@EnableJpaRepositories(
    basePackages = "com.example.primary",
    entityManagerFactoryRef = "primaryEntityManagerFactory"
)
public class PrimaryConfig {}

 

 

 

 

 

 


 

 

 

 

 


 

'학습 기록 (Learning Logs) > CS Study' 카테고리의 다른 글

[Note]In-memory cache-basic  (0) 2024.12.16
Fetch Join  (0) 2024.12.12
JPA-advanced-question  (0) 2024.12.12
JPA-optimized-performance  (0) 2024.12.12
JPA-features  (0) 2024.12.12