我正在用 Spring、JPA 和 GlassFish 3.1 做我的第一个项目,我遇到了一些麻烦。我一直在寻找一个星期的解决方案,但我一无所获。
我有一个实体(称为角色),一个具有访问任何实体的通用方法的 GeneralReposiroty,一个实现有关角色的特定方法的 RoleRepository,以及一个调用 RoleRepository 的服务类。 Service 类的方法是事务性的,但如果传播属性是 REQUIRED、REQUIRES_NEW 或 NESTED,我会得到 RollBackException。如果传播是强制的,它会抛出一个异常说:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:339)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:101)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
...
如果它是 NOT_SUPPORTED、NEVER 或 SUPPORT,选择查询工作正常,但如果我尝试执行创建、更新或删除查询,我会得到相同的 RollbackException。
我已经调试了选择查询,在它们抛出 RollBackException 之前,查询得到了很好的结果,并且在函数结束时抛出了异常。此外,如果我使用 EntityManager 的 find() 获取实体并且服务方法没有 @Transactional 注释,则工作正常,但如果我将其设置为事务性的,它会因相同的异常而崩溃。
我拥有的所有实体都会发生这种情况,但我用它来解释问题。
我认为我的 Spring 配置有问题,但我不知道是什么。
这是异常的完整跟踪(在某些行中有对 AuthenticationSuccessHandler 类的引用,因为这是我调用服务类的地方):
WARNING: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Transaction marked for rollback.
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:845)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:314)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.citius.reservas.service.service$$EnhancerByCGLIB$$37b50d4.tryJPAFind(<generated>)
at com.citius.reservas.AuthenticationSuccessHandlerImpl.onAuthenticationSuccess(AuthenticationSuccessHandlerImpl.java:47)
...
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.
at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:428)
at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:855)
at com.sun.enterprise.transaction.UserTransactionImpl.commit(UserTransactionImpl.java:208)
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:842)
... 45 more
我有以下实体:
@Entity
@Table(name = "roles", catalog = "reservas")
@XmlRootElement
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@NotNull
@Size(min = 1, max = 50)
private String name;
private static final long serialVersionUID = 1L;
GenericRepository,通用类,所有repositories实现:
public class GenericRepositoryImpl<T> implements GenericRepository<T> {
@PersistenceContext
private EntityManager em;
private Class<T> type;
public GenericRepositoryImpl( ){
Type t = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) t;
type = (Class) pt.getActualTypeArguments()[0];
}
@Override
public T find(Object pk) {
try{
return em.find(type, pk);
}catch(java.util.NoSuchElementException ex){
return null;
}
}
@Override
public T create(T t) {
em.persist(t);
em.flush();
em.refresh(t);
return t;
}
@Override
public List<T> query(Query q) {
List<T> list= q.getResultList();
if(list==null)
list=new ArrayList<T>();
return list;
}
}
这是角色库:
@Repository
public class RoleRepositoryImpl extends GenericRepositoryImpl<Role>
implements RoleRepository{
@PersistenceContext
private EntityManager em;
@Override
public Role findByName(String name) {
Query q = (Query) this.em.createNamedQuery("Role.findByName");
q.setParameter("name", name);
Role r = (Role) q.getSingleResult();
return r;
}
@Override
public List<Role> findAll() {
Query q = (Query) this.em.createNamedQuery("Role.findAll");
return this.query(q);
}
}
服务类:
@Service
public class service {
@Autowired
private RoleRepository roleRepository;
@Transactional
//This throws a RollBackException
public void tryJPAEmpty(){
roleRepository.findByName("example");
}
//This doesn't
public Role tryJPAFind(){
return roleRepository.find(new Integer(1));
}
}
Spring配置(applicationContext.xml)是:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
">
<context:component-scan base-package="com.***.***" />
<jpa:repositories base-package="com.***.***.repositories" />
<tx:annotation-driven />
<tx:annotation-driven transaction-manager="transactionManager" />
<context:annotation-config />
<beans>
<bean id="EntityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="JPAReservas"/>
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager" >
<property name="allowCustomIsolationLevels">
<value>true</value>
</property>
</bean>
</beans>
</beans>
Persistence.xml(我在我的 GlassFish 服务器中创建了一个数据源和一个连接池):
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="JPAReservas" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>Reservas_mysql</jta-data-source>
<!--Named queries-->
<mapping-file>META-INF/mapping/Role.xml</mapping-file>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
</persistence-unit>
</persistence>
最后,我在 xml 文件中命名了 queires,如下所示: 从角色 r 中选择 r
<named-query name="Role.findByName">
<query>SELECT r FROM Role r WHERE r.name = :name</query>
</named-query>
我对 Spring 和 JPA 真的很菜鸟,所以如果你知道一些改进,或者我可以做的改变,请告诉我。
好的,我已经解决了将事务类型更改为 RESOURCE_LOCAL 的问题,但是在开始时,Glassfish 给我带来了很多麻烦。我就是这样解决的:
我已将 metadata-complete="true" 添加到 web.xml 的标头中。 这里解释了为什么这是必要的:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="true">
我已经更改了我的 persistence.xml,因为使用 RESOURCE_LOCAL 应用程序不会使用 Glassfish 的连接池,我必须提供有关数据库连接的信息:
<persistence-unit name="JPAReservas" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<mapping-file>META-INF/mapping/User.xml</mapping-file>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/reservas"/>
<property name="javax.persistence.jdbc.user" value="admin"/>
<property name="javax.persistence.jdbc.password" value="adminpassword"/>
</properties>
</persistence-unit>
最后,我更改了 applicationContext.xml(Spring 配置),因为我不能像在 JTA 中那样对 RESOURCE_LOCAL 使用相同的 EntityManager 类:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="EntityManagerFactory" />
</bean>
仅此而已。我不知道为什么我不能像以前那样使用 JTA,但我已经解决了!!! :).谢谢!