在开发中需要操作数据库,进行增、删、改操作的过程中属于一次操作,如果在一个业务中需要更新多张表,那么任意一张表的更新失败,整个业务的更新就是失败,这时那些更新成功的表必须回滚,否则业务会出错,这时就要用到事务,即这个业务的操作属于一个事务,事务具有原子性、隔离性、一致性、持续性。这时便用到了事务,事务控制的目的是保证一组操作要么全部成功,要么全部失败。spring提供了对事务的支持,在spring中主要有两种方式使用事务,一、编程式事务控制;二、声明式事务控制。

一、编程式事务控制

所谓编程式事务控制即通过编写代码的方式实现事务的控制。

spring为了方便处理事务,提供了事务管理器,对事务的控制归根到底是通过事务管理器进行控制,在spring中所有的事务控制必须要有事务管理器。下面是一个编程式事务控制的例子,实现账户之间的转账,我们把对事务的控制放在系统的service层(分为controller层、service层、DAO层)来处理,下面是我的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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" >
<!--spring 自动检测--> <context:component-scan base-package="com.cn.study.day5" />
<!---->
<context:property-placeholder location="classpath:db.properties"/>
<!----> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${db.driver}</value>
</property>
<property name="url">
<value>${db.url}</value>
</property>
<property name="username">
<value>${db.username}</value>
</property>
<property name="password">
<value>123456</value>
</property>
</bean> <bean id="dao" class="com.cn.study.day5.service.inter.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property>
</bean> <!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property> </bean>
<!--事务管理器模板 方便使用事务-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean> </beans>

  配置了事务管理器,这里使用DataSourceTransactionManager,事务管理器有一个dataSource属性必须配置,这里使用ref属性引用上边的。有了事务管理器之后要使用事务还是比较麻烦,spring又提供了事务管理器模板,我们配置事务管理器模板,事务管理器模板需要一个事务管理器属性,我们引用上边的事务管理器。至此关于编程式的事务控制的配置文件已经准备完毕,下面进行编程式开发。由于,我们把事务控制放在service层,下面是我的service层的代码,

package com.cn.study.day5.service.inter.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate; import com.cn.study.day5.service.inter.AccountDaoInter;
import com.cn.study.day5.service.inter.AccountServiceIter; @Component
public class AccountServiceImpl implements AccountServiceIter {
@Autowired
private AccountDaoInter adi;
@Autowired
private TransactionTemplate tt;
//转账方法,由out向in转money元
@Override
public void transfer(final String out, final String in, final double money) {
// TODO Auto-generated method stub //使用事务管理器模板进行事务控制
tt.execute(new TransactionCallbackWithoutResult() { @Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// TODO Auto-generated method stub
adi.outMoney(out, money);
//一个异常,使用了事务控制,在出现了异常之后,事务会回滚
int i = 1 / 0;
adi.inMoney(in, money);
}
}); } }

由于是面向接口编程,这里我只贴出了service层的实现,使用了自动扫描机制(扫描类、属性上的注解@Component、@Autowired),transfer方法是实现转账的方法,首先从一个账户转出,然后转入另一个账户,使用事务管理器模板的execute方法,需要一个TransactionCallBack的实例,这里使用匿名内部类的方式,把要执行的方法放在doInTransactionWithoutResult中执行,保证了事务的控制。

使用这种方式可以保证事务控制,但是在实际开发过程当中这种方式对代码的改动太大,不符合低侵入开发原则,所有这种方式在开发中几乎很少用到,用的最多的是声明式的事务控制。

二、声明式事务控制

声明式事务控制又分为三种方式,一、基于TransactionProxyFactoryBean代理的声明式事务控制;二、使用AOP的声明式事务控制;三、基于@Transactional注解的声明式事务控制。

1、基于TransactionProxyFactoryBean的声明式事务控制

TransactionProxyFactoryBean是事务的代理类,spring会为目标类生成一个代理,具体的配置如下,

<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置业务层代理-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="accountServiceImpl"></property>
<property name="transactionManager" ref="transactionManager"></property>
<property name="transactionAttributes">
<props>
<prop key="transfer"></prop>
</props>
</property>
</bean>

这里只贴出了事务管理器和业务层代理的配置,剩余的数据源和业务类的配置可以执行配置,前面说到无论使用哪种方式配置事务管理,都需要使用事务管理器。重点看业务层代理,配置的class属性为TransactionProxyFactoryBean,需要配置三个属性:target(要代理的具体业务层实现类)、transactionManager(事务管理器)、transactionAttributes(要拦截的业务层方法)。配置完成之后,便可以进行测试,测试代码如下,

ApplicationContext ac=getApplicationContext();
AccountServiceIter asi=(AccountServiceIter)ac.getBean("accountServiceProxy");
asi.transfer("aa", "cc", 10d);

通过getApplicationContext()方法获得了ApplicationContext实例,然后获得accountServiceProxy的实例,这里获得的不是AccountServiceImpl的实例而是代理的实例对象,因为使用代理,代理了实际的业务类,所有这里不能再使用实际的类而应是代理类。

使用这种方式的需要为每一个需要使用事务的业务类配置一个代理比教麻烦,所以在开发过程中这种方式几乎不用。

2、使用AOP的声明式事务控制

这种方式是在开发过程中使用的比较多的一种,配置如下,

<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务增强-->
<tx:advice id="advicer" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置切点、事务通知-->
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.cn.study.day555.service.inter.impl.*.*(..))"/>
<aop:advisor advice-ref="advicer" pointcut-ref="myPointcut"/>
</aop:config>

