事务基本概念
-什么是事务
-事务的特性
什么是事务
-事务指逻辑上一种操作要么全部成功,要么全部失败
事务的特性
-原子性
-一致性
-隔离性
-持久性
原子性
指事务不可分割,事务中的操作要么都发生,要么都不发生。
一致性
事务执行前后数据完整性必须保持一致
隔离性
隔离性指多个事务并发访问数据库时,一个用户的事务不能被其他的事务所干扰,多个并发事务之间数据要相互隔离。
持久性
一个事物一旦被提交,他对数据库的数据改变是永久性的,即使数据库发生故障也不应该对其有任何影响。
Spring事务接口
PlatformTransactionManager事务管理器
Spring为不同的持久层框架提供了不同的PlatformTransactionManager
TransactionDefinition事务定义信息(隔离级别、传播行为、超时、只读)
如果不考虑隔离级别会引发如下的安全问题:
脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
不可重复读:一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
幻读:幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样.一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。
事务隔离级别
隔离级别 | 含义 |
---|---|
DEFAULT | 使用后端数据库默认的隔离级别 |
READ_UNCOMMITED | 允许你读取还未提交的数据。可导致脏、幻、不可重复读 |
READ_COMMITED | 允许在并发事务已提交后读取。可防止脏读、但幻读和不可重复读仍可发生 |
REPEATABLE_READ | 对相同字段的多次读取是一致的,除非事务本事改变。可防止脏、不可重复读,但幻读仍可发生 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。在这所有隔离级别中最慢,它是典型的通过完全锁定在事务中涉及的数据表来完成的。 |
常见数据库默认隔离级别
隔离级别 | 数据库 |
---|---|
READ_UNCOMMITED | |
READ_COMMITED | Oracle |
REPEATABLE_READ | Mysql |
SERIALIZABLE |
事务的传播行为
TransactionStatus事务运行状态(是否提交、是否有保存点、是否是新事务)
事务的传播行为:用于解决service层方法之间相互调用的问题
事务的传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置 |
PROPAGATION_SUPPORTS | 持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 创建新事务,无论当前存不存在事务,都创建新事务 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |
Spring支持两种方式进行事务管理
编程式事务管理
-在实际应用中很少使用
-通过TransactionTemplate手动管理事务
实际操作
-在AccountService中使用TransactionTemplate
-TransactionTemplate依赖DataSourceTransactionManager
-DataSourceTransaction依赖DataSource构造
编程式的事务管理代码
// 注入转账DAO类
private AccountDAO accountDAO;
// 注入事务管理模板
private TransactionTemplate transactionTemplate;
@Override
public void transfer(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
// 匿名内部类使用外部变量时,外部变量应定义为final
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDAO.outMoney(out, money);
// int i = 1/0;
accountDAO.inMoney(in, money);
}
});
}
转账操作前
转账操作后
可以看到转账程序运行正常
accountDAO.outMoney(out, money);
int i = 1/0;
accountDAO.inMoney(in, money);
将原先的注释取消,毫无疑问会发生异常
java.lang.ArithmeticException: / by zero
而数据库未发生改变,说明事务产生了作用
使用XML配置声明式事务
-开发中推荐使用(代码入侵性最小)
-Spring的声明式事务是通过AOP(切面编程)实现的
XML配置声明式事务管理方式一
-由于配置繁琐,需要对每一个需要进行事务管理的类都进行配置,因而很少使用
-不需要在相应的类添加注解
-使用事务代理
-基于TransactionProxyFactoryBean
<!-- 引入外部文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置业务类 -->
<bean id="accountServiceImpl" class="springDemo2.AccountServiceImpl">
<property name="accountDAO" ref="accountDAO"/>
</bean>
<!-- 配置DAO类 -->
<bean id="accountDAO" class="springDemo2.AccountDAOImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置业务层代理 -->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置目标对象 -->
<property name="target" ref="accountServiceImpl"/>
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"/>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<!--
prop格式:
*PROPAGATION :事务的传播行为
*ISOLATION :事务的隔离级别
*readOnly :只读(不可进行修改,比如update、delete、set等)
*-Exception :发生哪些异常事务回滚
*+Exception :发生哪些异常事务不回滚
-->
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
XML配置声明式事务管理方式二
-不需要在相应的类添加注解
-配置AOP
-基于AspectJ
-常用
<!-- 引入外部文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置业务类 -->
<bean id="accountServiceImpl" class="springDemo3.AccountServiceImpl">
<property name="accountDAO" ref="accountDAO"/>
</bean>
<!-- 配置DAO类 -->
<bean id="accountDAO" class="springDemo3.AccountDAOImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务增强Advice -->
<tx:advice id="Advice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 配置方法和传播行为 -->
<!--
prop格式:
*propagation :事务的传播行为
*isolation :事务的隔离级别
*read-only :只读(不可进行修改,比如update、delete、set等)
*rollbac-for :发生哪些异常事务回滚
*no-rollbac-for :发生哪些异常事务不回滚
-->
<tx:method name="*" propagation="REQUIRED" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* springDemo3.*.*(..))" id="pointcut1"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="Advice" pointcut-ref="pointcut1"/>
</aop:config>
XML配置声明式事务管理方式三
-基于注解
-XML配置简便
-常用
<!-- 引入外部文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置业务类 -->
<bean id="accountServiceImpl" class="springDemo4.AccountServiceImpl">
<property name="accountDAO" ref="accountDAO"/>
</bean>
<!-- 配置DAO类 -->
<bean id="accountDAO" class="springDemo4.AccountDAOImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
需要在类的上方添加事务注解
@org.springframework.transaction.annotation.Transactional