Spring框架 - 声明式事务管理
目录
- Spring框架事务管理
- Spring声明式事务
- 相关的类和API
- Spring框架声明式事务管理
- 声明式事务管理
- 配置文件形式
- Bean管理的元素属性
- 实操案例
- 半注解形式(配置文件 + 注解)
- 常用的注解
- 实操案例
- 全注解形式
- 实操案例
Spring框架事务管理
Spring声明式事务
声明式事务是Spring框架提供的一种通过配置而非编程代码来管理事务的方式。
相关的类和API
-
PlatformTransactionManager接口:平台事务管理器- 该接口根据不同的持久层框架,选择不同的具体实现类:
- 接口方法:
void commit(TransactionStatus status)void rollback(TransactionStatus status)- 使用Spring框架的JDBC模板或者MyBatis框架,选择
DataSourceTransactionManager实现类 - 使用Hibernate框架,选择
HibernateTransactionManager实现类
-
TransactionDefinition接口,事务定义信息接口- 定义了事务隔离级别
- 定义了事务传播行为
Spring框架声明式事务管理
声明式事务管理
有三种形式:
- 配置文件形式
- 半注解形式(配置文件 + 注解)
- 全注解形式(注解)
配置文件形式
Bean管理的元素属性
<tx:advice>:事务通知配置id:事务通知标识符,用于其它地方引用transaction-manager:指定事务管理器Bean的id叫什么<tx:attributes>:定义方法级别的事务属性<tx:method>:为匹配的方法指定详细的事务行为规则name:匹配XX的方法isolation:隔离级别read-only:
<aop:config>:AOP通知配置<aop:advisor>:系统通知advice-ref:引用上面的事务pointcut:定义切入点表达式
<aop:advisor>与<aop:aspect> 的区别:
| 特性 | <aop:advisor> | <aop:aspect> |
|---|---|---|
| 用途 | 系统级通知 | 自定义业务通知 |
| 通知类型 | 通常使用Around通知 | 支持5种通知 |
| 配置方式 | 引用外部定义的Advice | 在aspect内部定义通知方法 |
| 灵活性 | 相对固定 | 更灵活,可自定义逻辑 |
实操案例
- 创建maven Java项目
- 引入依赖
<!-- Maven中配置Java版本 --> <properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target> </properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><!--有单元测试的环境,Spring5版本,Junit4.12版本--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- 连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!-- mysql驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!-- Spring整合Junit测试的jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version><scope>test</scope></dependency><!-- AOP联盟 --><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!-- Spring Aspects --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><!-- aspectj --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.0.2.RELEASE</version></dependency> </dependencies> - 创建实体类:Account类
- 添加id、name、money这三个成员变量(字段)
- 获取它们的getter/setter方法
- 创建持久层的接口类和实现类:AccountDao类和AccountDaoImpl类
package com.qcby.dao;public interface AccountDao {/*** 转账* @param name 转账人* @param money 金额*/public void outMoney(String name, Double money);/*** 收账* @param name 收账人* @param money 金额*/public void inMoney(String name, Double money); }package com.qcby.dao.impl;import com.qcby.dao.AccountDao; import org.springframework.jdbc.core.JdbcTemplate;public class AccountDaoImpl implements AccountDao {private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Overridepublic void outMoney(String name, Double money) {//编写sql语句String sql = "update account set money = money - ? where name = ?";jdbcTemplate.update(sql, money, name);}@Overridepublic void inMoney(String name, Double money) {String sql = "update account set money = money + ? where name = ?";jdbcTemplate.update(sql, money, name);} } - 创建业务层的接口来和实现类:AccountService类和AccountServiceImpl类
package com.qcby.service;public interface AccountService {/**** @param out 转账* @param in 收账* @param money 金额*/public void pay(String out, String in, Double money); }package com.qcby.service.impl;import com.qcby.dao.AccountDao; import com.qcby.service.AccountService;public class AccountServiceImpl implements AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/**** @param out 转账* @param in 收账* @param money 金额*/@Overridepublic void pay(String out, String in, Double money) {accountDao.outMoney(out,money);accountDao.inMoney(in,money);} } - 编写属性文件:jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///spring_db jdbc.username=username // 你的用户名 jdbc.password=userpassword // 你的密码 - 编写配置文件:applicationContext.xml
- 加载属性文件,并配置参数
- 配置平台事务管理器
- 配置事务通知(有Spring提供的切面类)
- 配置AOP增强
- 配置jdbc模板
- 配置Service
- 配置dao
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--配置文件形式--><!--使用标签方式--><!--加载属性文件--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!--配置平台事务管理器--><bean id="tran" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!--配置事务的通知(由Spring提供的切面类)--><tx:advice id="txAdvice" transaction-manager="tran"><tx:attributes><!--对pay进行增强,设置隔离级别,传播行为,超时时间--><tx:method name="pay" isolation="DEFAULT"/><tx:method name="find*" read-only="true"></tx:method></tx:attributes></tx:advice><!--配置AOP增强--><aop:config><!--Spring框架提供系统通知,使用advisor标签--><aop:advisor advice-ref="txAdvice" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.pay(..))"></aop:advisor></aop:config><!--配置jdbc模板--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!--配置Service--><bean id="accountService" class="com.qcby.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean><!--配置Dao--><bean id="accountDao" class="com.qcby.dao.impl.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean> </beans> - 创建测试类:AccountTest类
package com.qcby.text;import com.qcby.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** IOC:配置文件形式* JDBC模板,配置文件形式 + 属性文件* 声明式事务:配置文件形式(Spring框架提供AOP)*/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AccountTest {@Autowiredpublic AccountService accountService;@Testpublic void run(){accountService.pay("美羊羊","沸羊羊",100.0);} }
半注解形式(配置文件 + 注解)
常用的注解
@Transactional()注解:将一个方法或类标记为需要事务性执行- 也就是说,在方法上添加了这个注解,Spring会在这个方法上开始执行前自动开启一个数据库事务,方法完毕后提交事务,出现异常回滚事务。
isolation属性:设置事务隔离级别@EnableAspectJAutoProxy注解:开启事务注解@PropertySource("classpath:jdbc.properties")注释:读取属性文件
实操案例
- 前三步与上面配置文件形式一样
- 持久层种的实现类,加上注解
@Repository("accountDao2") public class AccountDaoImpl2 implements AccountDao {@Autowiredprivate JdbcTemplate jdbcTemplate;// ……其它代码与上面一致 } - 业务层种的实现类,加上注解
@Service("accountService2") // 设置事务级别 @Transactional(isolation = Isolation.DEFAULT) public class AccountServiceImpl2 implements AccountService{@Autowired@Qualifier("accountDao2")private AccountDao accountDao;// ……其它代码与上面一致 } - 属性文件内容不变
- 配置文件内容有修改:applicationContext2.xml
- 去掉配置Service、配置Dao、配置AOP增强的Bean管理代码,修改为开启注解扫描
- 去掉配置事务的通知(由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:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--配置文件 + 注解--><!--开启注解扫描--><context:component-scan base-package="com.qcby"><!-- 排除标注了@Configuration的类 --><context:exclude-filter type="annotation"expression="org.springframework.context.annotation.Configuration"/></context:component-scan><!--标签方式--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!--加载属性文件--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!--配置平台事务管理--><bean id="tean" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!--配置JDBC模板--><bean id="jdebTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!--开启事务注解支持--><tx:annotation-driven transaction-manager="tean"></tx:annotation-driven> </beans> - 创建测试类:AccountTest2类
package com.qcby.text;import com.qcby.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** IOC:配置文件形式 + 注解* JDBC模板,配置文件形式 + 属性文件* 声明式事务:配置文件形式 + 注解*/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class AccountTest2 {@Autowired@Qualifier("accountService2")public AccountService accountService;@Testpublic void run(){accountService.pay("美羊羊","沸羊羊",100.0);} }
全注解形式
实操案例
- 前3步与上面半注解形式一致
- 不在需要属性文件和配置文件,只需要创建配置类:SpringConfig类
- 配置数据库连接池
- 创建模板对象
- 创建平台事务管理对象
package com.qcby.tools;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.transaction.PlatformTransactionManager;import javax.annotation.Resource; import javax.sql.DataSource;@Configuration @ComponentScan("com.qcby") // 开启事务注解 @EnableAspectJAutoProxy public class SpringConfig {/*** 连接数据库* @return* @throws Exception*/@Bean("dataSource")public DataSource dataSource() throws Exception{// 创建连接池对象,Spring框架内置了连接池对象DriverManagerDataSource ds = new DriverManagerDataSource();// 设置4个参数ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql:///spring_db");ds.setUsername("root");ds.setPassword("root");return ds;}/*** 创建模板对象* @param dataSource* @return*/@Resource(name = "dataSource") // 不仅可以作用在属性上,也可以作用方法上@Bean("jdbcTemplate") // 把JdbcTemplate保存到IOC容器中public JdbcTemplate createJdbcTemplate(DataSource dataSource){JdbcTemplate template = new JdbcTemplate(dataSource);return template;}/*** 创建平台事务管理器对象* @param dataSource* @return*/@Resource(name = "dataSource")@Bean("manager")public PlatformTransactionManager createTransactionManager(DataSource dataSource){DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);return manager;} } - 创建测试类:AccountTest3类
package com.qcby.text;import com.qcby.service.AccountService; import com.qcby.tools.SpringConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** IOC全注解:注解类* JDBC模板全注解,注解类* 声明式事务:注解类*/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {SpringConfig.class}) public class AccountTest3 {@Autowired@Qualifier("accountService3")public AccountService accountService;@Testpublic void run(){accountService.pay("美羊羊","沸羊羊",100.0);} }
