我使用Spring通过注释@Transactional
来管理我的事务。但是,我发现它即使有RuntimeException
也无法回滚。
首先,我解释一下我的应用结构。这是一个部署在Tomcat中的Web项目。我在DispatcherServlet
中添加了web.xml
。然后在servlet-context.xml
中定义其配置。在servlet-context.xml
中,我配置了事务。数据库是mysql-5.7.16-winx64
。
<tx:annotation-driven />
<beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory" />
</beans:bean>
<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
<beans:property name="url" value="jdbc:mysql://localhost:3306/digitalmenu?useUnicode=true&characterEncoding=UTF-8" />
<beans:property name="username" value="root" />
<beans:property name="password" value="root" />
</beans:bean>
<beans:bean id="hibernate4AnnotatedSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource"></beans:property>
<beans:property name="packagesToScan">
<beans:list>
<beans:value>com.shuishou.digitalmenu.account.models</beans:value>
</beans:list>
</beans:property>
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</beans:prop>
<beans:prop key="hibernate.show_sql">true</beans:prop>
<beans:prop key="hibernate.format_sql">true</beans:prop>
<beans:prop key="hibernate.use_sql_comments">true</beans:prop>
<beans:prop key="hibernate.auto_close_session">true</beans:prop>
<beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
<!-- Enable the second-level cache -->
<beans:prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</beans:prop>
<beans:prop key="hibernate.cache.use_second_level_cache">true</beans:prop>
<beans:prop key="hibernate.cache.use_query_cache">true</beans:prop>
</beans:props>
</beans:property>
例如,我有一个服务AccountService
(已经由@Service
注释)。这个类中有一个方法addAccount
。我将@Transactional
放在addAccount
方法前面。
@org.springframework.transaction.annotation.Transactional(readOnly = false)
public Result addAccount(long userId, String username, String password) {
UserData user = userDA.getUserByUsername(username);
if (user != null)
return new Result("account_existing");
user = new UserData();
user.setUsername(username);
user.setPassword(password);
userDA.saveUser(user);
}
这里,userDA
是一个只调用hibernate会话来保存对象的存储库。
@Repository("userDA")
public class UserDataAccessor implements IUserDataAccessor {
@Autowired
protected SessionFactory sessionFactory;
public Session getSession() {
return sessionFactory.getCurrentSession();
}
@Override
public Serializable saveUser(UserData user) {
return sessionFactory.getCurrentSession().save(user);
}
......
}
这是一种简单的方法。它不会抛出任何异常,所以一切都正常运行,但如果我改变一点让方法发生错误,我发现数据仍然存在于数据库中。在这里,我更改了addAccount
方法,如下所示:
@Transactional(readOnly = false)
public Result addAccount(long userId, String username, String password) {
UserData user = userDA.getUserByUsername(username);
if (user != null)
return new Result("account_existing");
user = new UserData();
user.setUsername(username);
user.setPassword(password);
userDA.saveUser(user);
int i = 1/0; //-------------------here to create a RuntimeException
}
我在方法结束时制作了RuntimeException
。但是,由于存在错误,因此不应将用户对象保留在数据库中。但数据仍然输入数据库。
我在userDA.saveUser(user);
的行设置了一个断点我发现在java运行这行userDA.saveUser(user);
之后,数据库被改变了,这与我想的不同。根据我的观点,数据库应该在函数运行完成后更改,没有任何异常。
因此,我有两个问题。