spring 事务控制
一. 通常web程序的访问流程是: 用户访问->controller->service->dao. 一个业务的成功,调用的service是执行成功的,意味着service中调用的所有的dao是执行成功的. 所以事务应该在service层统一控制.
1.1 事务控制的基本概念
编程式事务控制:
自己手动控制事务,叫做编程式事务控制:
JDBC: connection.setAutoCommite(false); 设置手动控制事务
Hibernate: session.beginTransaction() 开启一个事务
细粒度的事务控制: 可以对指定的方法,指定的方法的某几行添加事务控制
(比较灵活,但是开发起来比较繁琐,每次都要开启,提交,回滚操作)
声明式事务控制:
Spring提供了对事务的管理, 这个就叫声明式事务管理。
Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦。
Spring声明式事务管理,核心实现就是基于Aop。
【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。】
(因为aop拦截的是方法。)
Spring声明式事务管理器类:
Jdbc技术:DataSourceTransactionManager
Hibernate技术:HibernateTransactionManager
1.2 Spring框架的事务管理相关的类和API
1. PlatformTransactionManager接口 -- 平台事务管理器.(真正管理事务的类)。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类!
2. TransactionDefinition接口 -- 事务定义信息.(事务的隔离级别,传播行为,超时,只读)
3. TransactionStatus接口 -- 事务的状态
4. 总结:上述对象之间的关系:平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中
5. PlatformTransactionManager接口中实现类和常用的方法
1. 接口的实现类
* 如果使用的Spring的JDBC模板或者MyBatis框架,需要选择DataSourceTransactionManager实现类
* 如果使用的是Hibernate的框架,需要选择HibernateTransactionManager实现类
2. 该接口的常用方法
* void commit(TransactionStatus status)
* TransactionStatus getTransaction(TransactionDefinition definition)
* void rollback(TransactionStatus status)
6. TransactionDefinition
1. 事务隔离级别的常量
* static int ISOLATION_DEFAULT -- 采用数据库的默认隔离级别
* static int ISOLATION_READ_UNCOMMITTED
* static int ISOLATION_READ_COMMITTED
* static int ISOLATION_REPEATABLE_READ
* static int ISOLATION_SERIALIZABLE
2. 事务的传播行为常量(不用设置,使用默认值)
* 先解释什么是事务的传播行为:解决的是业务层之间的方法调用!!
* PROPAGATION_REQUIRED(默认值) -- A中有事务,使用A中的事务.如果没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
* PROPAGATION_SUPPORTS -- A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.
* PROPAGATION_MANDATORY -- A中有事务,使用A中的事务.如果A没有事务.抛出异常.
* PROPAGATION_REQUIRES_NEW(记)-- A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)
* PROPAGATION_NOT_SUPPORTED -- A中有事务,将A中的事务挂起.
* PROPAGATION_NEVER -- A中有事务,抛出异常.
* PROPAGATION_NESTED(记) -- 嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚(选择回滚到保存点或者是最初始状态)
二. spring 事务控制的使用
2.1 XML方式配置事务控制
1. 步骤一:引入开发包
c3p0-0.9.1.2.jar
commons-logging-1.1.3.jar
mysql-connector-java-5.1.7-bin.jar
spring-aop-4.1.6.RELEASE.jar
spring-aspects-4.1.6.RELEASE.jar
spring-beans-4.1.6.RELEASE.jar
spring-context-4.1.6.RELEASE.jar
spring-core-4.1.6.RELEASE.jar
spring-expression-4.1.6.RELEASE.jar
spring-jdbc-4.1.6.RELEASE.jar
spring-tx-4.1.6.RELEASE.jar
2. 步骤二:配置事务管理器
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
3. 步骤三:配置事务增强
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--
name :绑定事务的方法名,可以使用通配符,可以配置多个。
propagation :传播行为
isolation :隔离级别
read-only :是否只读
timeout :超时信息
rollback-for:发生哪些异常回滚.
no-rollback-for:发生哪些异常不回滚.
-->
<!-- 哪些方法加事务 -->
<tx:method name="get*" read-only="true"></tx:method>
<tx:method name="find*" read-only="true"></tx:method>
<tx:method name="*" read-only="false"></tx:method>
</tx:attributes>
</tx:advice>
4. 步骤四:配置AOP的切面:哪些方法需要事务控制
<!-- 配置AOP切面产生代理 -->
<aop:config>
<aop:pointcut expression="execution(* com.it.service.impl.DeptServiceImpl.*(..))" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
* 注意:如果是自己编写的切面,使用<aop:aspect>标签,如果是系统制作的,使用<aop:advisor>标签。
5. 步骤5:编写测试类
package com.it.c_transaction;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.it.entity.Dept;
import com.it.service.DeptService;
public class TransactionDemo {
//xml方式使用事务控制
@Test
public void testTransaction(){
ApplicationContext aContext = new ClassPathXmlApplicationContext("bean.xml");
DeptService service = (DeptService) aContext.getBean("deptService");
Dept dept = new Dept();
dept.setDeptName("技术部");
service.save(dept);
System.out.println("保存成功!");
}
}
主要类的配置
实体类Entity:
Dept.java
package com.it.entity;
public class Dept {
private int deptId;
private String deptName;
public int getDeptId() {
return deptId;
}
public void setDeptId(int deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
Repository: DeptDaoImpl.java
package com.it.dao.impl;
import org.springframework.jdbc.core.JdbcTemplate;
import com.it.dao.DeptDao;
import com.it.entity.Dept;
public class DeptDaoImpl implements DeptDao{
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void save(Dept dept) {
// TODO Auto-generated method stub
String sql = "insert into t_dept(deptName) values(?)";
jdbcTemplate.update(sql,dept.getDeptName());
}
}
service层: DeptServiceImpl.java
package com.it.service.impl;
import com.it.dao.DeptDao;
import com.it.entity.Dept;
import com.it.service.DeptService;
public class DeptServiceImpl implements DeptService{
private DeptDao deptDao;
public void setDeptDao(DeptDao deptDao) {
this.deptDao = deptDao;
}
@Override
public void save(Dept dept) {
// TODO Auto-generated method stub
deptDao.save(dept);
//int i = 10 / 0;
//deptDao.save(dept);
}
}
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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 数据源对象:c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring_jdbc?useUnicode=true&characterEncoding=utf8"></property>
<property name="user" value="root"></property>
<property name="password" value="1l9o9v0e"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!-- jdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="userDao" class="com.it.dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 3. dao实例 -->
<bean id="deptDao" class="com.it.dao.impl.DeptDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 4. service实例 -->
<bean id="deptService" class="com.it.service.impl.DeptServiceImpl">
<property name="deptDao" ref="deptDao"></property>
</bean>
<bean id="annoDeptService" class="com.it.service.impl.anno.DeptServiceImplAnno">
<property name="deptDao" ref="deptDao"></property>
</bean>
<!-- spring声明式事务配置 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"></tx:method>
<tx:method name="find*" read-only="true"></tx:method>
<tx:method name="*" read-only="false"></tx:method>
</tx:attributes>
</tx:advice>
<!-- aop配置:哪些方式需要拦截 -->
<aop:config>
<aop:pointcut expression="execution(* com.it.service.impl.DeptServiceImpl.*(..))" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
测试事务控制
TransactionDemo.java
package com.it.c_transaction;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.it.entity.Dept;
import com.it.service.DeptService;
public class TransactionDemo {
//xml方式使用事务控制
@Test
public void testTransaction(){
ApplicationContext aContext = new ClassPathXmlApplicationContext("bean.xml");
DeptService service = (DeptService) aContext.getBean("deptService");
Dept dept = new Dept();
dept.setDeptName("技术部");
service.save(dept);
System.out.println("保存成功!");
}
}
当运行测试方法时,成功的插入一条记录;当我们在service中加入: int i = 10/0;时,运行
测试方式,发现没有记录插入到数据库,这正是使用了事务管理,进行了rollback回滚操作.
2.2 使用注解方式进行事务管理
2.2.1
XML文件中添加配置
<!-- 注解方式的声明式事务管理 -->
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.it.service.impl.anno"></context:component-scan>
<!-- 1.开启注解事务 -->
<tx:annotation-driven transaction-manager="txManager" />
创建另外的service文件
DeptServiceImplAnno.java
package com.it.service.impl.anno;
import org.springframework.transaction.annotation.Transactional;
import com.it.dao.DeptDao;
import com.it.entity.Dept;
import com.it.service.DeptService;
public class DeptServiceImplAnno implements DeptService{
private DeptDao deptDao;
public void setDeptDao(DeptDao deptDao) {
this.deptDao = deptDao;
}
@Transactional(
value="txManager"
//noRollbackFor = ArithmeticException.class
)
@Override
public void save(Dept dept) {
// TODO Auto-generated method stub
deptDao.save(dept);
int i = 10 / 0;
deptDao.save(dept);
}
}
进行测试
//注解方式使用事务控制
@Test
public void testTransactionAnno(){
ApplicationContext aContext = new ClassPathXmlApplicationContext("bean.xml");
DeptService service = (DeptService) aContext.getBean("annoDeptService");
Dept dept = new Dept();
dept.setDeptName("行政部");
service.save(dept);
System.out.println("保存成功!");
}
可以实现和XML配置同样的作用.