Spring事务配置(兼容hibernate/mybatis两套框架)及Hibernate/Mybatis/Jdbctemplate事务测试

      如果一些老旧的系统需要兼容hibernate/mybatis两套orm数据库工具,则事务管理器就不能用Hibernate的事务管理器,而要用spring自带的事务管理器,具体配置如下:

       首先applicationContext.xml应该相当于一个手脚架, 将引用各种职能的xml配置,如下图, 导入了三个数据库相关的xml引用:




	
	
	
	

...

spring-db.xml如下 , 配置了事务管理和jdbctemplate等工具,注意,这里用org.springframework.jdbc.datasource.DataSourceTransactionManager来处理数据库事务而不是org.springframework.orm.hibernate4.HibernateTransactionManager.



        
    
    
    

    



      
            
        
			true
		     
    
    
          
    
    
    
    
    
	
	
	
	
			
		
	
		
	    
    
    
    				
		
	  

spring-hibernate.xml的配置:


    
	
	
	
	
	 
		
		 
			
			    com.freestyle.common.spring.entities
				com.freestyle.test.hibernate.entities
				com.freestyle.app.entities
				com.freestyle.test.beans
									
		
		
			
				org.hibernate.dialect.PostgreSQLDialect
				
				update
				
				 
				true
				
                
                 
                true
                
                
                
                false
                
                false
                
                100
                                
                50
                
                false
                
                auto
                false
                
                TRACE
			
    
	


      
        
    
     

 

spring-mybatis.xml:




	
		
		
		
		
		
		
			
				classpath*:com/freestyle/common/db/mybatis/CommonMapper.xml
				classpath*:com/freestyle/app/mybatis/dao/*Mapper.xml
			
		
	
	
	
    
        
        
        
        
    
    

 

POM的引用:


	com.alibaba
	druid
	1.1.16


      org.mybatis
        mybatis
        3.4.6


      org.mybatis
        mybatis-spring
       1.3.2


		
		
			org.hibernate
			hibernate-core
			${spring-hibernate.version}
			
				
					org.javassist
					javassist
				
			
		
		
		
		
		
			org.hibernate
			hibernate-entitymanager
			4.3.11.Final
			
		

 

 

=========================================

下面是事务测试:

1/测试事务回滚, 下面test3代码里面用hibernate和mybatis两种截然不同的框架来进行数据库的更改最后触发异常,并两者都能成功进行事务回滚.

mvDao为Hinbernate访问对象, 执行了save操作,其实并未写入数据库, 所以在其后加入一个createSQLQuery来迫使hibernate先执行update运作.

@Repository("transactionTestModule")
public class TransactionTestModule {
	@Resource(name = "hibernateUserDao")
	protected IHibernateEntityDao mvDao;
	@Resource(name = "hUserBaseDao")
	protected IHibernateEntityDao mvBaseDao;
	@Resource
	protected TgSeriesMapper mTgDao;
	@Resource
	protected TaUserMapper mTaDao;
	private Log log=LogFactory.getLog(TransactionTestModule.class);
	
	
	/***
	 * 手动通过throw exception 回滚
	 */
	@Transactional
	public void test3(){
		log.info("数据操作后业务检查失败,全部回滚");		
		TaUser lvUser1=mvDao.get("admin"); //mvDao为hibernate访问对象		
		lvUser1.setFaName("user1");
		mvDao.save(lvUser1,false);
		mvDao.createSQLQuery("select * from ta_user").list();
		com.freestyle.app.mybatis.entities.TaUser lvUser2=mTaDao.selectByPrimaryKey("test1");
		lvUser2.setFaName("user2");
		mTaDao.updateByPrimaryKey(lvUser2);
		//testMybatis2();
		if (mvDao!=null){ //演示业务检查失败,用抛出exception的方式回滚
			throw new RuntimeException("因为长得太帅,这次不改名了");
		}		
	}
	

         但是他们处理同一个事务吗? 并不是, mvDao和mTaDao均处于各自的事务当中, 两者是隔离的,也就是说mvDao作的数据更改在mTaDao来说是看不见的. 有后台数据连接为证,下图为当断点打在"if (mvDao!=null){ //演示业务检查失败,用抛出exception的方式回滚"时后台数据库连接的截图, 9928这个PID是mvDao(hibernate)申请的连接, 而9716是mybatis申请的连接:

 

通过测试得知, 即使用@Transactional来指明这个函数是处于事务中, 但里面的hibermate和mybatis两种数据库访问对象尽管是处理事务管理当中, 但它们的事务是独立的, 不能互访的.

 

