Spring 事务
JDBC原生事务实现
执行流程
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行sql
- 正常执行conn的commit()方法进行提交
- 异常回滚connection.rollback();
代码如下:
public class ConnectionUtil {
public final static String DB_DRIVER_CLASS = "com.mysql.jdbc.Driver";
public final static String DB_URL = "jdbc:mysql://127.0.0.1:3306/text";
public final static String DB_USERNAME = "root";
public final static String DB_PASSWORD = "123456";
public static Connection getConnection() throws ClassNotFoundException,
SQLException {
Connection con = null;
Class.forName(DB_DRIVER_CLASS);
con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
System.out.println("DB Connection created successfully");
return con;
}
}
public class JDBCTransationTest {
public static void main(String[] args) {
Connection connection = null;
try {
connection = ConnectionUtil.getConnection();
//开启事务
connection.setAutoCommit(false);
// 插入数据
insertTest(connection);
// 提交事务
connection.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
try {
//回滚事务
connection.rollback();
System.out.println("JDBC Transaction rolled back successfully");
} catch (SQLException e1) {
System.out.println("JDBC Transaction rolled back fail" + e1.getMessage());
}
} finally {
// 释放资源
if (connection != null) {
try {
selectAll(connection);
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void insertTest(Connection con) throws SQLException {
PreparedStatement stmt = con.prepareStatement("insert into test(num) values (?)");
stmt.setString(1, "test2");
stmt.executeUpdate();
System.out.println("Data inserted successfully");
stmt.close();
}
Spring 事务管理
基本流程
Sprign事务是通过AOP去创建对应的事务管理器,其执行基本流程如下:
- 利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接
- 修改数据库连接的autocommit为false
- 执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql
- 如果没有抛异常,则提交
- 如果抛了异常,则回滚
事务原理
开启Spring事务本质上就是增加了一个Advisor,以使用@EnableTransactionManagement注解来开启Spring事务,该注解代理的功能就是向Spring容器中添加了两个Bean:
1、 AutoProxyRegistrar
AutoProxyRegistrar主要的作用是向Spring容器中注册了一个 InfrastructureAdvisorAutoProxyCreator的Bean。 而InfrastructureAdvisorAutoProxyCreator 继承了AbstractAdvisorAutoProxyCreator,所以这个类的主要作用就是开启自动代理的作用,也就是一个BeanPostProcessor,会在初始化后步骤中去寻找Advisor类型的Bean,并判断当前某个 Bean是否有匹配的Advisor,是否需要利用动态代理产生一个代理对象。
2、 ProxyTransactionManagementConfiguration
ProxyTransactionManagementConfiguration是一个配置类,它又定义了另外三个bean:
-
BeanFactoryTransactionAttributeSourceAdvisor:一个Advisor =PointCut+Advice
-
AnnotationTransactionAttributeSource:相当于 BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut 。
用来判断某个类上是否存在@Transactional注解, 或者判断某个方法上是否存在@Transactional注解的
-
TransactionInterceptor:相当于BeanFactoryTransactionAttributeSourceAdvisor中的 Advice。
这是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个 代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的 invoke()方法
Spring事务基本执行原理
一个Bean在执行Bean的创建生命周期时,会经过InfrastructureAdvisorAutoProxyCreator的初始化后的方法,会判断当前当前Bean对象是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配逻辑为判断该Bean的类上是否存在@Transactional注解,或者类中的某个方法上是否存在 @Transactional注解,如果存在则表示该Bean需要进行动态代理产生一个代理对象作为Bean对象。 该代理对象在执行某个方法时,会再次判断当前执行的方法是否和 BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配则执行该Advisor中的 TransactionInterceptor的invoke()方法
代理对象的生成匹配
Pointcut的实现一定是基于ClassFilter和MethodMatcher来处理
ClassFilter#matches()方法,则类匹配只是做了判断该Bean的类上是否存在@Transactional注解,并没有做其他处理
再看MethodMatcher##matches()方法,才是做真正的匹配
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);AOP工具方法,可以拿到真实的对象方法,
然后去查找方法是是否有@Transactional注解,有则封装成TransactionAttribute对象,没有则会判断类上是否有@Transactional注解,有则封装成TransactionAttribute对象,这里可以追踪到SpringTransactionAnnotationParser#parseTransactionAnnotation()方法
注意这里有一个判断,如果非public方法上@Transactional是不会生成代理的,也就是此时事务是会失效的
事务的执行
TransactionInterceptor的invoke()
基本执行流程如下:
1、利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接 ,修改数据库连接的autocommit为false,绑定线程和数据源对象和数据库连接对象关系
this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification)
其实这里面还有很对事务传播的判断,这里不做解释,后面的事务传播在细说明。
**2、执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql **
retVal = invocation.proceedWithInvocation();
**3、 如果没有抛异常,则提交 **
this.commitTransactionAfterReturning(txInfo);
4、 如果抛了异常,则回滚
this.completeTransactionAfterThrowing(txInfo, var18);
Spring事务失效情况
情况一:数据库不支持事务
Spring事务本身最终是反馈到数据的,所以如果数据库的执行引擎不支持事务的,该事务是无效的
情况二:事务没有交给spring管理
分为两种,一种是,需要加事务的对象没有加入spring容器中,另一种是由于spring是通过事务管理器控制事务的,如果没有创建事务管理器则也会失效
情况三:方法为非Public的
在方法匹配时我们看到,如果是非public是不会封装属性的,所以@Transactional 默认是只能用于 public 的方法上,否则事务会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
情况四:方法本地调用
spring操作事务是通过代理对象操作的,所以事务方法必须通过代理对象调用才会生效,否则就只是普通的方法调用
情况五:业务异常吞并
可以看到,事务管理是通过try…catch捕获异常才能做回滚的,如果业务代码的异常try…catch并没有向外抛出,这时出现异常后事务就不会回滚而使得事务的一致性没法满足,进而事务失效
情况六:rollback的异常不匹配
spring默认的是RuntimeException或者Error,如果业务中出现其他类型的异常,则也不会回滚,进而事务失效
情况七:事务声明传播属性为不支持事务:PROPAGATION_NOT_SUPPORTED
spring提供的一种传播机制,表示以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
Spring事务底层执行与传播机制
Spring事务传播属性和隔离级别
事务传播属性
spring支持的传播属性有7种,如下:
1、PROPAGATION_REQUIRED=0
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择,也是默认的选择。
2、PROPAGATION_SUPPORTS=0
支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY=2
使用当前的事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW=3
新建事务,如果当前存在事务,把当前事务挂起。
5、PROPAGATION_NOT_SUPPORTED=4
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER=5
以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED=6
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
事务隔离级别
1、ISOLATION_DEFAULT=-1:
默认的隔离级别,使用数据库默认的事务隔离级别.(另外四个与JDBC的隔离级别相对应)
2、 ISOLATION_READ_UNCOMMITTED=1:
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
3、ISOLATION_READ_COMMITTED=2:
保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据
4、ISOLATION_REPEATABLE_READ=4:
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
5、 ISOLATION_SERIALIZABLE=8
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
脏读:
指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一 个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
不可重复读:
一个事务先后两次读取向同行的数据,发现数据不一致:事务1读取a行数据,事务2修改或者删除数据,事务1再次读取的数据和第一次不一样了。
幻觉读:
一个事务按照相同的查询条件重新读取数据,发现有新的数据插入:事务1按照条件a查询到b行记录,事务2再插入符合a条件的数据,当事务1再次按照条件a查询时发现多出了事务2插入的数据
事务执行与传播机制
回到TransactionInterceptor的invoke()追踪到TrasactionSupport#invokeWithinTransaction()
事务开启
1、事务开启createTransactionIfNecessary(),通过事务管理器创建事务
1.1、this.doGetTransaction(),获取事务管理器对象DataSourceTransactionManager.DataSourceTransactionObject,第一次进来只是创建了对象,数据源为null并且newConnectionHolder标识也为false,newConnectionHolder表示当前连接对象是否是新创建的标识,也就是只有第一次进来进行事务开启的时候才会设为true
顺带贴一下事务管理器的默认属性
1.2、isExistingTransaction是判断的当前是否有ConnectionHolder,显然第一次进来是不会走这里的逻辑
1.3、PROPAGATION_SUPPORTS=0,PROPAGATION_REQUIRES_NEW=3,PROPAGATION_NESTED=6,最终会到else,这里做的两件事,第一,挂起一个空事务suspend(null)
第二,开启事务startTransaction()方法
开启事务DefaultTransactionStatus status = this.newTransactionStatus(…),这里有一个重要属性newTransaction,为true,表示当前是一个新的事务,false表示当前已经开始了事务了,
重点看 this.doBegin(transaction, definition),获取连接对象,关闭自动事务提交、绑定数据库连接对象,这里会设置newConnectionHolder标识为true。
然后prepareSynchronization(),事务状态扭转,这里也提供了一个工具类的使用TransactionSynchronizationManager,用来帮助业务过程中进行查询或设置当前事务的状态
业务方法执行
retVal = invocation.proceedWithInvocation()
调用到@transactional注解的真实的业务方法
事务提交
this.commitTransactionAfterReturning(txInfo);通过事务管理器提交事务操作
这里有很多判断,正常的提交则会走到this.processCommit(defStatus);
事务回滚
this.completeTransactionAfterThrowing(txInfo, var18),正常回滚,就是通过管理器获取连接对象进行事务回滚
事务传播属性控制
PROPAGATION_REQUIRED=0
传播特性:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,也是spring默认的选择,就是整个只有一个事务
案例一:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
userService.a();
}
@Transactional
public void a() {
// a方法中的sql
}
}
分析:
当执行test时,执行流程和正常的开启一样,会进行真正的事务开启,dobegin()方法,创建ConnectionHolder,设置newConnectionHolder标识为true,并建立threadLocal和数据源、线程池的映射关系,通过ConnectionHolder获取连接对象,设置自动提交为false表示事务开启。
然后执行到test方法中,userService.a()会触发代理调用执行到createTransactionIfNecessary()方法–》getTransaction(),如下:
此时,isExistingTransaction()为true,表示当前已经有事务了,PROPAGATION_REQUIRED = 0
根据传播属性判断执行如下:
这里其实什么都没做,就做了事务状态扭转,设置newTransaction为false表示当前不是新事物
然后invocation.proceedWithInvocation()执行a()方法
到this.commitTransactionAfterReturning(txInfo)提交事务,执行a()对应得提交时,这里会进行匹配判断,由于此时newTransaction为false,所以这里不会做真正的提交
然后a()结束,到test()方法执行完,提交则会真正的this.doCommit(status),进行提交。
最终流程为:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 执行a方法中的sql
- 执行conn的commit()方法进行提交
案例二:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
userService.a();
int result = 100/0;
}
@Transactional
public void a() {
// a方法中的sql
}
}
分析:
这里其实和案例一差不多,只不过,当执行到 result = 100/0时会抛异常,此时会被test的rollback,this.completeTransactionAfterThrowing(txInfo, var18)
追踪rollback()—>processRollback(),这里newTransaction为true,所以会this.doRollback(status)做真正的回滚,并向上抛出异常
最终流程为:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 执行a方法中的sql
- 抛出异常
- 执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉
案例三:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
userService.a();
}
@Transactional
public void a() {
// a方法中的sql
int result = 100/0;
}
}
分析:
test()方法事务开启同案例一一样,第二次进入a()事务切面设置newTransaction为false,此时有异常会到a()的事务回滚方法追踪到processRollback(),根据条件判断,则会走到
这里a方法的事务切面回滚,只会设置一个事务回滚属性rollbackOnly为true,然后向上抛出异常会被test()事务切面捕获异常,然后回滚动作就和案例二一样,最终流程:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 执行a方法中的sql
- a方法抛出异常
- 执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉
案例四:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
try{
userService.a();
} catch (Exception e){
}
}
@Transactional
public void a() {
// a方法中的sql
int result = 100/0;
}
}
分析:
这里和案例三执行流程都差不多,但是当执行test()事务切面,尽管里面的a()方法异常了,但此时异常被业务方法自己捕获了,而并没有向上抛,也就是会走到test()事务切面的commit方法,
但这里有点不一样,案例三中可以看到a()回滚逻辑中设置了属性rollbackOnly为true,此时defStatus.isGlobalRollbackOnly()为true,this.shouldCommitOnGlobalRollbackOnly()默认为false,也就是此时test的提交逻辑并不会走真正的提交而是 this.processRollback(defStatus, true)进行强制回滚,而此时newTransaction为true,所以会真正的回滚
最终流程:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 执行a方法中的sql
- a方法抛出异常 ,设置全局回滚标识
- 执行test事务切面的commit,但会强制rollback()方法进行回滚,所以两个方法中的sql都会回滚掉
案例五:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
userService.a();
}
@Transactional
public void a() {
// a方法中的sql
try{
int result = 100/0;
} catch (Exception e){
}
}
}
分析:
这里由于a()把异常吃掉了,并且并没有向上抛出,也就是正常执行,所以最终流程和案例一一样
执行流程:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 执行a方法中的sql
- 执行conn的commit()方法进行提交
PROPAGATION_REQUIRES_NEW=3
新建事务,如果当前存在事务,把当前事务挂起。
案例六:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
userService.a();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void a() {
// a方法中的sql
int result = 100/0;
}
}
分析:
执行到userService.a()前都和案例一一样,当执行a()时,根据判断事务开启,由于a()的传播属性PROPAGATION_REQUIRES_NEW=3,所以会执行如下
suspendedResources = this.suspend(transaction),将test()的事务挂起,挂起会将ConnectionHolder置空,同时将上一个事务的连接池对象与当前线程解绑,并封装成挂起资源对象
this.startTransaction(definition, transaction, debugEnabled, suspendedResources),创建新的连接对象,开启新事物事务,同时将上一个事务的资源挂在当前事务中,用于后续恢复事务。所以此时
a()事务和test()的事务不是同一个事务,newTransaction标识都为true.
当执行到a()方法中时,由于异常则会走a()事务切面的回滚,此时a()方法中的sql会先回滚,并且会向上抛出异常,然后来到a()事务切面的 this.commitTransactionAfterReturning(txInfo);追踪到processCommit(),
很明显,a()执行完后,有一个事务挂起的资源,所以此时会进行test()事务的恢复
恢复事务,也就是将挂起的连接对象设置到事务管理器,并绑定当前线程、数据源、连接对象的对应关系,
此时,由于test()破获到a()抛出的异常,也会走自己的回滚操作,最终都会回滚,
执行流程:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 又新建一个数据库连接conn2
- 执行a方法中的sql
- 抛出异常
- 执行conn2的rollback()方法进行回滚
- 继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出
- 执行conn的rollback()方法进行回滚,最终还是两个方法中的sql都回滚了
PROPAGATION_NESTED=6
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
案例七:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// test方法中的sql
userService.a();
userService.b();
}
@Transactional(propagation = Propagation.PROPAGATION_NESTED)
public void a() {
// a方法中的sql
}
@Transactional(propagation = Propagation.PROPAGATION_NESTED)
public void b() {
// b方法中的sql
int result = 100/0;
}
}
分析:
test()事务切面的执行与前面都一致,但当执行a()事务切面时,由于传播属性变成了PROPAGATION_NESTED=6,则会执行如下:
设置newTransaction标识为false,并且创建回滚点,status.createAndHoldSavepoint()
此时,并没有新创建新的连接对象,然后到a()正常执行,到a()事务的提交,由于有回滚点,所以并不会真正的提交,这里只做了回滚点的清除,也就是a()的回滚点擦除了
然后到b()事务切面,同a()一样也会创建回滚点,并且不会创建新的连接对象,也就是说test()、a()、b()是同一个连接对象,此时b()异常,这来到b()的事务回滚逻辑,如下:
回滚到回滚点,并设置强制回滚标识rollbackOnly为false,然后清除回滚点
b()回滚逻辑完后,会抛出异常,这时会被test()事务破获到异常,就会来到test()的捕获逻辑,由于test()是创建事务的并且没有回滚点,测试走真正的回滚,这是test()、a()方法中的都会回滚,也就是全局回滚。
执行流程:
- 新建一个数据库连接conn
- 设置conn的autocommit为false
- 执行test方法中的sql
- 创建回滚点SAVEPOINT_1
- 执行a方法中的sql
- 擦除回滚点SAVEPOINT_1
- 创建回滚点SAVEPOINT_2
- 执行b中的方法
- 抛出异常
- 执行conn的rollback((Savepoint)savepoint)方法进行回滚
- 擦除回滚点SAVEPOINT_2
- 继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出
- 执行conn的rollback()方法进行回滚,最终还是test、a、b方法中的sql都回滚了
从上面流程看出,似乎回滚点没有遵循回滚,怎么能够保证回滚点呢?
从流程中可以看出,其实是因为内层方法的回滚没有做真正的回滚提交,因为真正的是交给外层回滚的,但我们发现它在回滚时,设置强制回滚标识rollbackOnly为false,从前面案例中,有结论:如果内层事务抛出异常,并且事务回滚时设置rollbackOnly为true,则表示需要强制回滚,如果为false则表示关闭强制回滚,也就是提交时会走真正的提交,所以我么可以将test的异常吃掉
代码如下:
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
try{
// test方法中的sql
userService.a();
userService.b();
}catch(Exception e){
}
}
@Transactional(propagation = Propagation.PROPAGATION_NESTED)
public void a() {
// a方法中的sql
}
@Transactional(propagation = Propagation.PROPAGATION_NESTED)
public void b() {
// b方法中的sql
int result = 100/0;
}
}
编程式事务
Spring 有声明式事务和编程式事务: 声明式事务只需要提供@Transactional 的注解,然后事务 的开启和提交/回滚、资源的清理就都由 spring 来管控,我们只需要关注业务代码即可;
编程式事务则需要使用 spring 提供的模板,如 TransactionTemplate,或者直接使用底层的 PlatformTransactionManager 手动控制提交、回滚。声明式事务的最大优点就是对代码的侵入性小, 只需要在方法上加@Transactional 的注解就可以实现事务; 编程式事务的最大优点就是事务的管控粒度较细,可以实现代码块级别的事务
如:
@Component
public class TransactionUtil {
@Autowired
private DataSourceTransactionManager transactionManager;
/**
* 开启事务
*
* @return:
* @Author: th_legend
**/
public TransactionStatus begin() {
TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
return transaction;
}
public void testTransaction(){
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 开启事务
TransactionStatus transactionStatus = begin(transactionDefinition);
try {
// 执行sql
// 注册事务状态监听
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public int getOrder() {
return TransactionSynchronization.super.getOrder();
}
@Override
public void suspend() {
System.out.println("事务挂起...");
}
@Override
public void resume() {
System.out.println("事务恢复...");
}
@Override
public void flush() {
TransactionSynchronization.super.flush();
}
@Override
public void beforeCommit(boolean readOnly) {
System.out.println("事务准备提交...");
}
@Override
public void beforeCompletion() {
System.out.println("事务准备提交或回滚...");
}
@Override
public void afterCommit() {
System.out.println("事务提交完成...");
}
@Override
public void afterCompletion(int status) {
System.out.println("事务提交活活滚完成...");
}
});
xxx();
commit(transactionStatus);
}catch (Exception e){
rollback(transactionStatus);
}
}
/**
* 开启事务
*
* @return:
* @Author: th_legend
**/
public TransactionStatus begin(TransactionDefinition transactionDefinition) {
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
return transaction;
}
/**
* 提交事务
*
* @return:
* @Author: th_legend
**/
public void commit(TransactionStatus transaction) {
if (null != transaction) {
transactionManager.commit(transaction);
}
}
/**
* 回滚事务
*
* @return:
* @Author: th_legend
**/
public void rollback(TransactionStatus transaction) {
if (null != transaction) {
transactionManager.rollback(transaction);
}
}
/**
* 获取数据源对象
*
* @return:
* @Author: th_legend
**/
public DataSource getDataSource(){
return transactionManager.getDataSource();
}
}