当前位置: 首页 > 编程日记 > 正文

Spring事务管理 与 SpringAOP

1,Spring事务的核心接口

  Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。 
  Spring事务管理涉及的接口的联系如下:

 

1.1 事务管理器

Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:

Public interface PlatformTransactionManager()...{  // 由TransactionDefinition得到TransactionStatus对象 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交Void commit(TransactionStatus status) throws TransactionException;  // 回滚Void rollback(TransactionStatus status) throws TransactionException;  } 

从这里可知具体的具体的事务管理机制对Spring来说是透明的,它并不关心那些,那些是对应各个平台需要关心的,所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。

1.2 JDBC事务

如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:

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

实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。

1.3 Java原生API事务

如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:

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

JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。

2,基本事务属性的定义

上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。 
那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:

而TransactionDefinition接口内容如下:

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

我们可以发现TransactionDefinition正好用来定义事务属性,下面详细介绍一下各个事务属性。

2.1 传播行为

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

传播行为含义
PROPAGATION_REQUIRED表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

注:以下具体讲解传播行为的内容参考自Spring事务机制详解 
(1)PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

//事务属性 PROPAGATION_REQUIRED
methodA{……methodB();……
}
//事务属性 PROPAGATION_REQUIRED
methodB{……
}

使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。

单独调用methodB方法:

main{ metodB(); 
}  

相当于

Main{ Connection con=null; try{ con = getConnection(); con.setAutoCommit(false); //方法调用
        methodB(); //提交事务
        con.commit(); } Catch(RuntimeException ex) { //回滚事务
        con.rollback();   } finally { //释放资源
        closeCon(); } 
} 

Spring保证在methodB方法中所有的调用都获得到一个相同的连接。在调用methodB时,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务。 
单独调用MethodA时,在MethodA内又会调用MethodB.

执行效果相当于:

main{ Connection con = null; try{ con = getConnection(); methodA(); con.commit(); } catch(RuntimeException ex) { con.rollback(); } finally {    closeCon(); }  
} 

调用MethodA时,环境中没有事务,所以开启一个新的事务.当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务。

(2)PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。

//事务属性 PROPAGATION_REQUIRED
methodA(){methodB();
}//事务属性 PROPAGATION_SUPPORTS
methodB(){……
}

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

(3)PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常

//事务属性 PROPAGATION_REQUIRED
methodA(){methodB();
}//事务属性 PROPAGATION_MANDATORY
    methodB(){……
}
View Code

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

(4)PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。

//事务属性 PROPAGATION_REQUIRED
methodA(){doSomeThingA();methodB();doSomeThingB();
}//事务属性 PROPAGATION_REQUIRES_NEW
methodB(){……
}

调用A方法:

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 {//释放资源
    }
}
View Code

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

(5)PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。(代码示例同上,可同理推出)

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

(7)PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而 nestedTransactionAllowed属性值默认为false。

//事务属性 PROPAGATION_REQUIRED
methodA(){doSomeThingA();methodB();doSomeThingB();
}//事务属性 PROPAGATION_NESTED
methodB(){……
}

如果单独调用methodB方法,则按REQUIRED属性执行。如果调用methodA方法,相当于下面的效果:

main(){Connection con = null;Savepoint savepoint = null;try{con = getConnection();con.setAutoCommit(false);doSomeThingA();savepoint = con2.setSavepoint();try{methodB();} catch(RuntimeException ex) {con.rollback(savepoint);} finally {//释放资源
        }doSomeThingB();con.commit();} catch(RuntimeException ex) {con.rollback();} finally {//释放资源
    }
}

当methodB方法调用之前,调用setSavepoint方法,保存当前的状态到savepoint。如果methodB方法调用失败,则恢复到之前保存的状态。但是需要注意的是,这时的事务并没有进行提交,如果后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的所有操作

嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。

使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTA TrasactionManager实现可能有不同的支持方式。

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。

另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.

PROPAGATION_REQUIRED应该是我们首先的事务传播行为。它能够满足我们大多数的事务需求。

2.2隔离级别

事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度。 
(1)并发事务引起的问题 
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题。

  • 脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
  • 不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
  • 幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

不可重复读与幻读的区别:

1)不可重复读的重点是修改

同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 
例如:在事务1中,Mary 读取了自己的工资为1000,操作并没有完成

con1 = getConnection();  select salary from employee empId ="Mary";  

