Spring事务处理

Spring事务处理

概念回顾

1.事务:事务是逻辑上的一组操作,要么执行,要么不执行。
2.事务的特性ACID
原子性:事务是最小的处理单元,不允许分割。原子性要保证动作要么完成,要么完全不起作用。
一致性:执行事务前后,数据保持一致。系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。
持久性:一个事务被提交后,它对数据库中的数据的改变是持久的,即使数据库发生故障也不应该对其有影响。通常情况下,事务的结果被写到持久化存储器中。

Spring事务管理

1.所谓的事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。
Spring作为一个容器,并不做任何事务的具体实现, 具体实现由Hibernate,JTA等各个持久化机制所提供的相关平台框架的事务来实现。它只是提供了事务管理的接口PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC,Hibernate等都提供了对应的事务管理器。
2.Spring事务管理接口
PlatformTransactionManager:(平台)事务管理器
TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
TransactionStatus:事务运行状态

3.PlatformTransactionManager接口

Spring事务处理_第1张图片
Spring提供了很多内置事务管理器实现:

内置事务管理器 说明
DataSourceTransactionManager 位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理
HibernateTransactionManager 位于org.springframework.orm.hibernate3包中,提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate3.2+版本
JpaTransactionManager 位于org.springframework.orm.jpa包中,提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理
JtaTransactionManager 位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器
JdoTransactionManager 位于org.springframework.orm.jdo包中,提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理
OC4JjtaTransactionManager 位于org.springframework.transaction.jta包中,Spring提供的对OC4J10.1.3+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持
WebSphereUowTransactionManager 位于org.springframework.transaction.jta包中,Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持
WebLogicJtaTransactionManager 位于org.springframework.transaction.jta包中,Spring提供的对WebLogic8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持

几个常用的事务管理器配置
1.JDBC事务


<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
bean>

<tx:annotation-driven transaction-manager="txManager" />

2.Hibernate

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
bean>

3.JPA事务


    
bean>


    <property name="dataSource" ref="someDataSource"/>
bean>

4.JTA事务

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
   <property name="transactionManagerName" value="java:/TransactionManager" />
bean>

4.TransactionDefinition接口

事务管理器接口PlateformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到一个事务。这个方法里的事务就是TransactionDefinition类,这个类就定义了一些基本的事务属性。
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性主要包含了五个方面:隔离级别,传播行为,回滚规则,是否只读,事务超时。
先来看一下TransactionDefinition接口中的方法

public interface TransactionDefinition {
    // 返回事务的传播行为
    int getPropagationBehavior(); 
    // 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
    int getIsolationLevel(); 
    //返回事务的名字
    String getName()// 返回事务必须在多少秒内完成
    int getTimeout();  
    // 返回是否优化为只读事务。
    boolean isReadOnly();
} 
(1)事务隔离级别(Isolation Level)

事务隔离级别定义了一个事务可能受其他并发事务影响的程度。
并发事务会带来的问题
在应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作),这样就会导致一些并发问题:
脏读(Dirty read):一个事务读取到了另一个事务未提交的数据操作结果,这是相当危险的,因为很可能所有的操作都被回滚。
丢失更新(Lost update):两个事务同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。
不可重复读(Non-repeatable reads):一个事务对同一行数据重复读取两次,却得到了两个不同的结果。比如事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。又叫做虚读。
幻读(Phantom reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者少了第一次查询中出现的数据。这是因为在两次查询中有另一个事务插入数据造成的。

注意:
1.不可重复读的重点是修改某个记录字段,幻读的重点是新增活删除字段。
对于前者,只需要锁住满足条件的记录,对于后者,要锁住满足条件极其相近的记录。
2.脏读,不可重复读和幻读,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
3.丢失更新是因为系统没有任何的锁操作,因此并发事务并没有被隔离开来。丢失更新应该是要完全避免的,但是不能单靠数据库事务控制器来解决,必须需要应用程序对要更新的数据加必要的锁来解决。因此,防止更新丢失是应用的责任。

为了避免上面出现的几种情况,在标准SQL规范中,定义了四个事务隔离级别,由低到高依次为Read uncommitted(未提交读), Read committed(已提交读), Repeatable read(可重复读), Serializable(可序列化),这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

隔离级别 数据一致性 脏读 不可重复读 幻读
Read uncommitted 最低级别,只能保证不读物理上损坏的数据 Y Y Y
Read committed 语句级 N Y Y
Repeatable read 事务级 N N Y
Serializable 最高级别,事务级 N N N

TransactionDefinition接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,mySql默认采用的是REPEATABLE_READ隔离级别,Oracle默认采用的事READ_COMMITTED隔离级别。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但幻读和不可重复读仍有可能发生
TransactionDefinition.ISOLATION_SERIALIABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以完全防止脏读、不可重复读和幻读。但是这将严重影响程序的性能,通常情况下不会用到该级别。

(2)事务传播行为(Propagation Behavior)

事务传播行为指的是当一个事务方法被另一个事务调用时,这个事务方法应该如何进行。比如说,methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这个就是由methodB的事务传播行为决定的。
TransactionDefinition定义中包括了如下七个表示传播行为的常量:
支持当前事务:

传播行为 含义
TransactionDefinition.PROPAGATION_REQUIRED 表示当前方法必须运行中在事务中。如果当前事务存在,方法将会在改事务中运行;否则,会启动一个新的事务
TransactionDefinition.SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
TransactionDefinition.PROPAGATION_MANDATORY 表示该方法在事务中运行,如果当前事务不存在,就会抛出异常

不支持当前事务:

传播行为 含义
TransactionDefinition.PROPAGATION_REQUIRES_NEW 表示当前事务必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务存在,则在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
TransactionDefinition.PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
TransactionDefinition.NEVER 表示当前方法不应该运行在事务中。如果当前有一个事务在运行,则会抛出异常

其他情况

传播行为 含义
TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则创建一个当前事务的嵌套事务来运行;如果当前没有事务,则该取值相当于TransactionDefinition.PROPAGATION_REQUIRED

前六种事务传播行为是Spring从EJB中引入的,它们共享相同的概念。最后一个是Spring特有的。

详细看一下每一个传播行为
PROPAGATION_REQUIRED

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}

