回顾
先来回顾一下事务的相关知识吧;
事务的概念:
- 事务是一种SQL 语句的执行,要么成功,要么失败,不能出现部分成功,部分失败,具有原子性;
- 事务所有的SQL全部执行完,才能提交(commit)事务,将数据存储到磁盘;
- 事务执行过程中只要有SQL出现问题,那么事务就必须回滚到最初的状态;
事务的特征:ACID
- A:事务的原子性;事务是一个不可分割的整体,事务必须具有原子特征,事务操作时,要么全部执行,要么全部不执行;
- C:事务的一致性;一个事务执行之前和之后,数据库数据必须保持一致性状态;(比如银行转账时,金额总和不变);
- I:事务的隔离性;当两个或者多个事务并发执行时,为保证数据的安全,将一个事务内的操作和其他事务隔离起来,不被其他正在执行的事务看到;
- D:事务的持久性;事务完成之后,数据库保证数据库中的数据修改是永久性的,即使数据库出现故障,也能保证恢复数据;
事务隔离性使用不当会照成脏数据问题:
- 脏读:一个事务读取了另一个事务未提交的数据;(当事务A和事务B并发操作时,事务A更新数据后,事务B读取到A未提交的数据,此时事务A回滚,事务B就读取到了事务A未提交的无效的脏数据);
- 不可重复读:一个事务操作导致另一个事务读取到前后两次不同的数据;(例如,事务A和事务B进行并发操作,事务B查询读取数据后,事务A更新操作事务B读取的数据,此时事务B继续查询数据,会发现自己前后两次读取的数据结果不一致);
- 幻读:一个事务的操作导致另一个事务前后两次查询的结果的数量不同;(例如,事务A和事务B进行并发操作,当事务B查询读取数据后,事务A操作新增或删除了一条满足事务B的条件的数据,此时事务B再次进行查询到前一次不存在的数据或者前一次没有的数据),(事务B读取了事务A新增的内容或者读不到事务A删除的数据);
在JDBC中,定义了以下5种事务隔离级别:
- TRANSACTION_NONE。 表示不支持事务
- TRANSACTION_READ_UNCOMMITTED。未提交读。说明在提交前一个事务可以看到另一个事务的变化。这样读”脏”数据,不可重复读和虚读都是被允许的。
- TRANSACTION_READ_COMMITTED。已提交读。说明读取未提交的数据是不允许的。这个级别仍然允许不可重复读和虚读产生。
- TRANSACTION_REPEATABLE_READ。可重复读。说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。
- TRANSACTION_SERIALIZABLE。可序列化/串行化。是最高的事务级别,它防止读脏数据,不可重复读和虚读。
现在开始看看Spring是如何管理事务的吧,它有两种实现方式,即通过配置或注解实现;
Spring对事务管理的两种实现方式
下面写的都是伪代码啊,重在Spring对事务的管理是怎么实现的:
通过配置实现
使用步骤:
- 引入依赖;
- 创建相关的类;
- 创建配置文件;
- 使用;
一、引入依赖;
和前面几节用的依赖一样,略;
二、创建类
里面是伪代码,并没有进行真正的数据库操作;
public class StudentDao {
//插入多个学生是需要同时成功的,这就需要借助事务处理
public void add() {
//添加多个学生
System.out.println("事务处理中");
System.out.println("添加学生的方法");
}
}
三、创建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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置数据源 通过连接池c3p0来操作-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--配置数据连接的核心配置-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--第一步 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步 配置事务增强(AOP)即把事务当增强来处理-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--事务操作-->
<tx:attributes>
<!--设置进行事务操作的方法匹配 -->
<!--name标签:匹配方法 add*: 所有的以add开头的方法都要进行事务操作-->
<tx:method name="add*"/>
</tx:attributes>
</tx:advice>
<!--第三步:配置切面-->
<aop:config>
<!--切入点 实际需要被增强的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.tulun7.dao.StudentDao.add(..))"/>
<!--配置切面 将切入点进行增强的过程-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
<!--创建StudentDao对象,这个时候只要调用该对象的方法,就会进行事务处理-->
<bean id="studentDao" class="com.tulun7.dao.StudentDao"/>
</beans>
四、使用
public class TestDemo1 {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("事务/spring-transaction.xml");
StudentDao studentDao = (StudentDao) context.getBean("studentDao");
studentDao.add();
}
}
通过注解实现
注解实现就更简单了,所以说在Spring里面更推荐使用注解;
使用步骤:
- 引入依赖;
- 创建相关的类;
- 创建配置文件;
- 使用;
一、引入依赖
和前面几节用的依赖一样,不过需要注意的是,因为这是使用注解实现,所以对版本的要求比较高,以前我引入的依赖都是 4.1.7 版本的,然后我写完它一直报错,我检查了几遍觉得没错啊,但就是报错,最后搜博客,有人说是包没引,但需要的就这些jar包,也没错,最后有博主说,是版本问题,最后我将 4.1.7 改成 4.3.9 就对了,所以注意这里需要改版本号;
二、创建相关的类
还是一样,只不过这里需要添加注解@Transactional,如果需要对这个类进行事务操作,将@Transactional添加到类前面就可以了,这里我只需要对这个方法进行事务操作,所以我就只添加在了add方法前面:
public class StudentDao1 {
@Transactional
public void add() {
//添加多个学生
System.out.println("事务处理中");
System.out.println("添加学生的方法");
}
}
三、创建配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置数据源 通过连接池c3p0来操作-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--配置数据连接的核心配置-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--第一步 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步 开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--第三步 在对应的类或者方法上面添加注解@Transactional-->
<!--创建StudentDao对象,这个时候只要调用该对象的方法-->
<bean id="studentDao" class="com.tulun7.dao.StudentDao1"/>
</beans>
四、使用
public class TestDemo2 {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("事务/注解实现/spring.xml");
StudentDao1 studentDao1 = (StudentDao1) context.getBean("studentDao");
studentDao1.add();
}
}