在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.

  con2 = getConnection();  update employee set salary = 2000;  con2.commit();  

在事务1中,Mary 再次读取自己的工资时,工资变为了2000

 //con1  select salary from employee empId ="Mary"; 

在一个事务中前后两次读取的结果并不一致,导致了不可重复读。

2)幻读的重点在于新增或者删除: 
同样的条件, 第1次和第2次读出来的记录数不一样 
例如:目前工资为1000的员工有10人。事务1,读取所有工资为1000的员工。

con1 = getConnection();  Select * from employee where salary =1000; 

共读取10条记录

这时另一个事务向employee表插入了一条员工记录,工资也为1000

 con2 = getConnection();  Insert into employee(empId,salary) values("Lili",1000);  con2.commit();  

事务1再次读取所有工资为1000的员工

 //con1  select * from employee where salary =1000;  

共读取到了11条记录,这就产生了幻像读。

从总的结果来看, 似乎不可重复读和幻读都表现为两次读取的结果不一致。但如果你从控制的角度来看, 两者的区别就比较大。 
对于前者, 只需要锁住满足条件的记录。 
对于后者, 要锁住满足条件及其相近的记录。

(2)隔离级别

隔离级别含义
ISOLATION_DEFAULT使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

2.3 只读

事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。

2.4 事务超时

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

2.5 回滚规则

事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的) 
但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

2.6 事务状态

上面讲到的调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现,这个接口的内容如下:

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

可以发现这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。

3,编程式事务和声明式事务

  Spring提供了对编程式事务声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。 

  简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

3.1编程式事务

Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。

3.1 .1使用TransactionTemplate

采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。代码片段:

 TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplateObject result = tt.execute(new TransactionCallback(){  public Object doTransaction(TransactionStatus status){  updateOperation();  return resultOfUpdateOperation();  }  }); // 执行execute方法进行事务管理

使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。

3.2.2 使用PlatformTransactionManager

示例代码如下:

 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定义一个某个框架平台的TransactionManager,如JDBC、HibernatedataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态try {// 数据库操作dataSourceTransactionManager.commit(status);// 提交} catch (Exception e) {dataSourceTransactionManager.rollback(status);// 回滚}

3.2 声明式事务

3.2.1 Spring声明式事务与SpringAOP的说明

1)Spring aop的底层实现是采用的动态代理实现的;

2)当目标类有接口和实现类的时候Spring采用jdk动态代理实现,当只有实现类的时候Spring采用cglib字节码增强的方式实现

3)AspectJ是一个基于Java语言的AOP框架,底层实现还是动态代理(即2中的两种代理方式),使用方式两种:基于xml和基于注解。

4)当然不使用AspectJ框架,Spring也可以实现aop也可以使用基于xml的声明式事务,用注册bean,往bean里注入属性的方法半自动实现,比较麻烦,所以平时都用AspectJ。

3.2.2 Spring声明式事务五种配置方式

5种方式;http://www.blogjava.net/robbie/archive/2009/04/05/264003.html

我们主要用的就是基于AspectJ框架的两种

第一种:基于xml的,使用使用tx标签配置的拦截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"><!-- 事务配置 --><!-- 事务管理器 --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 数据源 --><property name="dataSource" ref="dataSource" /></bean><!-- 通知 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!-- 传播行为 --><tx:method name="save*" propagation="REQUIRED" /><tx:method name="insert*" propagation="REQUIRED" /><tx:method name="add*" propagation="REQUIRED" /><tx:method name="create*" propagation="REQUIRED" /><tx:method name="delete*" propagation="REQUIRED" /><tx:method name="update*" propagation="REQUIRED" /><tx:method name="find*" propagation="SUPPORTS" read-only="true" /><tx:method name="select*" propagation="SUPPORTS" read-only="true" /><tx:method name="get*" propagation="SUPPORTS" read-only="true" /></tx:attributes></tx:advice><!-- 切面 --><aop:config><aop:advisor advice-ref="txAdvice"pointcut="execution(* com.taotao.service.*.*(..))" /></aop:config>
</beans>

