복붙노트

[SPRING] 스프링 부트 데이터 JPA - 업데이트 쿼리 수정 - 지속성 컨텍스트 새로 고침

SPRING

스프링 부트 데이터 JPA - 업데이트 쿼리 수정 - 지속성 컨텍스트 새로 고침

저는 Spring Boot 1.3.0.M4와 MySQL 데이터베이스를 가지고 일하고 있습니다.

수정 쿼리를 사용할 때 문제가 발생합니다. EntityManager에는 쿼리가 실행 된 후에 오래된 엔티티가 포함됩니다.

public interface EmailRepository extends JpaRepository<Email, Long> {

    @Transactional
    @Modifying
    @Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()")
    Integer deactivateByExpired();

}

DB에 Email [id = 1, active = true, expire = 2015 / 01 / 01]이 있다고 가정합니다.

실행 후 :

emailRepository.save(email);
emailRepository.deactivateByExpired();
System.out.println(emailRepository.findOne(1L).isActive()); // prints true!! it should print false
public interface EmailRepository extends JpaRepository<Email, Long> {

    @Transactional
    @Modifying(clearAutomatically = true)
    @Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()")
    Integer deactivateByExpired();

}

이 접근 방식은 영속 컨텍스트를 지우고 오래된 값을 갖지 않지만, 아직 플러시되지 않은 모든 변경 사항을 EntityManager에서 보류합니다. saveAndFlush ()가 아닌 save () 메소드 만 사용하므로 일부 엔티티에 대한 변경 사항이 손실됩니다.

public interface EmailRepository extends JpaRepository<Email, Long>, EmailRepositoryCustom {

}

public interface EmailRepositoryCustom {

    Integer deactivateByExpired();

}

public class EmailRepositoryImpl implements EmailRepositoryCustom {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    @Override
    public Integer deactivateByExpired() {
        String hsql = "update Email e set e.active = false where e.active = true and e.expire <= NOW()";
        Query query = entityManager.createQuery(hsql);
        entityManager.flush();
        Integer result = query.executeUpdate();
        entityManager.clear();
        return result;
    }

}

이 접근법은 @Modifying (clearAutomatically = true)와 비슷하게 작동하지만 먼저 업데이트를 실행하기 전에 EntityManager가 DB에 대한 모든 변경 사항을 플러시 한 다음 지속성 컨텍스트를 지 웁니다. 이렇게하면 오래된 엔티티는 없으며 모든 변경 사항은 DB에 저장됩니다.

JPA에서 업데이트 된 명령문을 실행하는 더 좋은 방법이 있는지 알고 싶습니다. 오래된 엔티티 문제가없고 DB에 대한 수동 플러시가 없어도됩니다. 아마도 2 차 수준 캐시를 비활성화할까요? Spring Boot에서 어떻게 할 수 있습니까?

2018 년 업데이트

스프링 데이터 JPA가 내 PR을 승인했다. @Modifying ()에 flushAutomatically 옵션이있다.

@Modifying(flushAutomatically = true, clearAutomatically = true)

해결법

  1. ==============================

    1.귀하의 질문에 대한 직접적인 답변이 아니라는 것을 알고 있습니다. 이미 수정본을 작성하고 Github에 대한 풀 요청을 시작했기 때문입니다. 고마워요!

    귀하의 질문에 대한 직접적인 답변이 아니라는 것을 알고 있습니다. 이미 수정본을 작성하고 Github에 대한 풀 요청을 시작했기 때문입니다. 고마워요!

    그러나 JPA 방식에 대해 설명하고 싶습니다. 따라서 특정 기준과 일치하는 모든 엔티티를 변경하고 각각에 대한 값을 업데이트하려고합니다. 일반적인 방법은 모든 필요한 엔티티를로드하는 것입니다.

    @Query("SELECT * FROM Email e where e.active = true and e.expire <= NOW()")
    List<Email> findExpired();
    

    그런 다음 반복하여 값을 업데이트하십시오.

    for (Email email : findExpired()) {
      email.setActive(false);
    }
    

    이제 최대 절전 모드는 모든 변경 사항을 알고 트랜잭션이 완료되거나 EntityManager.flush ()를 수동으로 호출하면 데이터베이스에 기록합니다. 모든 엔티티를 메모리에로드하기 때문에 많은 양의 데이터 항목이 있으면이 작업이 제대로 작동하지 않습니다. 하지만 이것은 최대 절전 모드 엔티티 캐시, 2 차 레벨 캐시 및 데이터베이스를 동기화 상태로 유지하는 가장 좋은 방법입니다.

    이 대답은 "@ Modifying"주석은 쓸모가 없다고 말합니까? 아니! 수정 된 항목이 로컬 캐시에 없는지 확인하는 경우 (예 : 쓰기 전용 응용 프로그램을 사용하는 경우이 방법이 적합합니다.

    그리고 단지 기록을 위해 : 당신은 @Transactional이 당신의 저장소 방법에 필요하지 않습니다.

    레코드 v2의 경우 : 활성 열은 그것이 만료 될 직접적인 의존성을 가지고있는 것처럼 보입니다. 그렇다면 활성을 완전히 삭제하지 않고 모든 쿼리에서 유효 기간 만 지켜 보는 것이 어떻습니까?

  2. ==============================

    2.klaus-groenbaek이 말했듯이 EntityManager를 삽입하고 새로 고침 메서드를 사용할 수 있습니다.

    klaus-groenbaek이 말했듯이 EntityManager를 삽입하고 새로 고침 메서드를 사용할 수 있습니다.

    @Inject
    EntityManager entityManager;
    
    ...
    
    emailRepository.save(email);
    emailRepository.deactivateByExpired();
    Email email2 = emailRepository.findOne(1L);
    entityManager.refresh(email2);
    System.out.println(email2.isActive()); // prints false
    
  3. from https://stackoverflow.com/questions/32258857/spring-boot-data-jpa-modifying-update-query-refresh-persistence-context by cc-by-sa and MIT license