一、什么是事务
在实际业务场景中,经常会遇到数据频繁修改或读取的问题。在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能造成数据不可挽回的错乱,所以需要事务来对数据库进行管理。
一组对于数据库的操作,在业务上来说是最小的数据库操作单元,要么都成功,如果有失败的情况,需要恢复到操作之前的状态。
二、在mysql的jdbc中进行事务管理
1、手动开启事务
2、手动提交事务
3、回滚事务
三、事务的特性(ACID)
1、原子性(Automicity)
一个事务里,对数据库的多个操作是一个整体,不可分割;
操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
2、一致性(Consitency)
数据必须从一个一致性状态转移到另一个一致性状态,但是对于整个数据的完整性保持稳定;
3、隔离性(Isolation)
在该事务执行的过程中,无论发生的任何数据的改变都应只存在于该事务中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。
4、持久性(Durability)
四、并发事务下可能出现的问题
1、脏读(针对修改)
一个事务读到了另一个事务还没有提交的数据(缓存中的数据),此种情况针对修改操作;
2、不可重复读(针对修改)
一个事务两次读取同一行数据,中间正好另一个事务对数据进行更新,导致两次读到的结果不一致,不被信任。此种情况针对修改操作。
3、幻读(针对增删数据)
一个事务执行两次查询,第二次结果集包含了第一次中没有或某些已经被删除的数据,造成两次结果不一致,这是另一个事务在这两次查询中间插入或者删除了数据造成的。此种情况针对的是对数据的增加或者删除。
五、数据库的隔离级别
1、读未提交
2、读已提交
3、可重复读
4、串行化
解决了所有问题。
SERIALIZABLE是最高的隔离级别,它通过强制事务串行执行(注意是串行),避免了前面的幻读情况,由于他大量加上锁,导致大量的请求超时,因此性能会比较底下,再特别需要数据一致性且并发量不需要那么大的时候才可能考虑这个隔离级别
5、数据库的默认隔离级别
(1)mysql:默认可重复读;
(2)Oracle:读已提交
六、原生JDBC中的事务处理
1、手动开启事务
connection.setAutoCommit(false);
2、手动提交事务
3、回滚事务
七、spring中的事务管理
事务管理一般都在service中,一般只有在service中的一个方法中才有可能多次执行数据库操作。<br /> 事务和数据库的交互是两个业务逻辑,最好的解决方式是使用aop;
1、在spring中进行事务管理的核心
(1)事务管理器(PlatformTransactionManager)
这里使用其实现类DataSourceTransactionMannager
访问数据库的对象:
1)原生jdbc访问数据库:connection;
2)spring访问数据库:jdbcTemplate;
3)mybatis访问数据库:sqlSession;
4)hibernate访问数据累:session。
(2)事务的定义(TransactionDefinition)
TransactionDefinition控制着事务的属性,可定义事务的隔离级别、超时时间、是否只读、传播途径等。
注意:注解方式在@Transactional注解的属性中配置;xml方式在事务切面的通知
1)数据库的隔离级别(Isolation)
DEFAULT | 默认隔离级别,mysql是可重复读;Oracle是读已提交 |
---|---|
READ_COMMITTED | 读未提交 |
READ_UNCOMMITTED | 读已提交 |
REPEATABLE_READ | 可重复读 |
SERIALIZABLE | 串行化 |
2)超时时间
设置超时时间,在设置的时间内,如果没有成功访问到数据库,则断开本次连接。
3)是否只读
为了提高效率,如果一组对数据库的操作都是查询操作,可设置为只读,可节约时间。spring默认是可读可写的。
4)传播途径(详见传播途径章节)
2、spring管理事务的两种方案
(1)编程式事务(了解,工作中不用)
步骤:
1)在xml配置文件中配置事务管理器:DataSourceTransactionManager;
注意:事务管理器依赖DriverManagerDataSource.
2)在xml配置文件中配置事务管理模板:TransactionTemplate;
注意:事务管理模板依赖事务管理器dataDourceTransactionManager.
3)将事务模板注入service
在service中声明一个transactionTemplate对象,通过该对象的excute(agr)方法,在传参数的地方声明一一个TransactionCallbackWithoutResult类的匿名内部类,重写接口中的方法,把需要进行事务管理的代码放在该方法中即可。
缺点:
1)违背了开闭原则;
2)职责不单一;
3)硬编码。
配置文件代码:
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置TransactionTemplate -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="dataSourceTransactionManager"></property>
</bean>
(2)声明式事务(重点)
A.以xml配置方式配置事务
以aop的形式配置在xml配置文件中,没有入侵性,更方便拓展。
需引入的约束:beans、aop、tx
1)在xml配置文件中配置事务管理器:DataSourceTransactionManager;
注意:事务管理器依赖DriverManagerDataSource.
2)进行aop配置:
a.确定切点:通常情况下service下所有的方法都要进行事务管理。
b.将切点和切面及通知产生关联。
注意:>>这里通知和切面都由spring提供。
>>这里的通知即是:事务的开启、事务的提交、事务的回滚,由spring封装。
c.配置spring中定义好的通知,在通知中定义每个方法的事务的定义属性。如isreadyonly等。
注意:一般在此处配置一个name=”*”,避免有的方法没有配置上事务管理。
代码示例:
<!-- 配置TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 声明spring中定义好的通知 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query*"/>
<tx:method name="transfer"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<!--定义切点 -->
<aop:pointcut expression="execution(* com.chen.service.AccountServiceImpl.*(..))" id="accountServicePointCut"/>
<!-- 与切面中的通知产生联系:此处的切面就时事务管理器,通知就是开始执行,提交及回滚等 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="accountServicePointCut"/>
</aop:config>
B.以注解的方式配置事务
需引入的约束:beans、tx
1)在xml配置文件中配置事务管理器:DataSourceTransactionManager;
注意:事务管理器依赖DriverManagerDataSource.
2)配置事务的注解驱动(注入事务管理器)
3)在需要添加事务的方法上添加注解:@Transactional;
注意:
a.@Transactional注解既可以声明在方法上,也可以声明在类上;
b.当注解添加在类上的时候,表示该类下的所有方法都使用类上定义的事务详情;
c.如果类上和方法上都添加了注解,以方法上的注解为准(靠近原则)。
代码示例:
<!-- 配置TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- -->
<tx:annotation-driven transaction-manager="transactionManager"/>