事务是逻辑上的一组操作的集合,要么全部成功,要么全部失败。在实际的业务场景中,事务的应用场景范围很广泛,例如转账,订单,支付等
事务有四大特性:
- 原子性:事务不可分割
- 一致性:事务执行的前后,数据完整性保持一致.
- 隔离性:一个事务执行的时候,不应该受到其他事务的打扰
- 持久性:一旦结束,数据就永久的保存到数据库.
在实际的开发中,可能会出现以下几种由数据操作引起的异常情况:
如果不考虑隔离性:
- 脏读:一个事务读到另一个事务未提交数据
- 不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致
- 虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致
事务存在几种隔离级别:
- 未提交读:以上情况都有可能发生。
- 已提交读:避免脏读,但不可重复读,虚读是有可能发生。
- 可重复读:避免脏读,不可重复读,但是虚读有可能发生。
- 串行化:避免以上所有情况.
后端分层开发中,事务应用于Service层中。spring提供了事务管理API
PlatformTransactionManager
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现
使用Spring JDBC或iBatis 进行持久化数据时使用(重点)
- org.springframework.jdbc.datasource.DataSourceTransactionManager
使用Hibernate进行持久化数据时使用
- org.springframework.orm.hibernate.HibernateTransactionManager
使用JPA进行持久化时使用
- org.springframework.orm.jpa.JpaTransactionManager
当持久化机制是Jdo时使用
- org.springframework.jdo.JdoTransactionManager
使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
- org.springframework.transaction.jta.JtaTransactionManager
TransactionDefinition
ISOLATION_DEFAULT:默认级别. Mysql --> repeatable_read | Oracle -->> read_commited
级别如下:
- ISOLATION_READ_UNCOMMITTED : 读未提交
- ISOLATION_READ_COMMITTED : 读已提交
- ISOLATION_REPEATABLE_READ :可重复读
- ISOLATION_SERIALIZABLE :串行化
事务的传播行为是用来解决实际开发中的问题
传播行为是指:解决业务层调用事务的关系。
A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.
A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
A,B 非事务的方式运行,A有事务,就会挂起当前的事务.
PROPAGATION_NEVER: 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED: 如果当前事务存在,则嵌套事务执行
基于SavePoint技术.
A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.
TransactionStatus
是否有保存点
是否是一个新的事务
事务是否已经提交
关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态,状态由TransactionStatus记录。
spring支持声明式事务管理,只需要配置文件,不需要额外的编写代码
演示如下:
创建account表并插入两条数据
CREATE TABLE `account` (`id` int(11) NOT NULL AUTO_INCREMENT,`money` double DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT INTO `account` VALUES ('1', '1000');
INSERT INTO `account` VALUES ('2', '1000');
新建项目略
导入pom.xml依赖
4.0.0 com.wmj SpringTX 1.0-SNAPSHOT 8 8 org.springframework spring-context 5.3.24 org.springframework spring-jdbc 5.3.24 org.aspectj aspectjweaver 1.9.9.1 org.springframework spring-tx 5.3.24 org.mybatis mybatis 3.5.11 org.mybatis mybatis-spring 2.0.7 mysql mysql-connector-java 8.0.31 com.alibaba druid 1.2.9 org.projectlombok lombok 1.18.24 log4j log4j 1.2.17 junit junit 4.13.2 test
配置mybatis-config.xml
配置日志
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
配置数据源
db.username = 帐号
db.password = 密码
db.url = jdbc:mysql:///库名?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.driverClassName = com.mysql.cj.jdbc.Driver
搭建后端业务代码
控制层
package com.wmj.controller;import com.wmj.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:04*/
@Controller
public class TestController {@Autowiredprivate TestService service;public void testTx(Integer id1,Integer id2,Double money){service.transferMoney(id1,id2,money);}
}
业务层接口及其实现类
package com.wmj.service;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:10*/public interface TestService {void transferMoney(Integer id1, Integer id2, Double money);
}
package com.wmj.service.impl;import com.wmj.mapper.TestMapper;
import com.wmj.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:11*/
@Service
public class TestServiceImpl implements TestService {@Autowiredprivate TestMapper mapper;@Overridepublic void transferMoney(Integer id1, Integer id2, Double money) {mapper.dmoney(id1,money);int a = 1/0;mapper.amoney(id2,money);}
}
持久层接口及其xml数据访问
package com.wmj.mapper;import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:12*/
@Repository
public interface TestMapper {Integer dmoney(@Param("id1") Integer id1, @Param("money") Double money);Integer amoney(@Param("id2") Integer id2, @Param("money") Double money);
}
update account set money = money - #{money} where id = #{id1} update account set money = money + #{money} where id = #{id2}
配置spring核心配置文件application.xml
package com.test;import com.wmj.controller.TestController;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:02*/public class Demo1 {@Testpublic void testTX(){ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("application.xml");TestController controller = (TestController)applicationContext.getBean("testController");controller.testTx(1,2,200.00);}
}
执行结果:
当没有遇到异常时,转账能正常执行,不在演示,此处演示当遇到异常时的事务效果
代码执行前数据库:


当遇到异常,代码终止执行,此时数据库

转账并没有进行提交,此时说明我们的事务配置成功!
spring给我们提供了事务注解开发的方式,大大减少了代码编写,十分方便
创建项目配置基本与上方相同,我们将application.xml内容进行修改如下:
将业务实现类改造如下:
package com.wmj.service.impl;import com.wmj.mapper.AccountMapper;
import com.wmj.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;/*** @Transactional 配置事务* 可以写在类上,也可以写在方法上*/
//@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
@Service
package com.wmj.service.impl;import com.wmj.mapper.TestMapper;
import com.wmj.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:11*/
/*** @Transactional 配置事务* 可以写在类上,也可以写在方法上*/
//@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
@Service
public class TestServiceImpl implements TestService {@Autowiredprivate TestMapper mapper;@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)@Overridepublic void transferMoney(Integer id1, Integer id2, Double money) {mapper.dmoney(id1,money);int a = 1/0;mapper.amoney(id2,money);}
}
测试略
本次主要记录了spring的事务支持,及其两种开发方式。再会!!!