spring是支持事务管理的,这样就不用直接在sql中使用begin transaction这样开始事务的命令了,spring帮你集成了
spring有两种事务,一种是编程式事务,即通过java代码来进行事务管理,一种是声明式事务,使用xml配置或者注解即可。
spring的事务接口是PlatFormTransationManager,实现类DataSourceTransactionManager
编程式事务。
(一)、使用jdbcTemplate进行事务管理
jdbcTemplate是spring中个一个数据库操做类,你可以理解为jdbc那种,只是好一点,稍微封装了一下,类似ibatis的sqlmapclient
1.首先引入事务处理类,可以配置在spring的配置xml,也可以配置在数据库相关的xml中
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
其中datasource就是数据库的一些连接属性,如下:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>root</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/test</value>
</property>
</bean>
2.java类中操作,测试事务回滚,这里故意设置teacher表的name不能为空,让其报错,从而测试是否回滚,执行事务了
ApplicationContext ctx=new ClassPathXmlApplicationContext("helloworld.xml");
DefaultTransactionDefinition dtd=new DefaultTransactionDefinition();
dtd.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); //设置事务隔离级别
dtd.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//获取spring事务处理类
PlatformTransactionManager tx=(PlatformTransactionManager) ctx.getBean("transactionManager");
TransactionStatus status=tx.getTransaction(dtd);
//获取数据源
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
try{
//执行sql,sql的执行可以换成使用ibatis,mybatis等框架
jdbcTemplate.execute(sql);
//如果执行sql,因为name不能为空,会报错,然后就回滚。
jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
tx.commit(status);
System.out.println("提交数据成功,状态--"+status);
}catch(Exception e){
tx.rollback(status);
System.out.println("数据回滚,状态--"+status);
}
这里要注意,由于是使用单元测试类执行的,故需要用ClassPathXmlApplicationContext来获取需要的这些bean,其实如果是启动项目了,可以直接使用注入的方式。
其中真正负责回滚和提交的是spring自带的transactionManager类,jdbcTemplate只不过负责执行sql,可以替换为ibatis或者其他的ORM框架。
3.需要测试的sql,以下没有说明,那么sql,sql2也是指这两个
private String sql="insert into student (name,sex,age,address,mobile) "
+ "values ('lisi','F',22,'AAAABCCC',158)";
private String sql2="insert into teacher (name,sex,age,address,mobile,classid) "
+ "values (?,?,?,?,?,?)";
(二)、tranactionTemplate事务管理
在spring中,还可以使用tranactionTemplate进行事务管理,上面那个太麻烦了,commit,rollback要自己来写,自己控制。tranactionTemplate就简单多了,你只需要负责业务操作,就是执行sql那部分,其他框架帮你
1、配置差不多,都是一个datasource,一个transactionManager,这里不重复了
2、java中调用,同样是在单元测试类中
//获取数据源
TransactionTemplate template=new TransactionTemplate(tx);
//事务隔离级别
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
//事务传播行为
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//带返回对象方法
template.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus arg0) {
//此处使用jdbcTemplate来执行sql,可以替换为其他方法,与事务无关
jdbcTemplate.execute(sql);
//如果执行sql,因为name不能为空,会报错,然后就回滚。
jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
return null;
}
});
其中tx就是上面配置的spring的transactionManager,你只需要负责在doInTransaction中执行你的业务逻辑
3、还有一个不带反回参数的execute方法,如下:
template.execute(new TransactionCallbackWithoutResult() {
//无返回对象事务方法,出错自动回滚,无需手动使用commit,rollback等
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
//此处使用jdbcTemplate来执行sql,可以替换为其他方法,与事务无关
jdbcTemplate.execute(sql);
//如果执行sql,因为name不能为空,会报错,然后就回滚。
jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
}
});
一般如果使用编程式事务,都是使用tranactionTemplate
声明式(配置式)事务
编程式事务还是对代码要修改,有侵入性,spring有更高级的,就是声明式事务,无需代码,只要配置
1.xml中配置如下,使用事务标签
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 这个name是指要切入的方法,以insert开头的都切入事务 -->
<tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
2.aop配置,因为这个就是用到了面向切面编程,故需要aop
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.xxxx.dao.TransactionDao.insert(..))"/>
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
pointcut表示切入点,这里表示TransactionDao类,以insert开头的方法都会加入事务,而不是insert的sql语句,不要混淆。
3.这个是被切入的那个类,负责数据库操作(业务操作)
<bean id="transaction" class="com.luohw.dao.TransactionDao">
<property name="dataSource" ref="dataSource"></property>
</bean>
4.业务逻辑:
public void insert(){
//在没有声明式事务前,sql执行正常,数据库有数据,sql2失败,没有插入
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.execute(sql);
jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
}
其中,由于用到tx事务标签,aop标签,配置文件开头需要引入以下东西:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
注意,这是指多的部分,之前还有spring必须的一个dtd和shema要引入。
这样对于业务代码,没有一点的侵入,完全只要配置就可以了,使用aop编程,spirng会自动动态代理,类似组合成一个新的方法,先开启事务,再执行你的sql。
使用注解注入事务,也是声明式的一种,更简洁好用。因为用aop,有可能返回太大,某个类,或者某个包所有的方法都加入了事务,有好处,一次操作多个,也有坏处,有些不需要事务的被你误杀。
注解的比较好控制范围,可以在方法,或者类上注解,那么就表示对应范围的都加了事务控制了。
1.在配置文件中,加入这个标签,表示支持事务注解
<tx:annotation-driven transaction-manager="transactionManager"/>
2.在需要加入事务控制的代码处,加入这个,此处以方法为例
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
public void insert2(){
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.execute(sql);
jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
}
看,是否非常简单。。。。
xml声明式事务,有传播行为(propagation),有以下值:
1.required——表示方法必须运行在事务中,如果没有,会创建一个新的事务,如果有,加入到这个事务中
2.mandatory——表示方法必须在事务中,如果没有,抛出异常
3.nested——如果存在事务,那么方法会在嵌套事务中运行?其他与required一样
4.never——表示方法不在事务中运行,如果有事务,那么抛出异常
5.not_supported——表示方法不在事务中运行,如果有事务,那么将被挂起(暂停),直到方法完成
6.required_new——表示方法必须在新的事务中运行, 如果当前存在事务,把当前事务挂起
7.supports——表示支持事务,如果有,也可以在事务中运行,也可以没有
传播行为用于确定方法嵌套方法时的事务调用情况。比如方法A调用方法B,两个方法都使用了注解事务。如果为required(一般都是这个),那么会把A,B合并到一起作为一个事务。如果是其他,看上面配置。
事务隔离级别(isolation)
1.read_uncommited——不提交读,未提交的数据,其他事务可以读,会出现脏读。比如B改了未提交,恰好A在事务中读,那么这个就是脏数据(A读了B修改前的数据)
2.read_commited——提交读,提交后的数据,其他事务才能读。会发生幻读和不可重复读
不可重复读, 一 个事务对同一行数据重复读取两次但是却得到了不同结果(有其他事务数据修改了)
幻读,事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(有其他事务数据插入了)
3.repeatable_read——可重复读,对于同一字段多次读取结果是一致的,除非数据被本身事务修改,会发生幻读。
4.serializable——序列化,最高级别(性能最差),什么问题读都不会发生
隔离级别 更新丢失 脏读取 不可重复读取 幻读
未提交读取 N Y Y Y
提交读取 N N Y Y
可重复读取 N N N Y
串行 N N N N