配置了事务增强<tx:advice>配置对要增强的方法的事务的传播行为等,配置<aop:config>配置切点和对应的事务通知,这样就完成了AOP的声明式事务控制。

3、基于@Transactional注解

使用@Transactional注解需要再配置文件中开启对这个注解的扫描:<tx:annotation-driven transaction-manager="transactionManager" />,引用了事务管理器,然后就可以使用@Transactional注解,此注解可以使用在类上,也可以使用在方法上,使用在类上即对此类的所有方法都起作用,使用在方法上则表示对单个方法起作用,还可以配置一些属性,放在另一篇文章中进行解释。

通过对以上四种配置事务的方式的描述,其中声明式方式中的第二种方式使用比较普通,对代码的侵入比较小,第三种因为配置简单,也比较常用,但是需要在业务类或方法上加@Transcational注解,对代码有一定的侵入。

有不正之处欢迎指出,谢谢!

spring入门(三)【事务控制】的更多相关文章

  1. Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制

    Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...

  2. Spring入门5.事务管理机制

    Spring入门5.事务管理机制 20131126 代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 回顾之前的知识,Spring 最为核心的两个部分 ...

  3. Spring中的事务控制

    Spring中事务控制的API介绍 PlatformTransactionManager 此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法 我们在开发中都是使用它的实现类: 真正 ...

  4. Spring MVC一事务控制问题

    在近期一个项目中用了Spring MVC作为控制层框架,但却出现了一个让人非常费解的问题:事务控制. Spring MVC的配置文件名称为:springMVC-servlet.xml,内容例如以下: ...

  5. 阶段3 2.Spring_10.Spring中事务控制_9 spring编程式事务控制1-了解

    编程式的事物控制,使用的情况非常少,主要作为了解 新建项目 首先导入包坐标 复制代码 这里默认值配置了Service.dao和连接池其他的内容都没有配置 也就说现在是没有事物支持的.运行测试文件 有错 ...

  6. Spring入门(三):通过JavaConfig装配bean

    上一篇博客中,我们讲解了使用组件扫描和自动装配实现自动化装配bean,这也是最好的使用方式. 但是某些场景下,我们可能无法使用自动装配的功能,此时就不得不显式的配置bean. 比如我们引用了一个第三方 ...

  7. <Redis> 入门三 事务

    Redis事务是什么 1.可以一次执行多个命令,本质是一组命令的集合. 2.一个事务中的所有命令都会被序列化,按顺序串行化执行而不会被其他命令插入,不许加塞. 意味着redis在事务执行的过程中,不允 ...

  8. spring入门(三) 使用spring mvc

    1.建立project / module 新建空的project:springMvcStudy 新建module:type maven-webapp,名字mvcStudy 2.为module设置Sou ...

  9. Spring的 JDBCTemplate和声明式事务控制

    Spring 中的 JdbcTemplate[会用] JdbcTemplate 概述 它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装.spring 框架为我们提供 ...

  10. spring入门3-jdbcTemplate简单使用和声明式事务

    1.JdbcTemplate简单使用 1.1.引入相关依赖包 <dependency> <groupId>mysql</groupId> <artifactI ...

随机推荐

  1. spring事务管理器设计思想(二)

    上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...

  2. Java抽象类的总结

    什么是抽象类: 当你在定义一个父级的类的时候,往往在父级内的方法没有添加任何内容,这时候如果你在子类里面调用父级的时候,万一在子类之中类名或者方法名没有写正确,会出现不执行的情况,但是这种情况默认是不 ...

  3. Atitti 大话存储读后感 attilax总结

    Atitti 大话存储读后感 attilax总结 1.1. 大话存储中心思想(主要讲了磁盘文件等存储)1 1.2. 最耐久的存储,莫过于石头了,要想几千万年的存储信息,使用石头是最好的方式了1 1.3 ...

  4. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  5. Java_IO流_File类配合使用(其中用到了递归)

    第一:Java File类的功能非常强大,利用Java基本上可以对文件进行所有的操作.以下对Java File文件操作以及常用方法进行简单介绍 案例1:遍历出指定目录下的文件夹,并输出文件名 stat ...

  6. myeclipse转到函数定义的方法去

    转到函数的定义CTRl+鼠标左击 myeclipse自动补全的快捷键 alt+/

  7. SVN:服务器资源删掉,本地添加时和删掉的名字同名出现One or more files are in a conflicted state.

    异常处理汇总-开发工具  http://www.cnblogs.com/dunitian/p/4522988.html

  8. 详解 ML2 Core Plugin(I) - 每天5分钟玩转 OpenStack(71)

    我们在 Neutron Server 小节学习到 Core Plugin,其功能是维护数据库中 network, subnet 和 port 的状态,并负责调用相应的 agent 在 network ...

  9. iOS开发之调用系统打电话发短信接口以及程序内发短信

    在本篇博客开头呢,先说一下写本篇的博客的原因吧.目前在做一个小项目,要用到在本应用程序内发验证码给其他用户,怎么在应用内发送短信的具体细节想不大起来了,于是就百度了一下,发现也有关于这方面的博客,点进 ...

  10. Why is HttpContext.Current null after await?

    今天在对项目代码进行异步化改进的时候,遇到一个奇怪的问题(莫笑,以前没遇过),正如标题一样,HttpContext.Current 在 await 异步执行之后,就会变为 null. 演示代码: pu ...