10、声明式事务
事务分为声明式和编程式两种:
声明式事务:声明式事务是指通过注解的形式对事务的各种特性进行控制和管理。
编码式(编程式)事务:指的是通过编码的方式实现事务的声明。
11.1、编码方式实现事务:
11.2、声明式事务环境搭建
11.2.1、准备测试数据库
##创建tx数据库
drop database if exists `tx`;
CREATE database `tx`;
##切换tx数据库
USE `tx`;
##删除用户表
DROP TABLE IF EXISTS `user`;
##创建用户表
CREATE TABLE `user` (
`id` int primary key auto_increment,
`username` varchar(50) NOT NULL,
`money` int(11) DEFAULT NULL
);
##插入数据
insert into `user`(`username`,`money`) values ('张三',1000),('李四',1000);
##删除图书表
drop table if exists `book`;
##创建图书表
create table `book`(
`id` int primary key auto_increment,
`name` varchar(500) not null,
`stock` int
);
##插入数据
insert into book(`name`,`stock`) values('java编程思想',100),('C++编程思想',100);
##查看数据
select * from book;
select * from user;
11.2.2、创建一个Java工程,导入Jar包
导入jar包:
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.3.jar
druid-1.1.9.jar
mysql-connector-java-5.1.37-bin.jar
spring-aop-4.3.18.RELEASE.jar
spring-beans-4.3.18.RELEASE.jar
spring-context-4.3.18.RELEASE.jar
spring-core-4.3.18.RELEASE.jar
spring-expression-4.3.18.RELEASE.jar
spring-jdbc-4.3.18.RELEASE.jar
spring-orm-4.3.18.RELEASE.jar
spring-test-4.3.18.RELEASE.jar
spring-tx-4.3.18.RELEASE.jar
配置jdbc.properties属性配置文件:
url=jdbc:mysql://localhost:3306/tx
user=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
配置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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置包扫描 -->
<context:component-scan base-package="com"></context:component-scan>
<!-- 加载jdbc.properties属性配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<property name="url" value="${url}"/>
<property name="driverClassName" value="${driverClassName}"/>
<property name="initialSize" value="${initialSize}" />
<property name="maxActive" value="${maxActive}"/>
</bean>
<!-- 配置jdbcTemplate工具类 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
11.3、测试Service的默认事务
实验1:测试service服务层的默认事务
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void updateBook() {
jdbcTemplate.update("update book set name = '图书表被修改了'");
}
}
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void updateUser() {
jdbcTemplate.update("update user set username = '用户表被修改了'");
}
}
@Service
public class TransactionService {
@Autowired
private UserDao userDao;
@Autowired
private BookDao bookDao;
public void multiUpdate() {
userDao.updateUser();
bookDao.updateBook();
}
}
异常的演示
@Service
public class TransactionService {
@Autowired
private UserDao userDao;
@Autowired
private BookDao bookDao;
public void multiUpdate() {
userDao.updateUser();
int i = 12 / 0 ;
bookDao.updateBook();
}
}
Spring事务引入的分析------PlatformTransactionManager类简单介绍
事务管理器实现类
由于我们使用的是数据库连接池访问数据库,所以事务管理器使用的是DataSourceTransactionManager。
11.4、使用Spring的注解声明事务管制
实验2:测试Spring的声明式事务
1、在需要事务的方法上加入注解。
/**
* @Transactional 表示当前方法有事务
*/
@Transactional
public void multiUpdate() {
userDao.updateUser();
int i = 12 / 0 ;
bookDao.updateBook();
}
2、配置事务管理器驱动
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
3、配置事务注解驱动
<!--
<tx:annotation-driven/> 开启注解的事务驱动 启用代理
transaction-manager="transactionManager" 配置事务管理器
如果事务管理器的id值是transactionManager,则事务管理器属性可以省略
-->
<tx:annotation-driven />
11.5、noRollbackFor和noRollbackForClassName测试不回滚的异常
实验3:noRollbackFor和noRollbackForClassName测试不回滚的异常
/**
* @Transactional 表示当前方法有事务<br/>
* 默认情况下。RuntimeException运行时异常和运行时子异常。都会回滚事务<br/>
* noRollbackFor=java.lang.ArithmeticException.class 表示算术异常不回滚事务<br/>
* noRollbackFor= {java.lang.ArithmeticException.class,java.lang.NullPointerException.class}<br/>
* noRollbackForClassName="java.lang.NullPointerException" 设置哪些类名的异常不回滚事务<br/>
*/
@Transactional(noRollbackForClassName="java.lang.NullPointerException")
public void multiUpdate() {
userDao.updateUser();
// int i = 12 / 0 ;
Object object = null;
System.out.println( object.toString() );
bookDao.updateBook();
}
11.6、自定义设置回滚异常
实验5:rollbackFor和rollbackForClassName回滚的异常
/**
* @throws FileNotFoundException
* @Transactional 表示当前方法有事务<br/>
* 默认情况下。RuntimeException运行时异常和运行时子异常。都会回滚事务<br/>
* rollbackFor=FileNotFoundException.class 设置当抛出FileNotFoundException异常时,回滚事务<br/>
* rollbackForClassName="java.io.FileNotFoundException" 设置当抛出FileNotFoundException异常时就会回滚事务<br/>
*/
@Transactional(rollbackForClassName="java.io.FileNotFoundException")
public void multiUpdate() throws FileNotFoundException {
userDao.updateUser();
int i = 12 ;
if (i == 12) {
throw new FileNotFoundException();
}
bookDao.updateBook();
}
11.7、事务的只读属性
实验4:测试readOnly只读属性
/**
* @throws FileNotFoundException
* @Transactional 表示当前方法有事务<br/>
* 默认情况下。RuntimeException运行时异常和运行时子异常。都会回滚事务<br/>
* readOnly=false 表示当前方法可以执行读操作。也可以执行写操作(指的是sql语句)。<br/>
* 修改、添加、删除、是写操作、select查询语句是读操作。<br/>
* readOnly=true 表示当前方法只能执行查询操作<br/>
*/
@Transactional(readOnly=true)
public void multiUpdate() throws FileNotFoundException {
userDao.updateUser();
bookDao.updateBook();
}
只读的方法执行修改语句报错。
11.8、事务超时属性timeout(秒为单位)
/**
* @throws FileNotFoundException
* @Transactional 表示当前方法有事务<br/>
* 默认情况下。RuntimeException运行时异常和运行时子异常。都会回滚事务<br/>
* timeout=3 表示3秒之后就超时,不允许再执行sql语句
*/
@Transactional(timeout=3)
public void multiUpdate() throws FileNotFoundException {
userDao.updateUser();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bookDao.updateBook();
}
当执行时间超过指定的秒钟后就会超时:
11.10、事务的传播特性propagation
什么是事务的传播行为:
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。事务的传播特性,有以下几种类型:
11.11、注解演示事物传播特性
UserService
BookService
TransactionService
实验1:大小事务传播特性都是REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void multlTransaction() {
@Transactional(propagation = Propagation.REQUIRED)
public void updateBook() {
@Transactional(propagation=Propagation.REQUIRED)
public void updateUser() {
实验2:大小事务传播特性都是REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser()
实验3:大事务是REQUIRED,小事务都是REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser()
实验4:大事务是REQUIRED,小1REQUIRED,小2REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRED)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser()
实验5:大事务是REQUIRED,小1REQUIRES_NEW,小2REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser()
12、xml配置式事务声明
复制原来注解事务管理的工程。去掉里面所有@Transactional的注解。
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 配置包扫描 -->
<context:component-scan base-package="com"></context:component-scan>
<!-- 加载jdbc.properties属性配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<property name="url" value="${url}"/>
<property name="driverClassName" value="${driverClassName}"/>
<property name="initialSize" value="${initialSize}" />
<property name="maxActive" value="${maxActive}"/>
</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:advice id="tx_advice" transaction-manager="transactionManager">
<tx:attributes>
<!--
tx:method 给某个方法配置事务属性
name就是方法的匹配规则(方法名)
精确匹配
-->
<tx:method name="updateUser" propagation="REQUIRED"/>
<!--
给save*方法配置事务属性
name="save*" 表示给save字符串打头的方法配置事务属性
-->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRES_NEW"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="multlTransaction" propagation="REQUIRES_NEW"/>
<!--
<tx:method name="*"/> 所有的方法
read-only="true" 剩下的方法都是查询操作。
-->
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--
aop:advisor 配置通知
-->
<aop:advisor advice-ref="tx_advice"
pointcut="execution(public * com.service..*Service*.*(..))"/>
</aop:config>
</beans>