2014-10-16 44 views
3

我試圖用Spring數據存儲庫中的分頁運行@NamedQuery時考慮過一個問題。 實體類看起來是這樣的:Spring Data 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; 

的倉庫接口:

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); 
} 

當我嘗試調用從非事務上下文(JUnit的)分頁存儲庫的方法,它工作正常。

當我把它從事務性服務的方法,如:

@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);  
    } 

...調用findAllPaged()再次工作正常。

然而,當我嘗試調用它應該使用指定的查詢我總是得到一個異常的方法:

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 

在庫法使用org.springframework.data.jpa.repository.Query註釋從事務上下文再次工作正常。

一段時間調試後,似乎問題 org.springframework.data.jpa.repository.query.NamedQuery造成的,doCreateCountQuery(),並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被捕獲,該事務回滾

我發現下面的解決方法(有時!):

  1. 使用的庫法

  2. org.springframework.data.jpa.repository.Query註釋OR-創建另一個命名查詢

    @NamedQuery(
        name = "Customer.findByNamePattern.count", 
        query = "select count(c.id) from Customer c where c.name like :pattern" 
    ), 
    

,讓人聽不清楚我:

  • 調用findAll()應引起同樣的問題,但它不。爲什麼?
  • 使用org.springframework.data.jpa.repository.Query而不是@NamedQuery也不會導致問題,爲什麼?
  • 如何在事務性上下文中使用帶有可分頁選項的@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]上閱讀了類似的錯誤之後,我將hibernate版本降級到4.2.15.Final,從而解決了問題。 但是問題仍然存在,是否可以在不更改Hibernate版本的情況下解決問題?

回答

1

我加了一個PR與一個潛在的解決辦法:https://github.com/spring-projects/spring-data-jpa/pull/110 我們使用一個新的(一次性)的EntityManager執行命名查詢查找使原來的EntityManager不會從失敗的查找影響。 事實證明,你的問題很難重現。你介意給它一個旋轉嗎? 也許你甚至可以爲此提供一個小測試用例?

+0

Thomas,感謝您的幫助,我已經在https://github.com/sztgeza/springdata-pageable-query上傳了一個小樣本 – demura 2014-10-26 20:46:29

+0

非常感謝您的測試案例。我們對DATAJPA-617的更改似乎解決了您的問題。我使用Spring Boot創建了一個小例子,並使用FIX創建了JPA版本:https://github.com/thomasdarimont/spring-data-bugs/tree/master/DATAJPA-617 – 2014-10-27 09:17:58

+0

Thomas,非常感謝修復,它似乎它現在有效。請問,我們可以在哪個官方發佈(以及何時)使用修復程序? TX。再次! – demura 2014-10-27 15:36:58

1

你碰上由多個工件驅動的問題:

通過定義一個JPA EntityManager必須關閉(和潛在的重建),它拋出一個異常後。這通常在實體操作失敗的情況下實現,您可以確定該狀態。對於簡單的命名查詢查詢,這是相當嚴格的,因爲它肯定不需要創建新的EntityManager。但是,我們需要處理這個問題。

這就是說,我們已經爲手動定義的查詢解決了這個問題(這就是爲什麼你看到它爲@Query工作)。然而,我們爲DATAJPA-350引入的防禦機制並不適用於指定的查詢部分。我爲您創建了DATAJPA-617

+0

感謝您的回答! – demura 2014-10-26 20:45:27