一,概述
1.1编程式事务控制
1)自己手动实现事务控制,就叫做编程式事务控制.
Jdbc代码:Connection.setAutoCommit(false);//设置手动控制事务
Hibernate代码:Session.beginTransaction();//开启一个事务
2)细粒度的事务控制:可以对指定的方法/指定方法的某几行代码添加事务控制.这种事务控制比较灵活,但开发起来比较繁琐,每次都要开启/提交/回滚.
1.2声明式事务控制
1)Spring提供了对事务的管理,这个就叫做声明式事务管理.
2)Spring提供了对事务控制的实现.开发者如果想使用Spring的声明式事务管理,只需要在配置文件中配置即可;不想使用时直接在配置文件中移除即可.Spring最大程度的实现了对事务控制的解耦.
3)Spring的声明式事务管理,核心实现就是基于AOP.
4)粗粒度的事务控制:只能给整个方法应用事务,不能对方法的某几行代码应用事务.(因为AOP拦截的是方法)
5)Spring声明式事务管理器类:
Jdbc-->DataSourceTransactionManager
Hibernate-->HibernateTransactionManager.
二,声明式事务管理
2.1准备工作
a)jar包准备:spring-core5个jar包,spring-aop4个jar包,spring-jdbc4个jar包.
2.2XML方式实现
a)Dept.java(setter和getter方法省略)
public class Dept {
public Dept(){
}
private int deptId;
private String deptName;
}
b)DeptDao.java
package com.bighuan.a_tx;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* dao实现,使用Spring对JDBC支持的功能
* @author bighuan
*
*/
public class DeptDao {
//容器注入JdbcTemplate对象
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void save(Dept dept){
String sql="insert into t_dept(deptName) values(?)";
jdbcTemplate.update(sql, dept.getDeptName());
}
}
c)DeptService.java
package com.bighuan.a_tx;
/**
* service层实现,调用dao
* @author bighuan
*
*/
public class DeptService {
//容器注入DeptDao对象
private DeptDao deptDao;
public void setDeptDao(DeptDao deptDao) {
this.deptDao = deptDao;
}
public void save(Dept dept){
//第一次调用
deptDao.save(dept);
//int i=1/0; //出现异常,整个Service.save(..)执行成功的要回滚
deptDao.save(dept);//第二次调用
}
}
d)bean.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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1,数据源对象: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://localhost:3306/hib_demo"></property>
<property name="user" value="root"></property>
<property name="password" value="abc"></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>
<!--2,JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3,DeptDao实例 -->
<bean id="deptDao" class="com.bighuan.a_tx.DeptDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 4,DeptService实例 -->
<bean id="deptService" class="com.bighuan.a_tx.DeptService">
<property name="deptDao" ref="deptDao"></property>
</bean>
<!-- ****Spring声明式事务管理配置**** -->
<!-- 5.1配置事务管理器类 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.2配置事务增强(如何管理事务?) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" read-only="false" />
<tx:method name="*find*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 5.3AOP配置:拦截哪些方法(切入点表达式)+应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.bighuan.a_tx.DeptService.*(..))" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
e)测试:
public class App {
private ApplicationContext ac=new ClassPathXmlApplicationContext("com/bighuan/a_tx/bean.xml");
@Test
public void testApp() throws Exception {
Dept dept=new Dept();
dept.setDeptName("test:开发部");
DeptService service=(DeptService) ac.getBean("deptService");
service.save(dept);
}
}
第一次:将DeptService.java中的错误代码注释掉,执行测试代码,发现数据插入了两条数据.
第二次:不注释掉代码,执行测试代码,不能插入数据;如果不使用事务控制的话,只能插入一条数据.
2.3注解方式实现
a)DeptDao.java
@Repository
public class DeptDao {
//容器注入JdbcTemplate对象
@Resource
private JdbcTemplate jdbcTemplate;
public void save(Dept dept){
String sql="insert into t_dept(deptName) values(?)";
jdbcTemplate.update(sql, dept.getDeptName());
}
}
b)DeptService.java
@Service
public class DeptService {
//容器注入DeptDao对象
@Resource
private DeptDao deptDao;
@Resource
private LogDao logDao;
@Transactional(readOnly=false,//读写事务
timeout=-1,//-1表示事务的超时时间不限制,最终由数据库底层来决定
//noRollbackFor=ArithmeticException.class,//遇到数学异常不回滚
propagation=Propagation.REQUIRED
)
public void save(Dept dept){
logDao.insertLog();
//第一次调用
deptDao.save(dept);
int i=1/0; //出现异常,整个Service.save(..)执行成功的要回滚
deptDao.save(dept);//第二次调用
}
}
c)LogDao.java
@Repository
public class LogDao {
@Resource
private JdbcTemplate jdbcTemplate;
@Transactional(propagation=Propagation.REQUIRED)
//@Transactional(propagation=Propagation.REQUIRES_NEW)
public void insertLog(){
jdbcTemplate.update("insert into log_ values('往dept表插入数据...')");
}
}
d)bean.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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1,数据源对象: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://localhost:3306/hib_demo"></property>
<property name="user" value="root"></property>
<property name="password" value="abc"></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>
<!--2,JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3,事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.bighuan.b_anno"></context:component-scan>
<!-- 注解方式实现事务:指定注解方式实现事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
注解方式相对于XML方式就简单的多.注解方式一定要开启注解扫描及声明注解方式实现事务.
e)测试:
public class App {
private ApplicationContext ac=new ClassPathXmlApplicationContext("com/bighuan/b_anno/bean.xml");
@Test
public void testApp() throws Exception {
Dept dept=new Dept();
dept.setDeptName("test:开发部");
DeptService service=(DeptService) ac.getBean("deptService");
service.save(dept);
}
}
测试思路与上面一样.
f)propagation=Propagation.REQUIRED与propagation=Propagation.REQUIRES_NEW的区别
前者表示:如果当前运行的方法,已经存在事务, 就会加入当前的事务.
后者表示:指定当前的方法必须在事务的环境下执行;如果当前运行的方法,已经存在事务,事务会挂起; 会始终开启一个新的事务,执行完后;刚才挂起的事务才继续运行.