说明:
(1)同理,Spring这个机构,也提供了【使用注解方式,配置声明式事务】的策略;
(2)本篇博客的IoC容器也采用了注解方式;
(3)本篇博客的代码的业务逻辑和【Spring JDBC与事务管理7:Spring声明式事务二:事务传播方式;】一样;只是本篇博客的声明式事务是采用注解形式来配置的而已;
(4)【注解形式】配置声明式事务很简单,主要包括两部分:
● 在applicationContext.xml中使用【<tx:annotation-driven transaction-manager="transactionManager"/>】 :启用注解形式声明事务;
● 然后,在需要事务控制的类、方法上使用【@Transactional注解】;
(5)在实际开发中,声明事务的常用配置和开发习惯,这个需要慢慢积累;
目录
(2)applicationContext.xml:配置公用类对象;启用注解形式声明事务;
(4)EmployeeService:一个简单的Service类;使用注解配置了声明式事务;
(5)BatchService:一个简单的Service类;使用注解配置了声明式事务;
一:【注解形式】配置声明式事务;
(1)pom.xml:引入所需依赖;
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.imooc.spring</groupId> <artifactId>jdbc</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.6.RELEASE</version> </dependency> <!--logback日志组件,Spring框架默认集成--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies> </project>
说明:
(1)没什么好说的,目前在pom.xml中,引入了【Spring Context模块】,【Spring JDBC模块】,【MySQL JDBC驱动】,【junit测试依赖】,【Spring Test模块】,【logback日志依赖】,【Spring AOP底层依赖的aspectjweaver】这几个;
(2)applicationContext.xml:配置公用类对象;启用注解形式声明事务;
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.imooc"/> <!--数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/springjdbctest?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/> <property name="username" value="root"/> <property name="password" value="12345"/> </bean> <!--JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 启用注解形式声明式事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
说明:
(1)【 <context:component-scan base-package="com.imooc"/>】意思是组件扫描,其作用是:确定扫描的包的范围是什么;(这个在【Spring IoC容器与Bean管理21:使用注解方式实现Spring IoC二:组件类型注解】中第一次接触过);
(2)即使我们这儿是使用注解方式配置声明式事务;但是,DriverManagerDataSource数据源类,JdbcTemplate核心类,DataSourceTransactionManager事务管理器类这三个系统底层的公用类,还是需要采用XML方式进行配置;
(3)【<tx:annotation-driven transaction-manager="transactionManager"/>】 :启用注解形式声明事务;写了这句话后,就能完成【注解形式声明式事务的底层设置】了;(为了能更好的理解这句话,可以快速参考【Spring AOP面向切面编程8:基于注解开发Spring AOP;】和【Spring JDBC与事务管理6:Spring声明式事务一:声明式事务配置;】)
(3)EmployeeDao类:一个简单的Dao类;
package com.imooc.spring.jdbc.dao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.util.List; import java.util.Map; @Repository public class EmployeeDao { @Resource private JdbcTemplate jdbcTemplate; public Employee findById(Integer eno){ String sql = "select * from employee where eno = ?"; //查询单条数据 Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class)); return employee; } public List<Employee> findByDname(String dname){ String sql = "select * from employee where dname = ?"; //查询复合数据 List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class)); return list; } public List<Map<String, Object>> findMapByDname(String dname){ String sql = "select eno as empno , salary as s from employee where dname = ?"; //将查询结果作为Map进行封装 List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname}); return maps; } public void insert(Employee employee){ String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)"; //利用update方法实现数据写入操作 jdbcTemplate.update(sql,new Object[]{ employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate() }); } public int update(Employee employee){ String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?"; int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()}); return count; } public int delete(Integer eno){ String sql = "delete from employee where eno = ?"; return jdbcTemplate.update(sql, new Object[]{eno}); } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
说明:
(1)这儿我们也使用注解方式配置IoC容器了;PS:为了少点麻烦,要注意如下这种的名字的对应:
(2)EmployeeDao没什么好说的,就是使用注解形式配置IoC,然后使用注解完成对象的注入;
(4)EmployeeService:一个简单的Service类;使用注解配置了声明式事务;
package com.imooc.spring.jdbc.service; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Date; @Service //声明式事务核心注解 //放在类上,将声明式事务配置应用于当前类所有方法,默认事务传播为 REQUIRED @Transactional public class EmployeeService { @Resource private EmployeeDao employeeDao; @Resource private BatchService batchService; @Transactional(propagation = Propagation.NOT_SUPPORTED , readOnly = true) public Employee findById(Integer eno){ return employeeDao.findById(eno); } public void batchImport() { for (int i = 1; i <= 10; i++) { if(i==3){ throw new RuntimeException("意料之外的异常"); } Employee employee = new Employee(); employee.setEno(8000 + i); employee.setEname("员工" + i); employee.setSalary(4000f); employee.setDname("市场部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } public void startImportJob(){ batchService.importJob1(); if(1==1){ throw new RuntimeException("意料之外的异常"); } batchService.importJob2(); System.out.println("批量导入成功"); } public EmployeeDao getEmployeeDao() { return employeeDao; } public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public BatchService getBatchService() { return batchService; } public void setBatchService(BatchService batchService) { this.batchService = batchService; } }
说明:
(1)对于EmployeeService,我们也使用注解方式配置其IoC;
(2)在类上使用【@Transactional注解】,传播方式默认为REQURIED;那么这个在类上的配置,会应用到本类的所有方法上;
(3)在方法上使用【@Transactional注解】,则该方法优先使用在本方法上配置的事务传播方式;
(4)即【@Transactional注解】可以用在类上,也可以用在方法上;
● 写在类上的【@Transactional注解】,会作用在本类的所有方法上;
● 写在方法上的【@Transactional注解】, 只对当前方法生效;
● 在加载是时候,写在方法上的【@Transactional注解】比写在类上的【@Transactional注解】优先级高;
(5)BatchService:一个简单的Service类;使用注解配置了声明式事务;
package com.imooc.spring.jdbc.service; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; import com.sun.xml.internal.ws.developer.Serialization; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Date; @Service @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true) public class BatchService { @Resource private EmployeeDao employeeDao; @Transactional(propagation = Propagation.REQUIRES_NEW) public void importJob1(){ for (int i = 1; i <= 10; i++) { Employee employee = new Employee(); employee.setEno(8000 + i); employee.setEname("研发部员工" + i); employee.setSalary(4000f); employee.setDname("研发部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } @Transactional(propagation = Propagation.REQUIRES_NEW) public void importJob2(){ for (int i = 1; i <= 10; i++) { Employee employee = new Employee(); employee.setEno(9000 + i); employee.setEname("市场部员工" + i); employee.setSalary(4500f); employee.setDname("市场部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } public EmployeeDao getEmployeeDao() { return employeeDao; } public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } }
说明:
(1)对于BatchService,我们也使用注解方式配置其IoC;
(2)在类上使用【@Transactional注解】;因为按照业务需求,BatchService中的方法都需要在独立的事务中运行,所以在BatchService的类上使用如下配置:
(6)测试;
![]()