Spring声明式事务
来源:互联网 发布:软件设计师 证书 编辑:程序博客网 时间:2024/06/18 01:56
事务
事务是一组逻辑上的操作,要么全部操作都成功,要么全部失败!
用四个词解释事务
ACID
- 原子性(Atomic):原子性保证事务的所有操作要么都发生,要么都不发生。
- 一致性(Consistent):一个事务结束了(不管成功与否),系统所处的状态和它的业务规则是一致的。
- 隔离性(Isolated):一个事务执行的时候不会受到其他事务的干扰。
- 持久性(Durable):事务一旦结束,数据就永久的保存到了数据库。
事务的隔离级别
多个事务同时进行,经常会操作同一个数据,在这种并发的情况下容易导致以下问题:
- 脏度—–脏度发生在一个事务读取了另一个事务改写但尚未提交的数据。如果另外一个事务回滚了,那么第一个事务读取的这个数据就是无效的。
- 不可重复读—–不可重复读发生在一个事务执行相同的查询两次货两次以上,但是每一次读取到的数据都不同。这通常是在两次查询之间有另外一个事务进行了更新。
- 幻读—–幻读与不可重复读有些类似。当事务(T1)读取了N行数据后,事务(T2)插入了几条数据,幻读就发生了。在后来的查询中T1就会发现一些原来没有的数据。
在理想状态下,事务之间将完全隔离,从而防止发生以上问题,可是完全隔离会严重影响性能。因为隔离经常牵涉到锁定数据库中的记录(而有时是锁定完整的数据表)。侵占性的锁定会阻碍并发,要求事务互相等待来完成工作。
考虑到完全隔离会影响性能,而且并不是所有的程序都需要完全隔离,所以有时可以在事务隔离方面灵活处理。因此,就会有几个隔离级别:
- 未提交读(READ_UNCOMMIT):允许读取尚未提交的数据。将会导致脏读、不可重复读和幻读。
- 已提交读(READ_COMMIT):允许读取已经提交的数据。可以防止脏读,不可以防止不可重复读和幻读。
- 可重复读(REPEATABLE_READ):对相同字段的读取是一致的,除非数据被当前事务本身改变。可以防止脏读、不可重复读。但是幻读仍有可能发生。
- 串读(SERIZLIZABLE):完全服从ACID的隔离级别,确保不会发生上述三种情况,当然,这也是最慢的。
Spring中的事务管理
1、平台事务管理器
PlatformTransactionManager:平台事务管理器。
Spring不直接管理事务。但是它为不同的持久层框架提供了不同的PlatfromTransactionManager接口实现:
2、事务定义
事务隔离级别:
事务传播行为:
3、事务状态
是否有保存点;
是否是一个新的事务;
事务是否已经提交。
三者的关系:
PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中产生一些事务状态,由TransactionStatus记录。
事务操作环境准备
数据表的创建:
CREATE TABLE 'account' ( 'id' INT(11) NOT NULL AUTO_INCREMENT, 'name' VARCHAR(20) NOT NULL, 'money' DOUBLE DEFAULT NULL, PRIMARY KEY ('id')) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;INSERT INTO account VALUES ('1', 'aaa', '1000');INSERT INTO account VALUES ('2', 'bbb', '1000');INSERT INTO account VALUES ('3', 'ccc', '1000');
Dao的创建:
public interface AcountDao { public void from(String fromName,double money); public void to(String toName,double money);}
Dao的实现类:
public class AcountDaoImpl extends JdbcDaoSupport implements AcountDao { @Override public void from(String fromName, double money) { String sql = "UPDATE account SET money = money - ? WHERE NAME=?"; this.getJdbcTemplate().update(sql,money,fromName); } @Override public void to(String toName, double money) { String sql = "UPDATE account SET money = money + ? WHERE NAME=?"; this.getJdbcTemplate().update(sql,money,toName); }}
Service接口:
public interface AcountService { public void transfer(String FromName,String ToName,double money);}
Service的实现:
public class AcountServiceImpl implements AcountService { private AcountDao acountDao; public void setAcountDao(AcountDao acountDao) { this.acountDao = acountDao; } @Override public void transfer(String FromName, String ToName, double money) { acountDao.from(FromName, money); acountDao.to(ToName, money); }}
applicationContext.xml的配置:
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置C3P0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="acountDao" class="Demo01.AcountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="acountService" class="Demo01.AcountServiceImpl"> <property name="acountDao" ref="acountDao"/> </bean></beans>
测试类:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class Demo01Test { @Autowired @Qualifier("acountService") private AcountService acountService; @Test public void test() { acountService.transfer("aaa", "bbb", 100d); }}
完成了此次事务管理实验环境的搭建!
编程式事务管理
注册事务管理器:
<!-- 注册事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/></bean>
注册事务模板类:
<!-- 注册事务模板类 --><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/></bean>
向Service层注入模板类:
ServiceImpl添加:
private TransactionTemplate transactionTemplate;public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate;}
配置文件添加:
<bean id="acountService" class="Demo01.AcountServiceImpl"> <property name="acountDao" ref="acountDao"/> <property name="transactionTemplate" ref="transactionTemplate"/></bean>
在ServiceImpl中使用模板实现事务的管理:
public void transfer(final String FromName, final String ToName, final double money) {transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus status) { acountDao.from(FromName, money); int a = 1/0;//在转账过程中产生异常 acountDao.to(ToName, money); return null; } }); }
我在转账过程中产生异常,如果不进行事务管理,会导致aaa扣款,bbb未增款。经过此次编程式事务管理,经测试成功!
声明式事务管理(最原始)
基于TransactionProxyFactoryBean.
导入AOP相应jar包。
第一步:注册事务管理器
第二步:创建代理对象:
<!-- 创建代理对象 --> <bean id="acountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 对哪个对象生成代理 --> <property name="target" ref="acountService"/> <!-- 注入事务管理器 --> <property name="transactionManager" ref="transactionManager"/> <!-- 事务的属性设置 --> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop><!--设置事务的传播行为 --> </props> </property> </bean>
prop标签的格式:
可以发现,这种方式有一个很大的缺点,我们需要为每一个管理事务的类生成代理。并且每一个都要配置。
声明式事务管理(自动代理)重点
导入AspectJ的jar包
第一步:注册事务管理器
第二步:定义增强:
<!-- 定义增强 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 事务属性的配置 --> <tx:attributes> <tx:method name="transfer" /> </tx:attributes> </tx:advice>
第三步:定义AOP配置
<!-- aop配置,定义切点和切面的信息 --> <aop:config> <!-- 定义切点,哪些类的哪些方法应用增强 --> <aop:pointcut expression="execution(* Demo03.AcountService+.*(..))" id="myPointCut"/> <!-- 定义切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointCut"/> </aop:config>
第四步:测试类
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext3.xml")public class Demo03Test { @Autowired @Qualifier("acountService")//不需要注入代理了,因为生成这个对象时候就是代理对象了 private AcountService acountService; @Test public void test() { acountService.transfer("aaa", "bbb", 100d); }}
声明式事务管理(基于注解)
第一步:注册事务管理器
第二步:注解事务
<!-- 开启注解的事务管理 --> <tx:annotation-driven transaction-manager="transactionManager"/>
第三步:在Service上使用注解
@Transactional @Override public void transfer(String FromName, String ToName, double money) { acountDao.from(FromName, money); int a=1/0; acountDao.to(ToName, money); }
- spring声明式事务
- Spring---声明式事务
- Spring声明式事务
- Spring声明式事务
- spring 声明式事务
- 声明式事务 spring
- Spring声明式事务
- Spring声明式事务
- spring 声明式事务
- Spring声明式事务
- Spring 声明式事务
- Spring声明式事务
- Spring声明式事务
- spring声明式事务
- Spring声明式事务
- Spring声明式事务
- Spring声明式事务
- spring声明式事务
- 常用贷款词汇英文名称
- 用tensorflow做图像分割的查错技巧
- Android之路 4. 活动的启动模式
- USB host通讯
- VS2015/VS2017:C compiler identification is unknown
- Spring声明式事务
- soot-2
- A
- 深度学习数据集集锦
- 解决使用 libcurl 遇到的多线程崩溃问题
- AIDL详解
- 近期项目设计知识点总结:flex布局、canvas、引入外部字体、rem、匹配选择器等
- 结构体的自引用及相互引用
- 用 CAShapeLayer、毛玻璃镂空效果创建加载动画