单独调用methodB方法时,因为当前上下文不存在事务,所以会开启一个新的事务。
调用methodA方法时,因为当前上下文不存在事务,所以会开启一个新的事务。当执行到methodB时,methodB发现当前上下文有事务,因此就加入到当前事务中来。

PROPAGATION_SUPPORTS

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}

单纯的调用methodB时,methodB方法是非事务的执行的。当调用methdA时,methodB则加入了methodA的事务中,事务地执行。

PROPAGATION_MANDATORY

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

// 事务属性为MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // do something
}

当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);当调用methodA时,methodB则加入到methodA的事务中,事务地执行。

PROPAGATION_REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
doSomeThingA();
methodB();
doSomeThingB();
// do something else
}


// 事务属性为REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // do something
}

当调用

main{  
methodA();
} 

相当于调用

main(){
    TransactionManager tm = null;
    try{
        //获得一个JTA事务管理器
        tm = getTransactionManager();
        tm.begin();//开启一个新的事务
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//挂起当前事务
        try{
            tm.begin();//重新开启第二个事务
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//提交第二个事务
        } Catch(RunTimeException ex) {
            ts2.rollback();//回滚第二个事务
        } finally {
            //释放资源
        }
        //methodB执行完后,恢复第一个事务
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//提交第一个事务
    } catch(RunTimeException ex) {
        ts1.rollback();//回滚第一个事务
    } finally {
        //释放资源
    }
}

在这里,我把ts1称为外层事务,ts2称为内层事务。从上面的代码可以看出,ts2与ts1是两个独立的事务,互不相干。Ts2是否成功并不依赖于 ts1。如果methodA方法在调用methodB方法后的doSomeThingB方法失败了,而methodB方法所做的结果依然被提交。而除了methodB之外的其它代码导致的结果却被回滚了。

PROPAGATION_NOT_SUPPORTED

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

// 事务属性为MANDATORY
@Transactional(propagation = Propagation.PROPAGATION_NOT_SUPPORTED)
public void methodB() {
    // do something
}

单独执行methodB无事务,执行methodA时,会挂起事务A,然后以非事务的方式执行methodB。

PROPAGATION_NEVER
总是非事务地执行,如果存在一个活动事务,则抛出异常。

PROPAGATION_NESTED
以PROPAGATION_NESTED启动的事务内嵌于外部事务中(如果存在外部事务的话),此时内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。另外,外部事务的回滚也会导致嵌套子事务的回滚。

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别
相同点:都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。
不同点:PROPAGATION_REQUIRES_NEW完全是一个新的事务, 而PROPAGATION_NESTED则是外部事务的子事务, 如果外部事务commit, 嵌套事务也会被commit。

(3)事务超时属性

事务超时,指的是一个事务所允许执行的最长时间,如果超过该时间限制而事务还没有完成,则自动回滚事务。TransactionDefinition中以int的值来表示超时时间,单位是秒。

