【发布时间】:2014-12-11 16:32:06
【问题描述】:
我在尝试使用 Spring Data 存储库中的分页运行 @NamedQuery 时考虑了一个问题。 实体类如下所示:
@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被捕获,但事务被回滚(有时!)
我找到了以下解决方法:
在存储库方法上使用
org.springframework.data.jpa.repository.Query注解-
或者-创建另一个命名查询
@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 来避免该问题(而不是显式创建计数查询)?
任何帮助将不胜感激!
更新
使用的版本是: 春天: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.Final,从而解决了该问题。 但是一个问题仍然存在,是否可以在不更改 Hibernate 版本的情况下解决问题?
【问题讨论】:
标签: spring jpa spring-data spring-data-jpa spring-transactions