这个配置(声明)的意思就是拦截以save、insert、add等开头的方法,按照后面的属性(这里配了传播行为给他们添加事务(通知)。

第二种:用注解@Transactional。

配置文件

<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"xmlns:cache="http://www.springframework.org/schema/cache" xmlns:repo="http://www.springframework.org/schema/data/repository"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.7.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd "default-lazy-init="true"><description>Spring配置</description><context:annotation-config />
<!-- 引入数据源配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/><!-- 连接池 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driverClass}"></property><property name="jdbcUrl" value="${jdbc.url}"></property><property name="user" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 持久层 --><bean id="userAccountDao" class="com.trans.trans04.UserAccountDao"> <property name="dataSource" ref="dataSource"></property></bean><bean id="userAccoutService" class="com.trans.trans04.UserAccoutService"></bean><!-- 配置事务管理其 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 注入数据连接池 --><property name="dataSource" ref="dataSource"></property></bean><!-- 开启注解事务 --><tx:annotation-driven transaction-manager="transactionManager" />
</beans>

注解使用

package com.trans.trans04;import javax.annotation.Resource;import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;@Transactional(propagation=Propagation.REQUIRED)
public class UserAccoutService implements IUserAccoutService {@Resource(name="userAccountDao")private IUserAccountDao userAccountDao; public void UserTransferMoney(String fromAccount,String toAccount,double money) {//  org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READuserAccountDao.TransferOut(fromAccount, money);//转出账户int m=5/0; //出现异常userAccountDao.TransferIn(toAccount, money);//转入账户}
}

Spring事务管理详解:https://blog.csdn.net/trigl/article/details/50968079

注解事务实例:https://blog.csdn.net/hsgao_water/article/details/52860380

更详细的实验:https://blog.csdn.net/zx13525079024/article/details/52075784

转载于:https://www.cnblogs.com/xdyixia/p/9376077.html

相关文章:

iso镜像文件烧写到U盘

iso镜像文件烧写到U盘 windows rufus-3.1.exe 百度云盘链接&#xff1a;https://pan.baidu.com/s/16p1O4lXMVTUltTvCm0DnHA 提取码&#xff1a;inzj 文件格式一般选择默认的就行,如果起不来,就换一个, linux 1、dd命令 2、系统自带 usb-creator-gtk工具 命令行输入&#x…

webgl 游戏_30个令人惊叹的WebGL示例和演示

WebGl仍在增长&#xff0c;尽管大多数现代浏览器都支持它&#xff0c;但它也可能需要在旧的浏览器上工作。在本文中&#xff0c;我遇到了很多WebGL的示例和演示&#xff0c;它们可以增进您对这项新技术的理解。因此&#xff0c;请坐下来放松身心&#xff0c;使用最新的浏览器&a…

IE8下的VML显示问题解决方案

最近在维护一个使用VML画曲线的网站&#xff0c;在不同的IE下浏览效果不一样&#xff0c;特别是在IE8下&#xff0c;出现莫名其妙的样式显示问题&#xff1a; 1.曲线不可见&#xff01;在IE9或IE7下&#xff0c;曲线正常绘制&#xff0c;但是在IE8下&#xff0c;不见坐标轴和曲…

创新工场有哪些失败项目?不要只看着成功

创新工场有哪些失败项目&#xff1f;不要只看着成功 李开复 &#xff0c;创新工场CEO回答&#xff1a;失败或碰到挑战的项目也不少。这里不点名&#xff0c;不谈细节&#xff0c;但是谈谈碰到什么挑战&#xff08;有些已经失败&#xff0c;有些还在努力&#xff09;&#xff1a…

彻底解决Linux索引节点(inode)占用率高的告警

今天邮箱里发现有一封某服务器inode使用率发生告警的邮件 登录到服务器上df -i查看&#xff0c;发现/路径下91%&#xff0c;磁盘使用率却不高&#xff0c;猜测可能是某个目录下的小文件过多&#xff0c;进而造成inode占用率过高&#xff0c;但不清楚根路径下各文件夹里的文件数…

镜像打包工具clonezilla

镜像打包工具clonezilla clonezilla 百度云盘链接&#xff1a;https://pan.baidu.com/s/1LOEPqNE9O0Z4QJmNExlgeA 提取码&#xff1a;zlso 使用方法&#xff1a; 1、将镜像直接烧入U盘 2、U盘启动

python数据分析设置_Python 数据分析系列之如何安装和设置 Python

由于人们用 Python 所做的事情不同&#xff0c;所以没有一个普适的 Python 及其插件包的安装方案&#xff0c;接下来我将详细介绍各个操作系统上 Python 科学计算环境部署。我推荐免费的 Anaconda 安装包&#xff0c;Anaconda 提供 Python 2.7 和 3.6 两个版本&#xff0c;以后…

javamail gmail

http://www.programfan.com/club/showpost.asp?id27614转载于:https://www.cnblogs.com/yqskj/archive/2013/01/11/2855715.html

robots.txt文件的解析及过滤

什么是robots.txt文件? robots.txt&#xff08;统一小写&#xff09;是一种存放于网站根目录下的ASCII编码的文本文件&#xff0c;它通常告诉网络搜索引擎的漫游器&#xff08;又称网络蜘蛛&#xff09;&#xff0c;此网站中的哪些内容是不能被搜索引擎的漫游器获取的&#xf…

CF949C Data Center Maintenance(建图+强联通分量)

题意 有 n 个信息中心&#xff0c;第 i 个信息中心要在第 ti 个小时维护&#xff0c;维护期间信息不能被获得。 每个用户的数据都有两份备份&#xff0c;第 i 个用户的数据放在信息中心 c(i,1) 和 c(i,2)。 现在要挑选一个尽量小的信息中心集合&#xff0c;使得将这个集合的维护…

fabric 启动peer_编写 Fabric 链码的一般准则

我相信智能合约(链码)是 Hyperledger Fabric 区块链网络的核心。正确开发链码可以真正发挥一个安全区块链的优势&#xff0c;反之则会带来灾难性的后果。在这篇文章里我不打算探讨 Hyperledger Fabric 链码设计的特定模式的好与坏&#xff0c;而是希望分享我在开发若干 Hyperle…

Qt pro文件下跨平台宏的使用(windows/linux 以及x86 和 arm的区分)

#Qt pro文件下跨平台宏的使用&#xff08;windows/linux 以及x86 和 arm的区分&#xff09; 在pro文件中添加&#xff1a; #仅在linux 系统下&#xff0c; 硬件平台无关的内容 unix{HEADERS \SOURCES \Manager.cpp \ }#arm64 的编译宏 contains(QMAKE_HOST.arch, aarch64){…

数论(一)——素数,GCD,LCM

这是一个数论系列&#xff1a;&#xff09; 一、素数 费马小定理 Theorem&#xff1a; 设 p 是一个素数,a 是一个整数且不是 p 的倍数,那么 很遗憾,费马小定理的逆定理是不成立的。对 a 2,满足的非素数 n 是存在的。 比如 n 341 11 31 对于整数 a,称满足的合数为以 a 为底的…

java自学 day1

1.数据类型 基本数据类型&#xff08;存放数据本身&#xff09; 分为数值型&#xff08;int&#xff0c;double等&#xff09; 字符型&#xff08;char&#xff09;布尔型&#xff08;boolean&#xff09; 引用数据类型&#xff08;存放数据的地址&#xff09;分为类&#xff0…

Qt下一行代码就可以使用的稳定易用的日志log类

Qt下一行代码就可以使用的稳定易用的日志类 此日志类是基于Qt 自带的 扩展的一个易用的日志类&#xff0c; 使用的是Qt自带的日志输出形式&#xff0c; 已长期运行在许多实际项目中&#xff0c;稳定可靠&#xff0c;而且跨平台&#xff0c; 在windows和linux 上都能稳定运行 …

apue读书笔记-第十二章

1 可重入&#xff0c;线程安全&#xff0c;异步信号安全之间的区别&#xff1f; 可重入&#xff1a;可以重复进入&#xff0c;不会引起问题&#xff08;这个概念最宽&#xff09; 线程安全&#xff1a;被多个线程使用时&#xff0c;不会出问题&#xff0c;也就是可以被多个进程…

取出url中的字符_如何在JavaScript中解析URL:例如主机名,路径名,查询,哈希?...

统一资源定位符&#xff08;缩写URL&#xff09;是对Web资源&#xff08;网页&#xff0c;图像&#xff0c;文件&#xff09;的引用。URL指定资源位置和检索资源的机制&#xff08;http&#xff0c;ftp&#xff0c;mailto&#xff09;。例如&#xff0c;这是此博客文章的URL&am…

SQL Server 2008中的Pivot和UnPivot

SQL Server 2008中SQL应用系列--目录索引 今天给新成员讲解PIVOT 和 UNPIVOT示例&#xff0c;顺便整理了一下其用法。这是自SQL Server 2005起提供的新功能。 官方示例&#xff1a;http://msdn.microsoft.com/zh-cn/library/ms177410%28vsql.105%29.aspx 首先看PIVOT示例&#…

leetcode python 032 识别最长合法括号

# 给定一个只包含字符&#xff08;和&#xff09;的字符串&#xff0c;# 找到最长的有效&#xff08;格式良好&#xff09;括号子字符串的长度。# 对于“&#xff08;&#xff08;&#xff09;”&#xff0c;最长的有效括号子串是“&#xff08;&#xff09;”&#xff0c;其长…

Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析

在Android系统中&#xff0c;Activity窗口的大小是由WindowManagerService服务来计算的。WindowManagerService服务会根据屏幕及其装饰区的大小来决定Activity窗口的大小。一个Activity窗口只有知道自己的大小之后&#xff0c;才能对它里面的UI元素进行测量、布局以及绘制。本文…

pcl需要注意的编译问题

pcl需要注意的编译问题 不要在头文件里 using namespace pcl 这会导致编译错误,而且根本分析不到错误在哪 不要在编译选项 里加 -marchnative 这个是让编译器根据你当前的cpu类型进行特定的编译优化, 例如 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdc11 -march…

linux python版本_linux下更新Python版本并修改默认版本

linux下更新Python版本并修改默认版本&#xff0c;有需要的朋友可以参考下。很多情况下拿到的服务器python版本很低&#xff0c;需要自己动手更改默认python版本1、从官网下载python安装包(这个版本可以是任意版本3.3 2.7 2.6等等)wget http://python.org/ftp/python/2.7/Pytho…

基于HTML5的Google水下搜索

这次愚人节的时候&#xff0c;Google推出了水下搜索&#xff0c;当然&#xff0c;这只是一个愚人的小把戏&#xff0c;不过效果非常不错&#xff0c;进入页面后&#xff0c;第一眼是一个水面的效果&#xff0c;水下的鲨鱼在游来游去&#xff0c;然后Google logo和搜索框从水面上…

windows下rpc框架thrift的环境配置

windows下rpc框架thrift的环境配置 引用链接: https://www.cnblogs.com/49er/p/7193829.html 最近在弄windows下 的Facebook的rpc 框架 thrift , 网上东西看了很多, 但是大都不能一篇到位, 这里总结了一下, 也记一下自己遇到的问题和解决的方法 这里把我在实际过程中遇见的问…

CentOS 6.3 安装 samba 共享

PHP环境在linux下&#xff0c;但是开发的时候用的是windows&#xff0c;于是我用了samba将linux的一个目录共享&#xff0c;然后在windows上做映射&#xff0c;这样就可以直接在windows下编辑linux上的文件了 首先&#xff0c;安装samba软件&#xff0c;我采用的是yum安装&…

微信小程序 长按图片不出现菜单_微信更新,新功能上了热搜

微信在推出新功能方面相当克制&#xff0c;但每一次总能引起全网关注。昨天&#xff0c;微信又因为一个小功能的改进再次上了热搜&#xff0c;在安卓最新的 7.0.17 版本当中&#xff0c;微信取消了两分钟内删除功能。在新版微信中&#xff0c;发出的消息在两分钟内只有撤回功能…

windows下配置java环境jdk

Windows系统下搭建java的开发环境和配置环境变量 具体步骤打开链接地址&#xff1a;https://www.cnblogs.com/lijuntao/p/6694483.html转载于:https://www.cnblogs.com/ccw869476711/p/9401468.html

mysql 分区_搞懂MySQL分区

一.InnoDB逻辑存储结构首先要先介绍一下InnoDB逻辑存储结构和区的概念&#xff0c;它的所有数据都被逻辑地存放在表空间&#xff0c;表空间又由段&#xff0c;区&#xff0c;页组成。段段就是上图的segment区域&#xff0c;常见的段有数据段、索引段、回滚段等&#xff0c;在In…

apt Could not get lock /var/lib/dpkg/lock 解决方案

apt Could not get lock /var/lib/dpkg/lock 解决方案 删除锁定文件 sudo rm /var/lib/dpkg/lock

oracle创建DBLink连接

1.创建dblink的第一种方式&#xff0c;是在本地数据库tnsnames.ora文件中配置了要远程访问的数据库。tnsnames.ora文件在你安装oracle客户端安装文件里 如&#xff1a;(E:\oracle\product\10.2.0\db_1\NETWORK\ADMIN) 创建远程连接&#xff1a; INT (DESCRIPTION (ADDRES…