3

스프링 데이터 저장소에서 페이징을 사용하여 @NamedQuery를 실행하려고 할 때 문제가 있다고 생각했습니다. 엔티티 클래스는 다음과 같습니다스프링 데이터 JPA : 페이지 가능 쿼리 롤백 트랜잭션

@NamedQueries({ 
@NamedQuery(
     name = "Customer.findByNamePattern", 
     query = "select c from Customer c where c.name like :pattern" 
    )  
}) 
@Entity 
public class Customer { 
    @Id 
    @GeneratedValue(strategy = GenerationType.TABLE) 
    private Long id;  
    private String name; 

저장소 인터페이스입니다 : '비 트랜잭션 컨텍스트 (JUnit을)에서 페이징 저장소 메소드를 호출 할 때

public interface CustomerRepository extends JpaRepository<Customer, Long> {  
    //@Query("select c from Customer c where c.name like :pattern") 
    Page<Customer> findByNamePattern(@Param("pattern") String pattern,Pageable pageable); 
} 

, 그것은 잘 작동합니다. findAllPaged() 다시 잘 작동 호출

@Service("customerService") 
@Transactional 
public class CustomerServiceImpl implements CustomerService { 
    private static Logger log = Logger.getLogger(CustomerServiceImpl.class.getName()); 
    @Autowired 
    private CustomerRepository customerRepository; 

    @Transactional(readOnly = true) 
    public Page<Customer> findAllPaged(int pageNum, int pageSize) {  
     PageRequest pr = new PageRequest(pageNum,pageSize); 
     return customerRepository.findAll(pr);  
    } 

    @Transactional(readOnly = true) 
    public Page<Customer> findByNamePatternPaged(String keyword, int pageNum, int pageSize) {  
     PageRequest pr = new PageRequest(pageNum,pageSize); 
     String pattern = "%"+keyword+"%"; 
     return customerRepository.findByNamePattern(pattern, pr);  
    } 

... : 내가 좋아하는, 트랜잭션 서비스 메서드에서 호출

. 트랜잭션 컨텍스트에서 다시 잘 작동 저장소 방법에 org.springframework.data.jpa.repository.Query 주석을 사용

javax.persistence.RollbackException 
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction;  nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly 
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:524) 
at  org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) 
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272) 
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 
at com.sun.proxy.$Proxy35.findByNamePatternPaged(Unknown Source) 
at datapagedquery.service.TestCustomerService.testFindByPatternPaged(TestCustomerService.java:36) 
... 
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly 
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:74) 
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:515) 
... 33 more 

:

그러나 나는 항상 예외가 명명 된 쿼리를 사용하는 메소드를 호출 할 때. 디버깅 잠시 후

, 문제가 doCreateCountQuery()에, org.springframework.data.jpa.repository.query.NamedQuery에 의한 것으로 보인다 및 hasNamedQuery() :

@Override 
protected TypedQuery<Long> doCreateCountQuery(Object[] values) { 

    EntityManager em = getEntityManager(); 
    TypedQuery<Long> countQuery = null; 

    if (hasNamedQuery(em, countQueryName)) { 
     countQuery = em.createNamedQuery(countQueryName, Long.class); 
    } else { 
     Query query = createQuery(values); 
     String queryString = extractor.extractQueryString(query); 
     countQuery = em.createQuery(QueryUtils.createCountQueryFor(queryString, countProjection), Long.class); 
    } 

    return createBinder(values).bind(countQuery); 
} 
private static boolean hasNamedQuery(EntityManager em, String queryName) { 

    try { 
     em.createNamedQuery(queryName); 
     return true; 
    } catch (IllegalArgumentException e) { 
     LOG.debug("Did not find named query {}", queryName); 
     return false; 
    } 
} 

그것은 존재하지 않는 생성 된 이름 Customer.findByNamePattern.count,에서 TypedQuery을 만들려고 EntityManager의 명명 된 쿼리 저장소. hasNamedQuery()은 그것을 확인하고, IllegalArgumentException, 을 던집니다. 다른 방법으로 만듭니다. 문제는 IllegalArgumentException이 걸려 있지만, 트랜잭션이 롤백된다는 점이다