(4)事务只读属性

事务只读属性是指,对事务性资源进行只读操作或者是读写操作。事务性资源指的是那些被事务管理的资源,包括数据源、JMS资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。TransactionDefinition中以boolean类型来表示该事务是否只读。

(5)回滚规则

该规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。但是你可以声明事务在遇到特定的检查型异常时回滚或者不会滚,而不用考虑是不是运行期异常。

5.TransactionStatus接口

TransactionStatus接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息。
PlatformTransactionManager.getTransaction(…)方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。
TransactionStatus接口的方法如下:

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
} 

6.Spring编程式事务

Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。简单的说,编程式事务侵入到了业务代码层面,但是提供了更加详细的事务管理;声明式事务由于基于AOP,所以既能起到事务管理的作用,又能不影响业务代码的具体实现。
Spring有两种方式的编程式事务管理,分别是:使用TransactionTemplate(推荐使用)和直接使用PlatformTransactionManager。

1.使用TransactionTemplate

使用TransactionTemplate和使用其他Spring模板是一样的,如JdbcTemplate和HibernateTemplate是一样的方法。TransactionTemplate继承了接口DefaultTransactionDefinition,用于简化事务管理。事务管理由模板类定义,主要是通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,通过调用模板类的参数类型为TransactionCallback或TransactionCallbackWithoutResult的execute方法来自动享受事务管理。

  • TransactionCallback:通过实现该接口的T doInTransaction(TransactionStatus status)方法来定义需要事务管理的操作代码
  • TransactionCallbackWithoutResult:继承TransactionCall接口,提供void doInTransactionWithoutResult(TransactionStatus status)用于那些不需要返回值的事务操作代码
TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplate
Object result = tt.execute(
    new TransactionCallback(){  
        public Object doTransaction(TransactionStatus status){  
            updateOperation();  
            return resultOfUpdateOperation();  
        }  
}); // 执行execute方法进行事务管理
@Test
public void testTransactionTemplate(){
	jdbcTemplate = new JdbcTemplate(dataSource);
    int i = jdbcTemplate.queryForInt(COUNT_SQL);  
    System.out.println("表中记录总数:"+i);
	//构造函数初始化TransactionTemplate
	TransactionTemplate template = new TransactionTemplate(txManager);
	template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
	//重写execute方法实现事务管理
	template.execute(new TransactionCallbackWithoutResult() {
		@Override
		protected void doInTransactionWithoutResult(TransactionStatus status) {
			jdbcTemplate.update(INSERT_SQL, "饿死");   //字段sd为int型,所以插入肯定失败报异常,自动回滚,代表TransactionTemplate自动管理事务
		}}
	);
	i = jdbcTemplate.queryForInt(COUNT_SQL);  
    System.out.println("表中记录总数:"+i);
}
2.使用PlatformTransactionManager
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="jdbcUrl" value="${db.jdbcUrl}" />
	<property name="user" value="${user}" />
	<property name="password" value="${password}" />
	<property name="driverClass" value="${db.driverClass}" />
	  
     <property name="minPoolSize"> 
         <value>5value> 
     property> 
      
     <property name="maxPoolSize"> 
         <value>30value> 
     property> 
      
     <property name="initialPoolSize"> 
         <value>10value> 
     property> 
      
     <property name="maxIdleTime"> 
         <value>60value> 
     property> 
      
     <property name="acquireIncrement"> 
         <value>5value> 
     property> 
      
     <property name="maxStatements"> 
         <value>0value> 
     property> 
      
     <property name="idleConnectionTestPeriod"> 
         <value>60value> 
     property> 
      
     <property name="acquireRetryAttempts"> 
         <value>30value> 
     property> 
      
     <property name="breakAfterAcquireFailure"> 
         <value>truevalue> 
     property> 
      
     <property name="testConnectionOnCheckout"> 
         <value>falsevalue> 
     property> 
bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
bean>

业务中的代码:

import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring-public.xml" })
public class test {
	@Resource
	private PlatformTransactionManager txManager;
	@Resource
	private  DataSource dataSource;
	private static JdbcTemplate jdbcTemplate;
	Logger logger=Logger.getLogger(test.class);
    private static final String INSERT_SQL = "insert into testtranstation(sd) values(?)";
    private static final String COUNT_SQL = "select count(*) from testtranstation";
	@Test
	public void testdelivery(){
		//定义事务隔离级别,传播行为,
	    DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
	    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
	    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
	    //事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务
	    TransactionStatus status = txManager.getTransaction(def);  
	    jdbcTemplate = new JdbcTemplate(dataSource);
	    int i = jdbcTemplate.queryForInt(COUNT_SQL);  
	    System.out.println("表中记录总数:"+i);
	    try {  
	        jdbcTemplate.update(INSERT_SQL, "1");  
	        txManager.commit(status);  //提交status中绑定的事务
	    } catch (RuntimeException e) {  
	        txManager.rollback(status);  //回滚
	    }  
	    i = jdbcTemplate.queryForInt(COUNT_SQL);  
	    System.out.println("表中记录总数:"+i);
	}
	
}

