Spring事务管理分为声明式事务管理和编程式事务管理,声明式事务管理又分为xml和注解两种配置方式。应该优先选择声明式事务,因为声明式事务对程序代码的影响最小,因此最符合非侵入式轻量级容器的理想 。只有在进行少量事务操作时,才应该选择编程式事务管理的方式。
声明式事务管理
xml配置方式
Spring配置文件:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 导入数据库配置文件 -->
<context:property-placeholder location="config.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName"><value>${jdbc_driverClassName}</value></property>
<property name="url"><value>${jdbc_url}</value></property>
<property name="username"><value>${jdbc_username}</value></property>
<property name="password"><value>${jdbc_password}</value></property>
<!-- ... -->
</bean>
<!-- 配置jdbcTemplate,使用jdbcTemplate操作数据库 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userService" class="com.springdemo.tx.UserService">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- xml方式的配置声明式事务 start-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.springdemo.tx.UserService.*(..))"/>
</aop:config>
<!-- xml方式的配置声明式事务 end-->
</beans>
config.properties:
jdbc_driverClassName=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://192.168.1.77:3306/testdb?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
jdbc_username=root
jdbc_password=root
UserService.java:
public class UserService {
private JdbcTemplate jdbcTemplate;
public void addUser(String name, int age){
jdbcTemplate.update("insert into test007 (name,age) values (name,age)", name, age);
}
public void updateUserName(String name, int id){
jdbcTemplate.update("update test007 set name=? where id=?", name, id);
}
public void updateUserAge(int age, int id){
jdbcTemplate.update("update test007 set age=? where id=?", age, id);
}
public void updateUser(String name, int age, int id){
updateUserName(name, id);
//模拟异常
Double.parseDouble("我不是Double,所以会抛出NumberFormatException异常");
updateUserAge(age, id);
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
main方法:
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.updateUser("小王", 18, 1);
}
使用注解方式
Spring配置文件中添加:
<!-- 开启注解配置 -->
<tx:annotation-driven transaction-manager="txManager"/>
然后在需要事务管理的方法上添加@Transactional注解
@Transactional
public void updateUser(String name, int age, int id){
updateUserName(name, id);
//模拟异常
Double.parseDouble("我不是Double,所以会抛出NumberFormatException异常");
updateUserAge(age, id);
}
注意:注解方式和xml方式可以二选一,也可以结合使用,两者没有依赖关系。
异常捕获和回滚
异常捕获会导致事务机制失效,继而不会触发回滚。
可以调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动回滚。
@Transactional
public void updateUser(String name, int age, int id){
try{
updateUserName(name, id);
//模拟异常
Double.parseDouble("我不是Double,所以会抛出NumberFormatException异常");
updateUserAge(age, id);
}catch(NumberFormatException ex){
//手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
ex.printStackTrace();
}
}
非RuntimeException时回滚
Spring事务管理机制默认只在抛出RuntimeException时才会触发回滚,可以设置rollbackFor属性来指定其他类型的异常也能回滚。
//rollbackFor指定抛出Exception类型异常时回滚
@Transactional(rollbackFor=Exception.class)
public void updateUser(String name, int age, int id) throws Exception{
updateUserName(name, id);
if(id == 1){
throw new Exception("Id为1的用户不可修改");
}
updateUserAge(age, id);
}
编程式事务管理
修改Spring配置文件:
<bean id="userService" class="com.springdemo.tx.UserService">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
<!-- 注释声明式事务管理配置 -->
<!-- <tx:annotation-driven transaction-manager="txManager"/>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.springdemo.tx.UserService.*(..))"/>
</aop:config> -->
修改UserService:
private TransactionTemplate transactionTemplate;
public void updateUser(final String name, final int age, final int id){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateUserName(name, id);
//模拟异常
Double.parseDouble("我不是Double,所以会抛出NumberFormatException异常");
updateUserAge(age, id);
}
});
}
public TransactionTemplate getTransactionTemplate() {
return transactionTemplate;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}