簡體   English   中英

事務回滾后,Spring不會關閉hibernate會話

[英]Spring doesn't close hibernate session after transaction rollback

我正在嘗試在服務層中捕獲ConstraintViolationException並重新拋出用戶定義的已檢查異常。 我正在我的控制器中捕獲異常並向我的BindingResult添加一個錯誤對象。 我正在使用聲明式事務管理我試圖使我的DAO成為一個存儲庫,並添加了一個PersistenceExceptionTranslationPostProcessor來捕獲一個Spring翻譯的異常。 我還添加了一個txAdvice來回滾所有throwables。 我的異常確實被抓住但我得到的錯誤500:

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)

我的控制器:

@RequestMapping (method = RequestMethod.POST) 
    @Transactional
    public String registerAdmin(@Valid @ModelAttribute("user") AdminUser user, BindingResult bindingResult, ModelMap model)  {
        if (bindingResult.hasErrors()) {
            return "admin/admins/form";
        } 
        else if (!user.getPassword().equals(user.getConfirmPassword())) {
            bindingResult.addError(new ObjectError("user.confirmPassword", "Passwords don't match"));
            return "admin/admins/form";
        }
        else {
            user.setPassword(passwordEncoder.encodePassword(user.getPassword(), null));
            try {
                userService.save(user);
                return "redirect:/admin/admins";
            } catch(ApplicationException ce) {
                bindingResult.addError(new ObjectError("user.email", "Email already registered"));
                return "admin/admins/form";
            }

        }

    }

我的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>

服務層:

public class UserServiceImpl implements UserDetailsService, UserService {

    private UserDAO dao;


    @Override
    public void save(User c) throws ApplicationException {
        try {
            dao.save(c);
        } catch(DataIntegrityViolationException cve) {
            throw new ApplicationException("email already registered");
        }
    }

如果我沒有捕獲運行時異常,我沒有得到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(rollbackFor = ApplicationException.class)強制回滾,它將不允許事務執行提交但它將回滾,您的應用程序仍將錯誤添加到BindingResult。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM