1.为什么使用事务?
为了保证数据的完整性和一致性,对数据操作要保证数据的安全
2.什么是事务?
将一组操作数据的SQL作为一个整体提交,要么都执行,要么都不执行
3..事务的特性
- 原子性
- 将多条sql语句作为一个整体,不可分割
- 一致性
- 数据操作前后,整体保持一致
- 例如转账,一方钱少,另一方钱多,保持平衡,总额不变
- 数据操作前后,整体保持一致
- 隔离性
- 多个事务并发操作同一数据时,保证事务时隔离开的,相互不会受到干扰,保证数据安全
- 持久性
- 事务操作数据的结果永久的保存到持久化存储器
4..事务的隔离级别设置
- 通过Connection接口的方法设置事务级别
void setTransactionIsolation(int level) throws SQLException
5.事务隔离级别
- 未提交读 1
- int TRANSACTION_READ_UNCOMMITTED=1
- 已提交读 2
- int TRANSACTION_READ_COMMITTED=2
- 重复读 4
- int TRANSACTION_REPEATABLE_READ=4
- 序列化/不可并发 8
- int TRANSACTION_SERIALIZABLE=8
6.数据默认的隔离级别
Mysql 4
oracle 2
7.每个隔离能解决的问题
- 不适用事务会出现的问题
- 数据丢失,脏读,不可重复读,幻读
- 使用事务隔离级别后,每个隔离界别能解决的问题
事务隔离级别 | 数据丢失 | 脏读 | 不可重复读 | 幻读 |
TRANSACTION_READ_UNCOMMITTED(1) | 解决 | 未解决 | 未解决 | 未解决 |
TRANSACTION_READ_COMMITTED(2) | 解决 | 解决 | 未解决 | 未解决 |
TRANSACTION_REPEATABLE_READ(4) | 解决 | 解决 | 解决 | 未解决 |
TRANSACTION_SERIALIZABLE(8) | 解决 | 解决 | 解决 | 解决 |
8.每个级别的含义和作用
- 未提交读
- 一个事务读取到了另一个事务还没有提交事务的数据
- 已提交读
- 一个事务读取到了另一个事务已经提交事务的数据
- 重复读
- 一个事务只能重复读取当前事务的操作数据,而不能读取到别的事务未提交或已提交的数据
- 序列化/不可并发
- 解决了所有问题
- 幻读
- 一个事务正在进行某种统计,其它事务又进行数据的添加,删除或修改等操作,导致两次事务的统计结果不一致,就产生了幻读
- Select * from xxx where id < 5 for update //行级锁
9.事务管理方式
编程式事务
使用原生的JDBC API进行事务管理
[1]获取数据库连接Connection对象
[2]取消事务的自动提交
[3]执行操作
[4]正常完成操作时手动提交事务
[5]执行失败时回滚事务
[6]关闭相关资源
Connection con = .... ;
try{
con.setTransactionIsolation(4);
con.setAutoCommit(false); //开启事务
//执行一条或多条SQL
con.commit(); //提交事务
} catch(Exception e){
con.rollback(); //回滚事务
} finally{
con.close();
}
声明式事务
通过相关配置,给程序增加事务操作
Spring为了管理底层的事务,提供了PlatformTransactionManager接口,并提供了相关实现类
DataSourceTransactionManager Spring与JdbcTemplate框架集成时,提供事务管理
HibernateTransanctionManager Spring与Hibernate框架集成时,提供的事务管理
10.声明式事务使用
1) 了解需求
2) 创建表结构
3) 搭建环境
①导包
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.3.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
mysql-connector-java-5.1.7-bin.jar
c3p0-0.9.1.2.jar
②创建beans.xml
-
- 引用外部资源文件:jdbc.properties
<!-- 加载外部属性资源文件 -->
<context:property-placeholder location="classpath:/jdbc.priperties"/>
-
- 声明c3p0
<!-- 声明c3p0数据源对象 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="initialPoolSize" value="2"></property>
<property name="minPoolSize" value="5"></property>
<property name="maxPoolSize" value="20"></property>
</bean>
-
- 声明JdbcTemplate
<!-- 声明jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
③测试c3p0
④创建Dao接口和实现类
⑤创建Service接口和实现类
⑥测试Service接口方法
无事务情况:可能出现数据不一致情况
有事务情况:采用Spring的声明式事务解决问题
[1]声明事务管理器对象
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
[2]启用基于注解的声明式事务
<!-- 开启基于注解的声明式事务
transaction-manager="transactionManager" 默认值.
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
11.事务属性
1)事务传播行为
方法被调用时,事务的开启方式
一共有7种传播方式:
propagation=Propagation.REQUIRED 默认值
表示一个方法被调用时,如果已经存在事务,则加入这个事务,否则,开启一个新事物
propagation=Propagation.REQUIRES_NEW
表示一个方法被调用时,不管调用的方法有无事务,被调用的方法总会为自己开启一个新事物
2)事务隔离级别
isolation=Isolation.DEFAULT
表示与数据库的默认隔离级别一致
3)事务回滚策略
框架默认的回滚策略不适合项目开发,希望有异常就回滚事务
rollbackFor=Exception.class 设置遇到的指定类型进行事务回滚
noRollbackFor 设置遇到指定的类型不进行回滚事务
4) 事务超时属性
timeout=3 设置事务的超时时间,单位:s
5) 只读属性
readOnly=true
一般查询数据时,需设置为true,数据库底层会对查询进行优化处理,提高效率
12.基于XML文件的声明式事务
<!-- 基于XML的声明式事务配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.atguigu..service.*Service.*(..))" id="txPointcutId"/>
<aop:advisor advice-ref="adviceId" pointcut-ref="txPointcutId"/>
</aop:config>
<tx:advice id="adviceId" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="purchase"
propagation="REQUIRED"
isolation="DEFAULT"
rollback-for="java.lang.Exception"
timeout="3" />
<tx:method name="update*" propagation="REQUIRED"
isolation="DEFAULT"
rollback-for="java.lang.Exception"
timeout="3" />
<tx:method name="query*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>