Spring的事务管理
最后一叨:
我相信你们首先会发现我的标题变了,不再是系统学习Spring之Spring in action了,因为以前那个标题没有一点意义,标题就是点明一篇文章的中心的.
使自己或者别人能够快速的找到自己想要的信息,所以在以后我都会采用这种直接点题的方法来写标题.还有一个就是以后不要乱叨了,对文章没有什么实质性的
作用,以后写博文会把这些无关紧要信息剔除。
索引:
1.通过四个词来了解Spring的事务
2.编程式事务管理
3.声明式事务管理
知识点:
1.通过四个词来了解Spring的事务
用四个词来描述Spring事务,分别是:原子性(Atomic)、一致性(Consistent)、隔离性(Isolate)、持久性(Durable).
1)原子性(Atomic): 事务是由一个或者多个行为捆绑在一起的工作的单一工作单元.一个或者多个活动同时成功则为成功,若有
一个失败则失败,简单的理解,单一工作单元要么同时工作,要么同时都不工作.
2)一致性(Consistent): 事务结束后,不管事务是成功还是失败都应该与逻辑模型的返回结果一致.
3)隔离性(Isolate): 多个用户操作相同的数据,防止每个用户之间数据相互影响,防止并发数据的读和写.
4)持久性(Durable): 一旦事务提交,事务结果就会持久保存,无论什么样的系统崩溃都无法影响到持久保存后的结果.
Spring不会直接去管理事务,而是代理给特定平台事务的实现,要么是JTA的实现要么是Persistence mechanism的Transaction Manager实现.
特定平台的Transaction Manager实现如下:
在本文中,下面将使用DataSourcetransactionmanager来进行事例的演示.
2.编程式事务管理
编程式事务管理就是在程序代码中添加事务管理,这是一个入侵式的事务管理,事例如下:
Dao层和Service层接口:
public interface SimpleDao { //新增用户信息 int addUser(Map<String,String> map); }
public interface SimpleService { //新增用户 int addUser(Map<String, String> map); }
Dao层的实现和Service层的实现:
@Repository("simpleDao") public class SimpleDaoImpl implements SimpleDao { @Autowired private SimpleJdbcTemplate jdbcTemplate; @SuppressWarnings("deprecation") @Override public int addUser(Map<String, String> map) { // TODO Auto-generated method stub return jdbcTemplate.update("insert into t_user values(?,?,?)",map.get("id"),map.get("username"),map.get("password")); } }
public class SimpleServiceImpl implements SimpleService { @Autowired private SimpleDao simpleDao; private TransactionTemplate transactionTemplate; public TransactionTemplate getTransactionTemplate() { return transactionTemplate; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public int addUser(final Map< String, String> map) { // TODO Auto-generated method stub transactionTemplate.execute(new TransactionCallback<Void>(){ public Void doInTransaction(TransactionStatus txStatus){ try{ simpleDao.addUser(map); }catch(RuntimeException e){ txStatus.setRollbackOnly(); System.out.println("回滚成功."); throw e; } return null; } }); return 0; } }
配置文件:
<context:component-scan base-package="com.ricky.zero"> </context:component-scan> <aop:aspectj-autoproxy/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/studyspring"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="simpleService" class="com.ricky.zero.service.impl.SimpleServiceImpl"> <property name="transactionTemplate"> <bean class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> </property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate"> <constructor-arg ref="dataSource"/> </bean>
从service的实现中可以看出,编程式的事务管理入侵性很强,但是优点也很明确,就是你可以细粒度的控制事务.一般情况下如果你不需要细粒度的去控制
事务,你可以选择下面要讲的非入侵性的声明式的事务管理.
测试下上面的编程式事务管理:
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); SimpleService simpleService = (SimpleService)ctx.getBean("simpleService"); Map<String,String> p = new HashMap<String,String>(); p.put("id", "1"); p.put("username", "big"); p.put("password", "small"); simpleService.addUser(p);
执行第一次,可以看到数据库中多了一条记录,
若继续在执行此程序,可以预见的是违反了唯一性约束条件,执行后,打印出"回滚成功."并输出异常信息.
得到以上信息说明编程式事务管理成功.
3.声明式事务管理
Dao层接口和Service层接口与上文相同,不在重复.
Dao层实现没有做任何修改,只修改了Service层实现:
@Service("simpleService") public class SimpleServiceImpl implements SimpleService { @Autowired private SimpleDao simpleDao; @Override public int addUser(final Map< String, String> map) { // TODO Auto-generated method stub int result = simpleDao.addUser(map); return result; } }
配置文件:
<context:component-scan base-package="com.ricky.zero"> </context:component-scan> <aop:aspectj-autoproxy/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/studyspring"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate"> <constructor-arg ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* *..SimpleService.*(..))"/> </aop:config>
测试运行:
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); SimpleService simpleService = (SimpleService)ctx.getBean("simpleService"); Map<String,String> p = new HashMap<String,String>(); p.put("id", "4"); p.put("username", "big"); p.put("password", "small"); simpleService.addUser(p);
运行结果:
插入成功,这是正确的操作.
修改Service层的实现 :
@Service("simpleService") public class SimpleServiceImpl implements SimpleService { @Autowired private SimpleDao simpleDao; @Override public int addUser(final Map< String, String> map) { // TODO Auto-generated method stub int result = simpleDao.addUser(map); //多执行了一次插入,由于ID相同,肯定会抛异常,如果能正确回滚,则没有一条信息可以插进去. simpleDao.addUser(map); return result; } }
修改测试:
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); SimpleService simpleService = (SimpleService)ctx.getBean("simpleService"); Map<String,String> p = new HashMap<String,String>(); p.put("id", "5");//id由4变成5 p.put("username", "big"); p.put("password", "small"); simpleService.addUser(p);
运行测试:
回滚成功,没有一条id为5的记录.
声明式事务管理除了XML配置的形式还有注解的形式.
修改Service实现:
@Service("simpleService") @Transactional(propagation = Propagation.SUPPORTS , readOnly = true) public class SimpleServiceImpl implements SimpleService { @Autowired private SimpleDao simpleDao; @Transactional(propagation = Propagation.REQUIRED , readOnly = false) @Override public int addUser(final Map< String, String> map) { // TODO Auto-generated method stub int result = simpleDao.addUser(map); //多执行了一次插入,由于ID相同,肯定会抛异常,如果能正确回滚,则没有一条信息可以插进去. simpleDao.addUser(map); return result; } }
配置文件:
<context:component-scan base-package="com.ricky.zero"> </context:component-scan> <aop:aspectj-autoproxy/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/studyspring"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate"> <constructor-arg ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>
测试同上:
注解和XML得到的结果是相同的,至于选择则根据喜好.