가 I는 다음과 같은 대안을 발견 (때로는!) :

  1. 저장소 방법

  2. org.springframework.data.jpa.repository.Query 주석을 사용하여 또는 - 다른 명명 된 쿼리

    @NamedQuery(
        name = "Customer.findByNamePattern.count", 
        query = "select count(c.id) from Customer c where c.name like :pattern" 
    ), 
    
,691,363을 만드는 나를 위해 분명하지 않다 (210)

는 : findAll()를 호출

  • 같은 문제를 야기한다, 그러나 그것은 나던. 왜?
  • @NamedQuery 대신 org.springframework.data.jpa.repository.Query을 사용하면 문제가 발생하지 않는 이유는 무엇입니까?
  • 트랜 잭 셔널 컨텍스트에서 @NamedQuery를 사용하여 페이지 질의 옵션을 사용하면 문제를 피할 수 있으며 (명시 적으로 개수 쿼리를 만들지 않음) 어떻게 사용할 수 있습니까?

도움을 주시면 감사하겠습니다.

UPDATE

버전 사용 하였다 : 스프링 : 4.0.5.RELEASE 스프링 데이터 : 1.6.0.RELEASE, 1.7.0.RELEASE 하이버 네이트 : 4.3.5.Final

에게

[https://jira.spring.io/browse/DATAJPA-442]]에서 비슷한 버그를 읽은 후 최대 절전 모드 버전을 4.2.15. 최종으로 다운 그레이드하여 문제를 해결했습니다. 그러나 질문은 여전히 ​​살아 있습니다. Hibernate 버전을 변경하지 않고 문제를 해결할 수 있습니까?

답변

1

잠재적 인 수정 사항이있는 PR을 추가했습니다. https://github.com/spring-projects/spring-data-jpa/pull/110 원래의 EntityManager가 실패한 조회의 영향을받지 않도록 명명 된 쿼리 조회를 수행하기 위해 새로운 (폐기 된) EntityManager를 사용합니다. 귀하의 문제는 재현하기가 다소 어려웠습니다. 스핀을 주시겠습니까? 아마도이 경우 작은 테스트 케이스를 제공 할 수 있습니까?

+0

토마스, 도와 주셔서 감사합니다. https://github.com/sztgeza/springdata-pageable-query에서 작은 데모를 업로드했습니다. – demura

+0

테스트 케이스를 제공해 주셔서 감사합니다. DATAJPA-617의 변경으로 문제가 해결 된 것 같습니다. 나는 Spring Boot와 JPA 버전의 작은 예제를 FIX로 만들었습니다 : https://github.com/thomasdarimont/spring-data-bugs/tree/master/DATAJPA-617 –

+0

Thomas, 고마워요. 그것은 지금 작동합니다. 공식 릴리스 (및 언제)에서 수정 프로그램을 사용할 수 있습니까? Tx. 다시! – demura

1

여러 유물에 의해 구동에 실행 문제 : 예외를 throw 후 JPA EntityManager는이 정의에

폐쇄 (잠재적으로 재현)한다. 이것은 대개 엔티티 작업이 실패 할 경우를 대비하여 수행되며 EntityManager 상태에 대해 확신 할 수 있습니다. 간단한 명명 쿼리 조회의 경우 새로운 EntityManager이 작성 될 필요가 없으므로 이것은 매우 엄격합니다. 그러나 우리는 그것을 처리 할 필요가 있습니다.

즉, 수동으로 정의한 쿼리에 대해이 문제를 해결하기 위해 (이미 @Query에서 작동하는 것으로 나타났습니다). 그러나 DATAJPA-350에 대해 도입 한 방어 메커니즘은 명명 된 쿼리 부분에는 적용되지 않습니다. 나는 DATAJPA-617을 만들었습니다.

+0

답변 해 주셔서 감사합니다. – demura