Java 弹簧不';事务回滚后关闭休眠会话
我试图在服务层中捕获ConstraintViolationException,并重新引发一个用户定义的已检查异常。我正在捕获控制器中的异常,并将错误对象添加到BindingResult中。我使用的是声明式事务管理,我尝试将DAO设置为存储库,并添加了PersistenceExceptionTranslationPostProcessor以捕获spring转换的异常。我还添加了一个txAdvice来回滚所有的可丢弃文件。我的异常确实被捕获,但我得到了一个错误500,原因是:Java 弹簧不';事务回滚后关闭休眠会话,java,spring,hibernate,Java,Spring,Hibernate,我试图在服务层中捕获ConstraintViolationException,并重新引发一个用户定义的已检查异常。我正在捕获控制器中的异常,并将错误对象添加到BindingResult中。我使用的是声明式事务管理,我尝试将DAO设置为存储库,并添加了PersistenceExceptionTranslationPostProcessor以捕获spring转换的异常。我还添加了一个txAdvice来回滚所有的可丢弃文件。我的异常确实被捕获,但我得到了一个错误500,原因是: Hibernate:
Hibernate: insert into user (email, password, first_name, last_name, userType) values (?, ?, ?, ?, 1)
[acme]: [WARN ] - 2013-Feb-05 11:12:43 - SqlExceptionHelper:logExceptions(): SQL Error: 1062, SQLState: 23000
[acme]: [ERROR] - 2013-Feb-05 11:12:43 - SqlExceptionHelper:logExceptions(): Duplicate entry 'admin' for key 'email_unique'
[acme]: [DEBUG] - 2013-Feb-05 11:12:43 - HibernateTransactionManager:processCommit(): Initiating transaction commit
[acme]: [DEBUG] - 2013-Feb-05 11:12:43 - HibernateTransactionManager:doCommit(): Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
[acme]: [ERROR] - 2013-Feb-05 11:12:43 - AssertionFailure:<init>(): HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in com.test.model.AdminUser entry (don't flush the Session after an exception occurs)
[acme]: [DEBUG] - 2013-Feb-05 11:12:43 - HibernateTransactionManager:doRollbackOnCommitException(): Initiating transaction rollback after commit exception
org.hibernate.AssertionFailure: null id in com.test.model.AdminUser entry (don't flush the Session after an exception occurs)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:79)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:194)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:156)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:468)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
我的Spring配置的一部分:
<context:component-scan base-package="com.test.dao, com.test.service" />
<context:property-placeholder location="/WEB-INF/spring.properties"/>
<import resource="springapp-security.xml"/>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testdb?zeroDateTimeBehavior=convertToNull"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="mappingLocations" value="classpath*:com/test/model/hbm/**/*.hbm.xml" />
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
</value>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" />
</tx:attributes>
</tx:advice>
如果我没有捕获运行时异常,我就不会得到hibernate异常(不要刷新会话…)您可能希望从控制器中删除事务注释,并将其添加到服务层 服务层如下所示。如果您的服务层正在抛出一个选中的异常,您可以将其添加到注释中,以便插入操作甚至不会被提交
public class UserServiceImpl implements UserDetailsService, UserService {
private UserDAO dao;
@Override
@Transactional(rollbackFor=ApplicationException.class)
public void save(User c) throws ApplicationException {
try {
dao.save(c);
} catch(DataIntegrityViolationException cve) {
throw new ApplicationException("email already registered");
}
}
代码中当前发生的情况是,事务没有回滚,但必须回滚,因为它实际上试图提交数据,但由于数据库约束,事务必须回滚。通过使用@Transactional(rollboor=ApplicationException.class)强制回滚,它将不允许事务执行提交,但它将回滚,并且您的应用程序仍会将错误添加到BindingResult。您可能希望从控制器中删除事务注释并将其添加到服务层 服务层如下所示。如果您的服务层正在抛出一个选中的异常,您可以将其添加到注释中,以便插入操作甚至不会被提交
public class UserServiceImpl implements UserDetailsService, UserService {
private UserDAO dao;
@Override
@Transactional(rollbackFor=ApplicationException.class)
public void save(User c) throws ApplicationException {
try {
dao.save(c);
} catch(DataIntegrityViolationException cve) {
throw new ApplicationException("email already registered");
}
}
代码中当前发生的情况是,事务没有回滚,但必须回滚,因为它实际上试图提交数据,但由于数据库约束,事务必须回滚。通过使用@Transactional(rollboor=ApplicationException.class)强制回滚,它将不允许事务执行提交,但它将回滚,并且您的应用程序仍会将错误添加到BindingResult中。从您的代码看,事务似乎正在控制器上启动并捕获异常。在这种情况下,不会发生回滚(不确定您是否希望这样做)。在您的帖子中,您说服务层正在启动事务并捕获异常。我的服务层没有启动事务,它在控制器中启动。对不起,我不清楚。我已经发布了我的服务代码。另外,从stacktrace中,我看到它正在回滚..HibernateTransactionManager:doRollbackOnCommitException():在提交异常后启动事务回滚从代码中看,事务似乎正在控制器上启动并捕获异常。在这种情况下,不会发生回滚(不确定您是否希望这样做)。在您的帖子中,您说服务层正在启动事务并捕获异常。我的服务层没有启动事务,它在控制器中启动。对不起,我不清楚。我已经发布了我的服务代码。从stacktrace中,我还看到它正在回滚..HibernateTransactionManager:doRollbackOnCommitException():在提交异常后启动事务回滚非常感谢!你能告诉我为什么不能让事务围绕控制器方法开始和结束吗?此外,我还尝试了您的解决方案,但没有在注释中指定异常,因为我的配置中有txAdvice,可以回滚所有可丢弃的文件,并且在您的控制器方法中没有发生异常。您无法告诉事务管理器回滚异常。唯一可以做到这一点的方法是在控制器中以编程方式启动事务,或者从控制器中抛出异常。以下是您不希望在控制器中使用事务的一些原因。您很可能不想被迫从控制器抛出异常或在控制器代码中处理事务管理。谢谢,我开始了解Spring如何在异常发生时使用AOP回滚事务。谢谢!!!非常感谢你!你能告诉我为什么不能让事务围绕控制器方法开始和结束吗?此外,我还尝试了您的解决方案,但没有在注释中指定异常,因为我的配置中有txAdvice,可以回滚所有可丢弃的文件,并且在您的控制器方法中没有发生异常。您无法告诉事务管理器回滚异常。唯一可以做到这一点的方法是在控制器中以编程方式启动事务,或者从控制器中抛出异常。以下是您不希望在控制器中使用事务的一些原因。您很可能不想被迫从控制器抛出异常或在控制器代码中处理事务管理。谢谢,我开始了解Spring如何在异常发生时使用AOP回滚事务。谢谢!!!
public class UserServiceImpl implements UserDetailsService, UserService {
private UserDAO dao;
@Override
@Transactional(rollbackFor=ApplicationException.class)
public void save(User c) throws ApplicationException {
try {
dao.save(c);
} catch(DataIntegrityViolationException cve) {
throw new ApplicationException("email already registered");
}
}