7.Spring声明式事务

编程式事务每次实现都要单独实现,在业务量大功能复杂时,使用编程式事务是很痛苦的。而使用声明式事务属于无侵入型,不会影响业务逻辑的实现。
声明式事务主要有两种实现方式,一种为通过使用Spring的定义事务通知与AOP相关配置实现,另一种是通过@Transactional实现事务管理。
方式一:

<tx:advice id="advice" transaction-manager="transactionManager">
	<tx:attributes>
	    
		<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>
		
		<tx:method name="*" propagation="SUPPORTS"/>
	tx:attributes>
tx:advice>


<aop:config>
    <aop:pointcut expression="execution(* com.kaizhi.*.service.impl.*.*(..))" id="pointcut"/>
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
aop:config>

注意:
1.定义事务通知,用于指定事务属性,其中’transaction-manager’属性指定事务管理器,并通过’'指定具体需要拦截的方法。
2.用来拦截方法,其中参数有:

参数 说明
name 方法名称,将匹配的方法注入事务管理,可用通配符
propagation 事务传播行为
isolation 事务隔离级别,默认为default
timeout 事务超时时间设置,单位为秒,默认为-1,表示事务超时将依赖底层事务系统
read-only 事务只读设置,默认为false,表示不是只读
rollback-for 需要出发回滚的异常定义,可定义多个,以’,'分隔,默认任何RuntimeException都将导致事务回滚,而任何Checked Exception将不导致回滚
no-rollback-for 不被触发进行回滚的Exception(s),可定义多个,以’,'分隔

方式二: @Transactional

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
     <property name="dataSource" ref="dataSource"/>
bean>    
<tx:annotation-driven transaction-manager="txManager"/> 

指定@Transactional注解

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)

注意:
1.@Transactional的具体参数跟上面的中的一样。Spring提供的@Transaction注解提供事务管理,内部同样是利用环绕通知TransactionInterceptor实现事务的开启及关闭。
2.如果在接口、实现类或方法上都指定了@Transactional注解,则优先级顺序为方法>实现类>接口
3.建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制(基于接口的代理)是没问题,但是如果使用CGLIB代理(继承)机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是’不能继承的’。

看一个完整的例子:

public class StudentService {
    public Student createStudent(Student student) {
        SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
                .openSession();
        try {
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            mapper.insertAddress(student.getAddress());
            mapper.insertStudent(student);
            sqlSession.commit();
            return student;
        } catch (Exception e) {
            sqlSession.rollback();
            throw new RuntimeException(e);
        } finally {
            sqlSession.close();
        }
    }
}

全局配置:


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
    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.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}">property>
        <property name="url" value="${jdbc.url}">property>
        <property name="username" value="${jdbc.username}">property>
        <property name="password" value="${jdbc.password}">property>
    bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliases" value="com.owen.mybatis.domain.Student" />
        <property name="typeHandlers"
            value="com.owen.mybatis.typehandlers.PhoneTypeHandler" />
        <property name="typeHandlersPackage" value="com.owen.mybatis.typehandlers" />
        <property name="mapperLocations" value="classpath*:com/mybatis3/**/*.xml" />
    bean>

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.owen.mybatis.mappers" />
    bean>

    
    
    <tx:annotation-driven transaction-manager="transactionManager" />

    
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    bean>
beans>

使用注解@Transactional

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, noRollbackFor = { TException.class }, readOnly = true, timeout = 3)
@Component
public class StudentService {
    @Autowired
    private StudentMapper studentMapper;

    public Student createStudent(Student student) {
    studentMapper.insertAddress(student.getAddress());
        if(student.getName().equalsIgnoreCase("")) {
            throw new RuntimeException("Student name should not be empty.");
        }
        studentMapper.insertStudent(student);
        return student;
    }
}

附上主要参考文章:
https://blog.csdn.net/liaohaojian/article/details/70139151
https://blog.csdn.net/soonfly/article/details/70305708

你可能感兴趣的:(Spring)