2/再测试jdbctemplate与hibernate是否处理同一事务
如下代码, 因为同是jdbctemplate, 所以在事务结束前tmp是有效的, 但mvDao则因为处理属于它自已的事务里面,所以第三句会出错"ERROR SqlExceptionHelper:146 - 错误: 关系 "tmp" 不存在"

	@Transactional
	public void testJdbcTemplateNHibernate() {
		ContextHolder.getJdbcTemplate().execute("create temp table tmp on commit drop as select * from ta_user where fa_login='admin'");
		ContextHolder.getJdbcTemplate().queryForList("select * from tmp");
		
		List lvList= mvDao.createSQLQuery("select * from tmp").list();
		log.info(lvList);
	}
	

通过测试得知, jdbctemplate同样是与hibernate的事务是独立的, 因为开两个不同的数据库连接, 所以不能互访.

 

3/测试jdbctemplate与mybatis事务

修改TaUserMapper.xml,在末尾加上:


  	 create temp table ${pvsTb} as select * from ta_user ;  	 
  
  

修改TaUserMapper.java,在末尾加上:

void createTemp(@Param("pvsTb") String pvsTb);
List selectFromTemp(@Param("pvsTb") String pvsTb);

执行jdbctemplate与mybatis分别建立tmp1和tmp2,并交叉访问:

@Transactional
	public void testJdbcTemplateNMybatis() {
		//jdbctemplate 建立tmp1, mybatis建立tmp2
		ContextHolder.getJdbcTemplate().execute("create temp table tmp1 on commit drop as select * from ta_user where fa_login='admin'");		
		mTaDao.createTemp("tmp2");
		
		//交叉访问, jdbctemplate访问tmp2, mybatis访问tmp1
	    ContextHolder.getJdbcTemplate().queryForList("select * from tmp2");		
		mTaDao.selectFromTemp("tmp1");		
	}

很神奇地, 居然可以互访, 也就是说jdbctemplate与mybatis的事务用的是同一个, 查后台数据库连接也是共用一个连接的 .

 

4/测试hibernate两个sqlquery之间是否事务能互访 , 如下代码, 是可以互访的, 但必须注意, sqlquery每次执行都会开启自已独立的事务(在这里是事务嵌套了), 所以如果建立临时表时用on commit drop, 当sqlquery执行完就会commit, 临时表就会被回收, 那么第2个sqlquery就会出错.

			mvDao.createSQLQuery("create temp table tmp /*on commit drop*/ as select * from ta_user where fa_login='admin'").executeUpdate();
			mvDao.createSQLQuery("select * from tmp").list(); 

   由于sqlquery会开启自已的事务的特性, 那如果想用runtimeexception来让方法层面的事务回滚那是不可能的, 在sqlquery执行中产生的数据变动不能回滚, 因为它执行完就commit了. 如下代码 , fa_name最后还是被改为abc.

	@Transactional
	public void testSQLQuery() {
		mvDao.createSQLQuery("update ta_user set fa_name='abc' where fa_login='admin'").executeUpdate();
		if (1+1==2)throw new RuntimeException("rollback");
     }

   而这时候, 若想要将sqlquery的事务要与hibernate的传统事务绑定在一起, 那么可以自已显式的用一个transaction将它们绑在一起:

Session lvSess=mvDao.getSession();
		Transaction lvTrans=lvSess.beginTransaction();
		try {
			TaUser lvUser=mvDao.get("admin");
			log.info(lvUser.getFaName());//fa name=admin
			mvDao.evit(lvUser);
			mvDao.createSQLQuery(lvSess,"update ta_user set fa_name='abc' where fa_login='admin'").executeUpdate();			
			lvUser=mvDao.get("admin");
			log.info(lvUser.getFaName());//fa name=abc		
			mvDao.evit(lvUser);			
			com.freestyle.app.mybatis.entities.TaUser lvUser1=mTaDao.selectByPrimaryKey("admin"); //这是另一条transaction
			log.info(lvUser1.getFaName()); //fa name=admin								
			....
		}
		catch (RuntimeException e) {
			lvTrans.rollback();
			throw e;
		}

=====================

总结:

1/@Transactional注解的方法里面, mybatis与jdbctemplate共用同一事务共用同一个数据库连接

2/@Transactional注解的方法里面, mybatis与hibernate有各自的事务管理, 但它们的事务是互相独立的, 也用不同的数据库连接, 不能互访

3/@Transactional注解的方法里面, jdbctemplate与hibernate有各自的事务管理, 但它们的事务是互相独立的,  也用不同的数据库连接, 不能互访

4/即使在@Transactional注解的方法里面,hibernate的sqlquery所作出的数据变动是不能通过runtimeexception来回滚的,原因就是它会有自已的事务, 执行完就立即commit了. 最好的方法就是自已显式写代码将sqlquery和传统的hibernate的dao绑在同一个transaction里面

你可能感兴趣的:(Web,Framework,Spring,事务)