SSM 框架学习: Spring 声明式事务 Published on Mar 19, 2024 in 日常 with 0 comment Java SpringFramework ### 1. 声明式事务概念  #### 1.1 编程式事务 编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。在 Java 中,通常使用事务管理器来实现编程式事务。 编程式事务的主要优点是灵活性高,可以按照自己的需求来控制事务的粒度、模式等,但是编写大量的事务代码容易出现问题,对代码的可读性和可维护性也有一定的影响。 编程式事务的实现方法主要存在以下缺陷: - 细节没有屏蔽,具体实现过程,所有细节都要程序员完成,比较繁琐。 - 代码复用性不高,每次都要编写代码导致代码没有实现复用。 #### 1.2 声明式事务 声明式事务是指通过注解或者 XML 配置的方式来控制事务的提交和回滚。开发者只需要添加配置即可,具体事务由第三方框架来实现。同时,声明式事务可以将事务的控制和业务逻辑分开,提高代码的可读性和可维护性。 即声明式事务可以通过配置文件或者注解来控制事务。 #### 1.3 事务管理器 - 选择一个合适的事务管理器实现加入到 IoC 容器 - 指定哪些方法需要添加事务 1、Spring 声明式事务对应依赖 - spring-tx:包含声明式事务实现的基本规范 - spring-jdbc:包含 DataSource 方式事务管理器实现类 `DataSourceTransactionManager` - spring-orm:包含其他持久层框架的事务管理器实现类,例如 Jpa 等。 2、DataSourceTransactionManager 类的主要方法: - doBegin():开启事务 - doSuspend(): 挂起事务 - doResume(): 恢复挂起的事务 - doCommit(): 提交事务 - doRollback(): 回滚事务 ### 2. 基于注解的声明式事务 #### 2.1 基本程序搭建 (1)pom 文件中引入需要的依赖。 ```xml org.springframework spring-jdbc 5.3.1 org.springframework spring-tx 5.3.1 ``` (2)通过 properties 文件的方式配置数据库 ```xml alen.url = jdbc:mysql://localhost:3306/ssm alen.driver = com.mysql.cj.jdbc.Driver alen.username = root alen.password = XXXX ``` (3)编辑对应的配置类 ```java @Configuration @ComponentScan("org.alen") @PropertySource("classpath:jdbc.properties") public class JavaConfig { @Value("${alen.driver}") private String driver; @Value("${alen.url}") private String url; @Value("${alen.username}") private String username; @Value("${alen.password}") private String password; // druid 连接池 @Bean public DataSource dataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driver); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } ``` (4)准备 DAO 层 ```java @Repository public class PersonDao { @Autowired private JdbcTemplate jdbcTemplate; public void updateNameById(String name, Integer id){ String sql = "UPDATE INFO SET NAME = ? WHERE ID = ? ; "; int rows = jdbcTemplate.update(sql, name, id); } public void updateAgeById(Integer age, Integer id){ String sql = "UPDATE INFO SET AGE = ? WHERE ID = ? ;" ; jdbcTemplate.update(sql, age, id); } } ``` (5)准备 Service 层 ```java @Service public class PersonService { @Autowired private PersonDao personDao; public void changeInfo(){ personDao.updateAgeById(100,1); System.out.println("----"); personDao.updateNameById("John", 1); } } ``` (6)搭建测试环境 ```java @SpringJUnitConfig(JavaConfig.class) public class SpringTxTest { @Autowired private PersonService personService; @Test public void test(){ personService.changeInfo(); } } ``` 效果如下:  #### 2.2 基本程序搭建 (1)在 IOC 中加入对事务管理的支持 ```java // 事务管理的支持 @Bean public TransactionManager transactionManager(DataSource dataSource){ // 内部要进行事务操作,基于 连接池 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); // 连接池对象 dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } ``` (2)在配置类 JavaConfig 上开启对事务注解的支持 `@EnableTransactionManagement` (3)在方法上添加事务 添加事务的位置有两个,如果加在类上,代表这个类下的所有方法都有事务。如果加在方法上,则代表该方法有事务。 方法代码修改如下: ```java @Transactional public void changeInfo(){ personDao.updateAgeById(200,1); // other code.. personDao.updateNameById("John", 44); } } ``` #### 2.3 事务属性:只读 对于一个查询操作来说,如果设置为只读,就能明确告诉数据库该操作不涉及写操作,这样可以优化效率。 ```java @Transactional(readOnly = true) public void queryInfo(){ // other code ... } } ``` #### 2.4 事务属性:超时时间 事务在执行过程中,有可能遇到一些问题导致程序卡住,从而长时间占用数据库资源。此时这个程序应该被回滚,撤消其已经做的操作,把资源让出来。 简单来说:超时回滚,释放资源。 ```java @Transactional(readOnly = false, timeout = 3) public void changeInfo() { try { personDao.updateAgeById(22,1); Thread.sleep(4000); personDao.updateNameById("John", 44); } catch (InterruptedException e) { throw new RuntimeException(e); } } ``` 这时运行该程序,会发生超时的相关异常。  如果在类上设置了事务属性,方法也设置了事务注解。那么,方法上的注解会覆盖掉类上的注解。 #### 2.5 事务属性:隔离级别 事务隔离级别指的是多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定: (1)读未提交:事务可以读取未被提交的数据,容易产生脏读等问题,实现简单但是不安全,一般不用。 (2)读已提交:事务只能读取已经提交的数据,但是可能引发不可重复读和幻读。(推荐) (3)可重复读:在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但有幻读的问题。 (4)串行化:最高的隔离级别,完全禁止并发。只允许一个事务执行完后,才能执行另一个事务。但是,效率低,不适合高并发。 通过事务的 `isolation` 枚举来进行设置。 ```java @Transactional(readOnly = false, timeout = 3, isolation = Isolation.READ_COMMITTED) public void changeInfo() { try { personDao.updateAgeById(22,1); Thread.sleep(4000); personDao.updateNameById("John", 44); } catch (InterruptedException e) { throw new RuntimeException(e); } } ``` 本文由 Alen 创作,采用 知识共享署名4.0 国际许可协议进行许可本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名最后编辑时间为: Apr 22, 2024 at 04:43 pm