一、事务的概念
1.什么是事务
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。
2.事务的特性
(1)原子性
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
(2)一致性
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
(3)隔离性
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
(4)持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
3.不考虑隔离性产生的问题
脏读,不可重读,虚读、幻读
二、Spring事务管理api
1.Spring事务管理两种方式
第一种:编程事务管理
第二种:声明事务管理:
(1)基于xml配置文件实现
(2)基于注解实现
2.PlatFromTransactionManager事务管理器
(1)Spring为不同的持久化框架提供了不同的PlatFromTransactionManager接口实现
事务 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTansactionManager | 使用Spring JDBC或MyBatis进行持久化数据时使用 |
org.springframework.orm.hibernate5.HibernateTansactionManager | 使用Hibernate5.0版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTansactionManager | 使用JPA版本进行持久化数据时使用 |
org.springframework.jdo.JdoTansactionManager | 当持久化机制是Jdo时使用 |
org.springframework.transaction.jta.JtaTansactionManager | 使用一个JTA实现事务管理,在一个事务跨越多个资源时必须使用 |
(2)首先配置事务管理器(创建对应的事务管理对象)
三、举例:搭建转账环境
1.创建数据库表,添加数据
2.创建service和dao类,完成注入关系
(1)service层又叫业务逻辑层
(2)dao层,持久层,单纯对数据库操作层,在dao层不添加业务
(3)现需求:酱萌烦要把补助500转账给jxs
酱萌烦少500
jxs多500
代码示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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://127.0.0.1:3306/mydb2"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 创建jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource对象 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建OrdersDao对象 -->
<bean id="ordersDao" class="com.jxs.orders.OrdersDao">
<!-- 注入jdbcTemplate对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 创建OrderService对象 -->
<bean id="orderService" class="com.jxs.orders.OrderService">
<!-- 注入odersDao对象 -->
<property name="ordersDao" ref="ordersDao"></property>
</bean>
</beans>
package com.jxs.orders;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Created by jiangxs on 2018/3/30.
*/
public class OrdersDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void reduceAccount(String accountName, int money) {
String sql = "update account " +
" set salary=salary-?" +
" where accountName=?";
int row = jdbcTemplate.update(sql, money, accountName);
System.out.println(row);
}
public void increaseAccount(String accountName, int money) {
String sql = "update account " +
" set salary=salary+?" +
" where accountName=?";
int row = jdbcTemplate.update(sql, money, accountName);
System.out.println(row);
}
}
package com.jxs.orders;
/**
* Created by jiangxs on 2018/3/30.
*/
public class OrderService {
private OrdersDao ordersDao;
public void setOrdersDao(OrdersDao ordersDao) {
this.ordersDao = ordersDao;
}
public void changeOrders(String from, String to, int money) {
ordersDao.reduceAccount(from, money);
ordersDao.increaseAccount(to,money);
}
}
测试代码:
package com.jxs.orders;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by jiangxs on 2018/3/30.
*/
public class TestOrders {
@Test
public void testOrderService() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
OrderService service = (OrderService) context.getBean("orderService");
String from = "酱萌烦";
String to = "jxs";
int money = 500;
service.changeOrders(from, to, money);
}
}
3.产生问题
如果酱萌烦少了500,出现异常,而jxs不会多500,钱丢失了。
4.解决
添加事务解决,出现异常时进行回滚操作
(1)配置文件的方式进行添加事务
第一步:配置事务管理器
第二步:配置事务增强
第三步:配置切面
代码示例:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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://127.0.0.1:3306/mydb2"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 第一步 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 第二步 配置事务增强 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<!-- 做事务操作 -->
<tx:attributes>
<!-- 设置进行事务操作的方法的匹配规则 -->
<!-- 对以change开头的方法进行增强 -->
<tx:method name="change*"/>
</tx:attributes>
</tx:advice>
<!-- 第三步 配置切面 -->
<aop:config>
<!-- 切入点 -->
<!-- id为自定义名称,expression为表达式-->
<aop:pointcut id="pointcut1" expression="execution(* com.jxs.orders.OrderService.*(..))"/>
<!-- 切面 -->
<!-- 增强写之前配置事务增强的id -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>
<!-- 创建jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource对象 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建OrdersDao对象 -->
<bean id="ordersDao" class="com.jxs.orders.OrdersDao">
<!-- 注入jdbcTemplate对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 创建OrderService对象 -->
<bean id="orderService" class="com.jxs.orders.OrderService">
<!-- 注入odersDao对象 -->
<property name="ordersDao" ref="ordersDao"></property>
</bean>
</beans>
OrdersDao.java不变,在OrderService.java中引入异常:
orderService.java代码:
package com.jxs.orders;
/**
* Created by jiangxs on 2018/3/30.
*/
public class OrderService {
private OrdersDao ordersDao;
public void setOrdersDao(OrdersDao ordersDao) {
this.ordersDao = ordersDao;
}
public void changeOrders(String from, String to, int money) {
ordersDao.reduceAccount(from, money);
// 引入异常
int a = 10/0;
ordersDao.increaseAccount(to,money);
}
}
测试代码:
package com.jxs.orders;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by jiangxs on 2018/3/30.
*/
public class TestOrders {
@Test
public void testOrderService1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");
OrderService service = (OrderService) context.getBean("orderService");
String from = "酱萌烦";
String to = "jxs";
int money = 500;
service.changeOrders(from, to, money);
}
}
由于配置文件中增加了事务,当程序出现异常时会进行回滚操作,从而防止数据库出现错误的操作。
(2)声明事务管理(注解)
第一步:配置事务管理器
第二步:配置事务注解
第三步:在要使用事务的方法所在类上面添加注释
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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://127.0.0.1:3306/mydb2"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</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>
<!-- 创建jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource对象 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建OrdersDao对象 -->
<bean id="ordersDao" class="com.jxs.orders.OrdersDao">
<!-- 注入jdbcTemplate对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 创建OrderService对象 -->
<bean id="orderService" class="com.jxs.orders.OrderService">
<!-- 注入odersDao对象 -->
<property name="ordersDao" ref="ordersDao"></property>
</bean>
</beans>
使用事务的类为OrderService,需在其上添加注解:
package com.jxs.orders;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by jiangxs on 2018/3/30.
*/
@Transactional
public class OrderService {
private OrdersDao ordersDao;
public void setOrdersDao(OrdersDao ordersDao) {
this.ordersDao = ordersDao;
}
public void changeOrders(String from, String to, int money) {
ordersDao.reduceAccount(from, money);
// 引入异常
int a = 10/0;
ordersDao.increaseAccount(to,money);
}
}