做网站的工作有发展空间没有安全工程师
news/
2025/9/27 7:35:57/
文章来源:
做网站的工作有发展空间没有,安全工程师,网站备案名字要怎么写,360网站认证域名什么是事务#xff1f;
我们在开发企业应用时#xff0c;对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中#xff0c;任何一步操作都有可能发生异常#xff0c;异常会导致后续操作无法完成#xff0c;此时由于业务逻辑并未正确…什么是事务
我们在开发企业应用时对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中任何一步操作都有可能发生异常异常会导致后续操作无法完成此时由于业务逻辑并未正确的完成之前成功操作数据的并不可靠需要在这种情况下进行回退。
事务的作用就是为了保证用户的每一个操作都是可靠的事务中的每一步操作都必须成功执行只要有发生异常就回退到事务开始未进行操作的状态。
事务管理是Spring框架中最为常用的功能之一我们在使用Spring Boot开发应用时大部分情况下也都需要使用事务。
快速入门
在Spring Boot中当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager。所以我们不需要任何额外配置就可以用Transactional注解进行事务的使用。
我们以之前实现的《用spring-data-jpa访问数据库》的示例Chapter3-2-2作为基础工程进行事务的使用常识。
在该样例工程中若对该数据访问方式不了解可先阅读该文章我们引入了spring-data-jpa并创建了User实体以及对User的数据访问对象UserRepository在ApplicationTest类中实现了使用UserRepository进行数据读写的单元测试用例如下
RunWith(SpringJUnit4ClassRunner.class)SpringApplicationConfiguration(Application.class)public class ApplicationTests { Autowired private UserRepository userRepository; Test public void test() throws Exception { // 创建10条记录 userRepository.save(new User(AAA, 10)); userRepository.save(new User(BBB, 20)); userRepository.save(new User(CCC, 30)); userRepository.save(new User(DDD, 40)); userRepository.save(new User(EEE, 50)); userRepository.save(new User(FFF, 60)); userRepository.save(new User(GGG, 70)); userRepository.save(new User(HHH, 80)); userRepository.save(new User(III, 90)); userRepository.save(new User(JJJ, 100)); // 省略后续的一些验证操作 }}可以看到在这个单元测试用例中使用UserRepository对象连续创建了10个User实体到数据库中下面我们人为的来制造一些异常看看会发生什么情况。
通过定义User的name属性长度为5这样通过创建时User实体的name属性超长就可以触发异常产生。
Entitypublic class User { Id GeneratedValue private Long id; Column(nullable false, length 5) private String name; Column(nullable false) private Integer age; // 省略构造函数、getter和setter}修改测试用例中创建记录的语句将一条记录的name长度超过5如下name为HHHHHHHHH的User对象将会抛出异常。
// 创建10条记录userRepository.save(new User(AAA, 10));userRepository.save(new User(BBB, 20));userRepository.save(new User(CCC, 30));userRepository.save(new User(DDD, 40));userRepository.save(new User(EEE, 50));userRepository.save(new User(FFF, 60));userRepository.save(new User(GGG, 70));userRepository.save(new User(HHHHHHHHHH, 80));userRepository.save(new User(III, 90));userRepository.save(new User(JJJ, 100));执行测试用例可以看到控制台中抛出了如下异常name字段超长
2016-05-27 10:30:35.948 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1406, SQLState: 220012016-05-27 10:30:35.948 ERROR 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data truncation: Data too long for column name at row 12016-05-27 10:30:35.951 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 1406, SQLState: HY0002016-05-27 10:30:35.951 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data too long for column name at row 1org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement此时查数据库中创建了name从AAA到GGG的记录没有HHHHHHHHHH、III、JJJ的记录。而若这是一个希望保证完整性操作的情况下AAA到GGG的记录希望能在发生异常的时候被回退这时候就可以使用事务让它实现回退做法非常简单我们只需要在test函数上添加Transactional注解即可。
TestTransactionalpublic void test() throws Exception { // 省略测试内容}再来执行该测试用例可以看到控制台中输出了回滚日志Rolled back transaction for test context
2016-05-27 10:35:32.210 WARN 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1406, SQLState: 220012016-05-27 10:35:32.210 ERROR 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data truncation: Data too long for column name at row 12016-05-27 10:35:32.213 WARN 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 1406, SQLState: HY0002016-05-27 10:35:32.213 WARN 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data too long for column name at row 12016-05-27 10:35:32.221 INFO 5672 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test context [DefaultTestContext1d7a715 testClass ApplicationTests, testInstance com.didispace.ApplicationTests95a785, testMethod testApplicationTests, testException org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement, mergedContextConfiguration [MergedContextConfiguration11f39f9 testClass ApplicationTests, locations {}, classes {class com.didispace.Application}, contextInitializerClasses [], activeProfiles {}, propertySourceLocations {}, propertySourceProperties {}, contextLoader org.springframework.boot.test.SpringApplicationContextLoader, parent [null]]].org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement再看数据库中User表就没有AAA到GGG的用户数据了成功实现了自动回滚。
这里主要通过单元测试演示了如何使用Transactional注解来声明一个函数需要被事务管理通常我们单元测试为了保证每个测试之间的数据独立会使用Rollback注解让每个单元测试都能在结束时回滚。而真正在开发业务逻辑时我们通常在service层接口中使用Transactional来对各个业务逻辑进行事务管理的配置例如
public interface UserService { Transactional User login(String name, String password); }事务详解
上面的例子中我们使用了默认的事务配置可以满足一些基本的事务需求但是当我们项目较大较复杂时比如有多个数据源等这时候需要在声明事务时指定不同的事务管理器。对于不同数据源的事务管理配置可以见《Spring Boot多数据源配置与使用》中的设置。在声明事务时只需要通过value属性指定配置的事务管理器名即可例如Transactional(valuetransactionManagerPrimary)。
除了指定不同的事务管理器之后还能对事务进行隔离级别和传播行为的控制下面分别详细解释
#### 隔离级别
隔离级别是指若干个并发的事务之间的隔离程度与我们开发时候主要相关的场景包括脏读取、重复读、幻读。
我们可以看org.springframework.transaction.annotation.Isolation枚举类中定义了五个表示隔离级别的值
public enum Isolation { DEFAULT(-1), READ_UNCOMMITTED(1), READ_COMMITTED(2), REPEATABLE_READ(4), SERIALIZABLE(8);}DEFAULT这是默认值表示使用底层数据库的默认隔离级别。对大部分数据库而言通常这值就是READ_COMMITTED。READ_UNCOMMITTED该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读因此很少使用该隔离级别。READ_COMMITTED该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读这也是大多数情况下的推荐值。REPEATABLE_READ该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。SERIALIZABLE所有的事务依次逐个执行这样事务之间就完全不可能产生干扰也就是说该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
指定方法通过使用isolation属性设置例如
Transactional(isolation Isolation.DEFAULT)传播行为
所谓事务的传播行为是指如果在开始当前事务之前一个事务上下文已经存在此时有若干选项可以指定一个事务性方法的执行行为。
我们可以看org.springframework.transaction.annotation.Propagation枚举类中定义了6个表示传播行为的枚举值
public enum Propagation { REQUIRED(0), SUPPORTS(1), MANDATORY(2), REQUIRES_NEW(3), NOT_SUPPORTED(4), NEVER(5), NESTED(6);}REQUIRED如果当前存在事务则加入该事务如果当前没有事务则创建一个新的事务。SUPPORTS如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行。MANDATORY如果当前存在事务则加入该事务如果当前没有事务则抛出异常。REQUIRES_NEW创建一个新的事务如果当前存在事务则把当前事务挂起。NOT_SUPPORTED以非事务方式运行如果当前存在事务则把当前事务挂起。NEVER以非事务方式运行如果当前存在事务则抛出异常。NESTED如果当前存在事务则创建一个事务作为当前事务的嵌套事务来运行如果当前没有事务则该取值等价于REQUIRED。
指定方法通过使用propagation属性设置例如
Transactional(propagation Propagation.REQUIRED)代码示例
本文的相关例子可以查看下面仓库中的chapter3-3-1目录
Githubhttps://github.com/dyc87112/SpringBoot-LearningGiteehttps://gitee.com/didispace/SpringBoot-Learning
如果您觉得本文不错欢迎Star支持您的关注是我坚持的动力
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/919150.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!