事务的四大基本特性:
事物的概述
⑴ 原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
⑵ 一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
⑶ 隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
⑷ 持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
关于事务的隔离级别我之前发布了一篇文章:https://mp.csdn.net/mdeditor/81487466
spring中事务的分类:
spring中事务可以分为编程式事务控制和声明式事务控制。
编程式事务控制
自己手动控制事务,就叫做编程式事务控制。
Jdbc代码:
Conn.setAutoCommit(false); // 设置手动控制事务
Hibernate代码:
Session.beginTransaction(); // 开启一个事务
【细粒度的事务控制: 可以对指定的方法、指定的方法的某几行添加事务控制】
(比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚.)
声明式事务控制
Spring提供了对事务的管理, 这个就叫声明式事务管理。
Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦。
Spring声明式事务管理,核心实现就是基于Aop。
【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。】
(因为aop拦截的是方法。)
Spring声明式事务管理器类:
Jdbc技术:DataSourceTransactionManager
Hibernate技术:HibernateTransactionManager
手写编程式事务:
1. UserDao.java
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void add(String name, Integer age) {
String sql = "INSERT INTO users(NAME, age) VALUES(?,?);";
int update = jdbcTemplate.update(sql, name, age);
System.out.println("updateResult:" + update);
}
}
2. UserService
/**
* 演示手动的对事务进行开启,提交,回滚操作
*/
@Service
public class UserService {
@Autowired
private UserDao userDao;
@AutoWired
private TransactionUtils transactionUtils;
public void add() {
try{
TransactionStatus begin = null;
userDao.add("lisi", 18);
int i=1/0;//发生异常
transactionUtils.commit(begin);
}catch(Exception ex){
//异常时rollback
transactionUtils.rollback(begin);
}
}
}
3. App 测试类
public class UserTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
}
}
4.手动事物管理类
@Component
public class TransactionUtils {
// 事物管理器
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
public TransactionStatus begin() {
//开启事务
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
return transaction;
}
public void commit(TransactionStatus transaction) {
//提交事务
dataSourceTransactionManager.commit(transaction);
}
public void rollback(TransactionStatus transaction) {
//回滚事务
dataSourceTransactionManager.rollback(transaction);
}
}
5. bean.xml (Spring务管理配置)
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解 -->
<context:component-scan base-package="cn.itcats"></context:component-scan>
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事物 -->
<bean id="DataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
但是这么做有一点不好,就是每次控制事务都要写很多重复代码,那么使用springAOP便是一个很好的解决思路。那么下面写一个声明式事务管理,这也是spring事务的核心,采用AOP+环绕通知+异常通知+编程式事务组成了声明式事务管理
XML方式实现:
1. UserDao.java
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void add(String name, Integer age) {
String sql = "INSERT INTO users(NAME, age) VALUES(?,?);";
int update = jdbcTemplate.update(sql, name, age);
System.out.println("updateResult:" + update);
}
}
2. UserService
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
userDao.add("lisi", 18);
int i=1/0;//可能会发生异常
userDao.add("zhangsan", 19);
}
}
3. App 测试类
public class UserTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
}
}
4.手动事物管理类
@Component
public class TransactionUtils {
// 事物管理器
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
public TransactionStatus begin() {
//开启事务
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
return transaction;
}
public void commit(TransactionStatus transaction) {
//提交事务
dataSourceTransactionManager.commit(transaction);
}
public void rollback(TransactionStatus transaction) {
//回滚事务
dataSourceTransactionManager.rollback(transaction);
}
}
5. bean.xml (Spring务管理配置)
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解 -->
<context:component-scan base-package="cn.itcats"></context:component-scan>
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事物 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事物增强 -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="*" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcats.aop.*.*(..))"
id="pt" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
</aop:config>
</beans>
切面类:
public class Aop2 {
@Autowired
private TransactionUtils transactionUtils;
@AutoWired
private TransactionStatus begin;
public void begin() {
//System.out.println("......前置通知......");
}
public void after() {
//System.out.println(".......后置通知......");
}
public void afterReturning() {
//System.out.println(".......运行通知......");
}
public void afterThrowing() {
//System.out.println(".......异常通知......");
transactionUtils.rollback(begin);
}
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
begin = transactionUtils.begin();
System.out.println("我是环绕通知-开启事物");
//作用:放行被AOP所切的方法
proceedingJoinPoint.proceed();
transactionUtils.commit(begin);
System.out.println("我是环绕通知-提交事物");
}
}
若: 1/0在try catch中 则是可以向数据库添加数据的,因为它不会抛出异常。因为加入了try catch不会显示抛出异常,那么
proceedingJoinPoint.proceed();后的代码仍会执行,由于最后执行了commit(),则还是可以向数据库添加数据的。若显示抛出异常,非try catch,则执行完异常通知,proceed()后的代码不会执行。需要注意:使用声明式事务,不要使用try catch块,即业务逻辑层使用到声明式异常,不要使用try,要将异常直接抛出,一般在控制层捕获。
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
try {
userDao.add("lisi", 18);
int i=1/0; //发生异常
userDao.add("zhangsan", 19);
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结: 事务原理 AOP编程+(环绕前beginTransaction)环绕通知(环绕后commit)+异常通知(发生异常rollback)。
事务的七种传播行为:
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
public class Test {
LogService logService = new LogService();
Userdao userDao = new Userdao();
@Transactional
public void add(){
logService.log();
userDao.add("haha");
int i = 1/0;
}
}
class LogService{
public void log(){
//添加到数据库
}
}
class Userdao{
public void add(String name){
//添加到数据库
}
}
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务,有则用自己的事务。这是最常见的选择。
所有Propagation.REQUIRED
修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
默认使用的传播行为,虽log方法不存在事务,但Test类的中add()和LogService()中的log()使用同一个事务。logService.log()和userDao.add("haha")都不会更新到数据库,即使log方法也为PROPAGATION.REQUIRED两者都不会更新。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
public class Test {
LogService logService = new LogService();
Userdao userDao = new Userdao();
@Transactional(propagation=Propagation.REQUIRED)
public void add(){
logService.log();
userDao.add("haha");
int i = 1/0;
}
}
class LogService{
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void log(){
//添加到数据库
}
}
class Userdao{
public void add(String name){
//添加到数据库
}
}
logService.log()结果会添加到数据库,而userDao.add("haha")不会添加到数据库。
Propagation.REQUIRES_NEW
修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
PROPAGATION_SUPPORTS--如果当前有事务就用当前的,如果当前没有事务就用非事务运行。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_MANDATORY(强制)--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。