一、事务四个属性
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
二、事务的重要性
打个最简单的比方吧,A和B两人之间的支付宝转账问题,A向B转账50RMB,正常的结果是,A - 50 并且 B + 50; 但如果是下面这种情况,那就杯具了,A - 50 成功,而B + 50 失败。这样一来岂不是 A亏大了!谁还敢随意转账?就算是首富,也不敢随意转账O(∩_∩)O哈!
所以,在进行 A - 50 和 B + 50 需要添加事务管理。
三、先看下没有加事务的Demo, 看完就知道事务的重要性啦~
(1)、整体结构、
(2)、jar 包
(3)、SQL语句
创建数据库 create database spring; 建立表 create table countmoney(idCard int primary key auto_increment,name varchar(32),money int); 插入两条记录 insert into countmoney(name,money)values(‘xx‘,300); insert into countmoney(name,money)values(‘++‘,300); 结果 select * from countmoney;
+--------+------+-------+
| idCard | name | money |
+--------+------+-------+
| 1 | xx | 300 |
| 2 | ++ | 300 |
+--------+------+-------+
(4)、代码
model 层
1 package com.xpw.model; 2 3 public class Count { 4 private int idCard; 5 private String name; 6 private int money; 7 8 public Count(){ 9 10 } 11 12 public int getIdCard() { 13 return idCard; 14 } 15 public void setIdCard(int idCard) { 16 this.idCard = idCard; 17 } 18 public String getName() { 19 return name; 20 } 21 public void setName(String name) { 22 this.name = name; 23 } 24 public int getMoney() { 25 return money; 26 } 27 public void setMoney(int money) { 28 this.money = money; 29 } 30 31 }
dao 层
1 package com.xpw.dao; 2 3 public interface TradeDao { 4 5 public void outputMoney(int idCard, int money); 6 7 public void inputMoney(int idCard, int money); 8 }
dao impl 层
1 package com.xpw.dao.impl; 2 3 import org.springframework.jdbc.core.JdbcTemplate; 4 import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; 5 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 6 import org.springframework.jdbc.core.namedparam.SqlParameterSource; 7 8 import com.xpw.dao.TradeDao; 9 10 public class TradeDaoImpl implements TradeDao { 11 12 private NamedParameterJdbcTemplate namedParameterJdbcTemplate; 13 14 public void setNamedParameterJdbcTemplate( 15 NamedParameterJdbcTemplate namedParameterJdbcTemplate) { 16 this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; 17 } 18 19 @Override 20 public void outputMoney(int idCard, int count) { 21 String sql = "update trademoney set money = money -:count where idCard = :idCard"; 22 MapSqlParameterSource param = new MapSqlParameterSource(); 23 param.addValue("count", count); 24 param.addValue("idCard", idCard); 25 this.namedParameterJdbcTemplate.update(sql, param); 26 } 27 28 @Override 29 public void inputMoney(int idCard, int count) { 30 //我们故意在此出错,抛出异常,让 B + 50失败 31 System.out.println(1/0); 32 String sql = "update trademoney set money = money + :count where idCard = :idCard"; 33 MapSqlParameterSource param = new MapSqlParameterSource(); 34 param.addValue("count", count); 35 param.addValue("idCard", idCard); 36 this.namedParameterJdbcTemplate.update(sql, param); 37 } 38 }
Service层
1 package com.xpw.service; 2 3 public interface TradeService { 4 public void trade(int fromIdCard, int toIdCard, int money); 5 }
Service impl 层
1 package com.xpw.service.impl; 2 3 import com.xpw.dao.TradeDao; 4 import com.xpw.service.TradeService; 5 6 public class TradeServiceImpl implements TradeService { 7 8 private TradeDao tradeDao; 9 10 public void setTradeDao(TradeDao tradeDao) { 11 this.tradeDao = tradeDao; 12 } 13 14 @Override 15 public void trade(int fromIdCard, int toIdCard, int money) { 16 this.tradeDao.outputMoney(fromIdCard, money); 17 this.tradeDao.inputMoney(toIdCard, money); 18 } 19 }
(5)文件配置信息
beans.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context.xsd"> 9 10 <context:component-scan base-package="org.springframework.docs.test" /> 11 <context:property-placeholder location="jdbc.properties"/> 12 13 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 14 <property name="driverClassName" value="${jdbc.driverClassName}"/> 15 <property name="url" value="${jdbc.url}"/> 16 <property name="username" value="${jdbc.username}"/> 17 <property name="password" value="${jdbc.password}"/> 18 </bean> 19 20 <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 21 <constructor-arg ref="dataSource"></constructor-arg> 22 </bean> 23 24 <bean id="tradeDao" class="com.xpw.dao.impl.TradeDaoImpl"> 25 <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"/> 26 </bean> 27 28 <bean id="tradeService" class="com.xpw.service.impl.TradeServiceImpl"> 29 <property name="tradeDao" ref="tradeDao"></property> 30 </bean> 31 </beans>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring jdbc.username=root jdbc.password=root
(6)测试
package com.xpw.trade; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.xpw.service.TradeService; public class TradeTest { private static ApplicationContext ac; @Before public void init(){ ac = new ClassPathXmlApplicationContext("beans.xml"); } @Test public void testTrade(){ TradeService ts = (TradeService) ac.getBean("tradeService"); int fromIdCard = 1; int toIdCard = 2; int money = 50; ts.trade(fromIdCard, toIdCard, money); } }
结果 select * from trademoney; +--------+------+-------+ | idCard | name | money | +--------+------+-------+ | 1 | xx | 250 | | 2 | ++ | 300 | +--------+------+-------+ 2 rows in set (0.00 sec) 由于,在 inputmoney()方法,我们故意 做1/0操作,也没有做try catch ,导致不会往下执行向B账户添加50的业务,所以 A亏了50。。。 从上面的结果我们知道了事务的重要性了吧。。A - 50 和 B + 50 必须同时成功,才可以称为一个成功的交易,一旦 谁出错,就必须回滚!即 不能 将 A - 50 , B 也不能 被 + 50
下面,我们就 添加事务管理吧。。当然,事务管理有两种,详情见如下
四、spring 事务分类
1、编程式事务管理
Spring 提供的事务模版类:org.springframework.transaction.support.TransactionTemplate
事务管理器:org.springframework.jdbc.datasource.DataSourceTransactionManager
service impl 层的代码有所改动(注意,便于阅者copy实践,我就把整个类的代码贴出来,下同)
1 package com.xpw.service.impl; 2 3 import org.springframework.transaction.TransactionStatus; 4 import org.springframework.transaction.support.TransactionCallback; 5 import org.springframework.transaction.support.TransactionCallbackWithoutResult; 6 import org.springframework.transaction.support.TransactionTemplate; 7 8 import com.xpw.dao.TradeDao; 9 import com.xpw.service.TradeService; 10 11 public class TradeServiceImpl implements TradeService { 12 13 private TradeDao tradeDao; 14 private TransactionTemplate transactionTemplate; 15 16 public void setTradeDao(TradeDao tradeDao) { 17 this.tradeDao = tradeDao; 18 } 19 20 public void setTransactionTemplate(TransactionTemplate transactionTemplate) { 21 this.transactionTemplate = transactionTemplate; 22 } 23 24 //编程事务管理 25 @Override 26 public void trade(final int fromIdCard, final int toIdCard, final int money) { 27 this.transactionTemplate.execute(new TransactionCallbackWithoutResult() { 28 29 @Override 30 protected void doInTransactionWithoutResult(TransactionStatus arg0) { 31 tradeDao.outputMoney(fromIdCard, money); 32 tradeDao.inputMoney(toIdCard, money); 33 } 34 }); 35 } 36 }
beans.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context.xsd"> 9 10 <context:component-scan base-package="org.springframework.docs.test" /> 11 <context:property-placeholder location="jdbc.properties"/> 12 13 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 14 <property name="driverClassName" value="${jdbc.driverClassName}"/> 15 <property name="url" value="${jdbc.url}"/> 16 <property name="username" value="${jdbc.username}"/> 17 <property name="password" value="${jdbc.password}"/> 18 </bean> 19 20 <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> 21 <property name="transactionManager" ref="transactionManager"></property> 22 </bean> 23 <!-- 事务管理器 --> 24 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 25 </bean> 26 27 <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 28 <constructor-arg ref="dataSource"></constructor-arg> 29 </bean> 30 31 <bean id="tradeDao" class="com.xpw.dao.impl.TradeDaoImpl"> 32 <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"/> 33 </bean> 34 35 <bean id="tradeService" class="com.xpw.service.impl.TradeServiceImpl"> 36 <property name="tradeDao" ref="tradeDao"></property> 37 <property name="transactionTemplate" ref="transactionTemplate"></property> 38 </bean> 39 </beans>
其它的代码都没有变
结果: mysql> select * from trademoney; +--------+------+-------+ | idCard | name | money | +--------+------+-------+ | 1 | xx | 300 | | 2 | ++ | 300 | +--------+------+-------+ 2 rows in set (0.00 sec) 从上面的结果可以知道,编程式事务管理已经成功了,在 B + 50 失败了,回回滚,所以 A 不会 - 50
2、声明式事务管理
使用annotation
service impl 层
1 package com.xpw.service.impl; 2 3 4 import org.springframework.transaction.annotation.Transactional; 5 6 import org.springframework.transaction.support.TransactionTemplate; 7 8 import com.xpw.dao.TradeDao; 9 import com.xpw.service.TradeService; 10 11 @Transactional 12 public class TradeServiceImpl implements TradeService { 13 14 private TradeDao tradeDao; 15 private TransactionTemplate transactionTemplate; 16 17 public void setTradeDao(TradeDao tradeDao) { 18 this.tradeDao = tradeDao; 19 } 20 21 public void setTransactionTemplate(TransactionTemplate transactionTemplate) { 22 this.transactionTemplate = transactionTemplate; 23 } 24 25 @Override 26 public void trade(int fromIdCard, int toIdCard, int money) { 27 this.tradeDao.outputMoney(fromIdCard, money); 28 this.tradeDao.inputMoney(toIdCard, money); 29 } 30 31 }
beans.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop = "http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 12 http://www.springframework.org/schema/aop 13 http://www.springframework.org/schema/aop/spring-aop.xsd"> 14 15 <context:component-scan base-package="org.springframework.docs.test" /> 16 <context:property-placeholder location="jdbc.properties"/> 17 18 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 19 <property name="driverClassName" value="${jdbc.driverClassName}"/> 20 <property name="url" value="${jdbc.url}"/> 21 <property name="username" value="${jdbc.username}"/> 22 <property name="password" value="${jdbc.password}"/> 23 </bean> 24 25 <!-- 事务管理器 --> 26 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 27 <property name="dataSource" ref="dataSource"></property> 28 </bean> 29 30 <tx:annotation-driven transaction-manager="transactionManager"/> 31 32 <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 33 <constructor-arg ref="dataSource"></constructor-arg> 34 </bean> 35 36 <bean id="tradeDao" class="com.xpw.dao.impl.TradeDaoImpl"> 37 <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"/> 38 </bean> 39 40 <bean id="tradeService" class="com.xpw.service.impl.TradeServiceImpl"> 41 <property name="tradeDao" ref="tradeDao"></property> 42 </bean> 43 </beans>
结果: +--------+------+-------+ | idCard | name | money | +--------+------+-------+ | 1 | xx | 300 | | 2 | ++ | 300 | +--------+------+-------+ 2 rows in set (0.00 sec) 由此,此方式成功 添加了事务管理
使用xml 方式
service impl 层
1 package com.xpw.service.impl; 2 3 import com.xpw.dao.TradeDao; 4 import com.xpw.service.TradeService; 5 6 public class TradeServiceImpl implements TradeService { 7 8 private TradeDao tradeDao; 9 10 public void setTradeDao(TradeDao tradeDao) { 11 this.tradeDao = tradeDao; 12 } 13 14 15 @Override 16 public void trade(int fromIdCard, int toIdCard, int money) { 17 this.tradeDao.outputMoney(fromIdCard, money); 18 this.tradeDao.inputMoney(toIdCard, money); 19 } 20 }
beans.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context.xsd 9 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop.xsd"> 12 13 <context:component-scan base-package="org.springframework.docs.test" /> 14 <context:property-placeholder location="jdbc.properties" /> 15 16 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 17 destroy-method="close"> 18 <property name="driverClassName" value="${jdbc.driverClassName}" /> 19 <property name="url" value="${jdbc.url}" /> 20 <property name="username" value="${jdbc.username}" /> 21 <property name="password" value="${jdbc.password}" /> 22 </bean> 23 24 <!-- 事务管理器 --> 25 <bean id="transactionManager" 26 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 27 <property name="dataSource" ref="dataSource"></property> 28 </bean> 29 30 <!-- 事务通知器 --> 31 <tx:advice> 32 <tx:attributes> 33 <tx:method name="*" /> 34 </tx:attributes> 35 </tx:advice> 36 <!-- 事务切面 --> 37 <aop:config> 38 <!-- 事务切点 --> 39 <aop:pointcut expression="execution(* com.xpw.service.*.*(..))" id="transactionPointcut"/> 40 <aop:advisor advice-ref="transactionPointcut"/> 41 </aop:config> 42 43 <bean id="namedParameterJdbcTemplate" 44 class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 45 <constructor-arg ref="dataSource"></constructor-arg> 46 </bean> 47 48 <bean id="tradeDao" class="com.xpw.dao.impl.TradeDaoImpl"> 49 <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate" /> 50 </bean> 51 52 <bean id="tradeService" class="com.xpw.service.impl.TradeServiceImpl"> 53 <property name="tradeDao" ref="tradeDao"></property> 54 </bean> 55 </beans>
结果 mysql> select * from trademoney; +--------+------+-------+ | idCard | name | money | +--------+------+-------+ | 1 | xx | 300 | | 2 | ++ | 300 | +--------+------+-------+ 2 rows in set (0.00 sec)
五、总结
事务管理有编程式、声明式,本人推荐后者。因为前者,虽然实现了事务管理,但在一定程度上,非业务逻辑代码浸入了我们的业务逻辑代码,如果系统大型的话,也不可避免重复操作,代码看起来也不整洁了,也不方便后期维护。
【tip】转载请注明原文来自 :http://www.cnblogs.com/chenmo-xpw/p/3949264.html