`

在Spring 3.1.0 M2中配置Hibernate事务失效?

 
阅读更多

SpringSource与2011年6月8号发布了Spring 3.1.0 M2,TEAM BLOG与6月9号和6月10号连续发布两篇博文Spring Framework 3.1 M2 releasedSpring 3.1 M2: Configuration Enhancements来介绍。这其中Chris Beans的文章介绍了如何整合Hibernate,说在M2中新引入了一个类AnnotationSessionFactoryBuilder来简化code-based的Spring配置方式。结合到具体项目中:

@Configuration
@EnableTransactionManagement
public class DataConfig {
	
	@Inject
	private Environment environment;

	@Inject
	private DataSource dataSource;
	
	@Bean
	public JdbcTemplate jdbcTemplate() {
		return new JdbcTemplate(dataSource);
	}
	
	@Bean
	public SimpleJdbcInsert simpleJdbcInsert() {
		return new SimpleJdbcInsert(dataSource);
	}
	
	// 3.1.0.M2
	@Bean
	public SessionFactory sessionFactory() throws Exception {

		Properties hibernateProperties = new Properties();
		hibernateProperties.put("hibernate.dialect", environment.getProperty("hibernate.dialect"));
		hibernateProperties.put("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));
		hibernateProperties.put("hibernate.generate_statistics", environment.getProperty("hibernate.generate_statistics"));
		hibernateProperties.put("hibernate.format_sql", environment.getProperty("hibernate.format_sql"));

		return new AnnotationSessionFactoryBuilder()
		.setDataSource(dataSource)
		.setPackagesToScan(environment.getProperty("hibernate.packagesToScan")
		.setHibernateProperties(hibernateProperties)
		.buildSessionFactory();
	}
	
	@Bean
	public PlatformTransactionManager transactionManager() throws Exception {
		HibernateTransactionManager bean = new HibernateTransactionManager();
		bean.setSessionFactory(sessionFactory());
		return bean;
	}
	
	@Configuration
	@Profile("dev")
	static class Development {
		
		@Inject
		private Environment environment;
		
		@Bean(destroyMethod="close")
		public DataSource dataSource() {
			DruidDataSource dataSource = new DruidDataSource();
			dataSource.setDriverClassName(environment.getProperty("jdbc.driverClassName"));
			dataSource.setUrl(environment.getProperty("jdbc.url"));
			dataSource.setUsername(environment.getProperty("jdbc.username"));
			dataSource.setPassword(environment.getProperty("jdbc.password"));
			return dataSource;
		}
		
	}
	
}
 

 但是经过测试,这样配置在service方法抛出异常后事务是不能回滚的!

问题似乎在最近刚出的RC1版本中得到了修正,在RC1版本中,AnnotationSessionFactoryBuilder类已经悄然从发布包中移除了,于是我们只能使用原始的AnnotationSessionFactoryBean类来配置:

 

@Configuration
@EnableTransactionManagement
public class DataConfig {
	
	@Inject
	private Environment environment;

	@Inject
	private DataSource dataSource;
	
	@Bean
	public JdbcTemplate jdbcTemplate() {
		return new JdbcTemplate(dataSource);
	}
	
	@Bean
	public SimpleJdbcInsert simpleJdbcInsert() {
		return new SimpleJdbcInsert(dataSource);
	}
	
	// 3.1.0.RC1
	@Bean
	public IdTransferringMergeEventListener merge() {
		IdTransferringMergeEventListener bean = new IdTransferringMergeEventListener();
		return bean;
	}
	
	@Bean
	public AnnotationSessionFactoryBean annotationSessionFactoryBean() {
		Properties hibernateProperties = new Properties();
		hibernateProperties.setProperty("hibernate.dialect", environment.getProperty("hibernate.dialect"));
		hibernateProperties.setProperty("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));
		hibernateProperties.setProperty("hibernate.generate_statistics", environment.getProperty("hibernate.generate_statistics"));
		hibernateProperties.setProperty("hibernate.format_sql", environment.getProperty("hibernate.format_sql"));
		
		AnnotationSessionFactoryBean bean = new AnnotationSessionFactoryBean();
		bean.setDataSource(dataSource);
		bean.setPackagesToScan(new String[] { environment.getProperty("hibernate.packagesToScan") });
		bean.setHibernateProperties(hibernateProperties);
		
		// optional
		Map<String, Object> eventListeners = new HashMap<String, Object>();
		eventListeners.put("merge", merge());
		bean.setEventListeners(eventListeners);
		
		return bean;
	}
	
	@Bean
	public PlatformTransactionManager transactionManager() {
		HibernateTransactionManager bean = new HibernateTransactionManager();
		bean.setSessionFactory(annotationSessionFactoryBean().getObject());
		return bean;
	}
	
	@Configuration
	@Profile("dev")
	static class Development {
		
		@Inject
		private Environment environment;
		
		@Bean(destroyMethod="close")
		public DataSource dataSource() {
			DruidDataSource dataSource = new DruidDataSource();
			dataSource.setDriverClassName(environment.getProperty("jdbc.driverClassName"));
			dataSource.setUrl(environment.getProperty("jdbc.url"));
			dataSource.setUsername(environment.getProperty("jdbc.username"));
			dataSource.setPassword(environment.getProperty("jdbc.password"));
			return dataSource;
		}
		
	}
	
}

 经过测试,这样的配置事务是可以回滚的。因为官方JIRA中没有找到对应的BUG列表,只能揣测开发团队内部发现此问题,但是有点想不明白为什么当初要“隆重”的介绍这个AnnotationSessionFactoryBuilder类出场。

然后很自然地联想到,如果直接在M2版本中使用上面这个RC1版本的配置,事务是否能回滚?答案是能回滚。

最后注意到在SPRING FRAMEWORK 3.1 RC1 RELEASED一文中有个叫Tobias的也注意到了AnnotationSessionFactoryBuilder类从RC1版中移除了,不知道他之前有没有碰到事务失效的问题?XD

 

补充(2011.10.22 15:10):

通过P6SPY抓取底层JDBC发送给数据库的sql发现有差异,如下:

 

事务失效

20111022 15:05:00,429|2|1|statement|insert into t_primary_keys(pk_key, pk_value) values('t_teacher', 0)
20111022 15:05:00,434|4|1|statement|update t_primary_keys set pk_value = 1 where pk_value = 0 and pk_key = 't_teacher'
20111022 15:05:00,436|1|1|commit|
20111022 15:05:00,442|1|1|statement|update t_primary_keys set pk_value = 2 where pk_value = 1 and pk_key = 't_teacher'
20111022 15:05:00,443|0|1|commit|
20111022 15:05:00,472|1|1|statement|insert into t_primary_keys(pk_key, pk_value) values('t_student', 0)
20111022 15:05:00,477|5|1|statement|update t_primary_keys set pk_value = 1 where pk_value = 0 and pk_key = 't_student'
20111022 15:05:00,492|15|1|commit|
20111022 15:05:00,494|0|1|statement|update t_primary_keys set pk_value = 2 where pk_value = 1 and pk_key = 't_student'
20111022 15:05:00,495|0|1|commit|
20111022 15:05:00,523|3|1|statement|insert into t_teacher (name, id) values ('teacher', 1)
20111022 15:05:00,535|11|1|statement|insert into t_student (name, id) values ('sssssssssssssssssssssss', 1)
 

事务成功

 

20111022 15:09:01,162|1|1|statement|update t_primary_keys set pk_value = 3 where pk_value = 2 and pk_key = 't_teacher'
20111022 15:09:01,163|1|1|commit|
20111022 15:09:01,186|1|1|statement|update t_primary_keys set pk_value = 3 where pk_value = 2 and pk_key = 't_student'
20111022 15:09:01,187|1|1|commit|
20111022 15:09:01,214|2|0|statement|insert into t_teacher (name, id) values ('teacher', 2)
20111022 15:09:01,226|11|0|statement|insert into t_student (name, id) values ('sssssssssssssssssssssss', 2)
20111022 15:09:01,240|1|0|rollback|

 

从中不难看出,事务成功的sql中发出了rollback,而事务失效的既没有发出rollback,也没有发出commit,这个时候jdbc链接是否释放,存不存在内存泄漏的情况暂时不得而知,等以后有时间在深入研究一下。

分享到:
评论
5 楼 stephansun 2012-04-04  
xkxkboy 写道
请问版主,介绍no xml 配置的spring3.1.0那里有详细的参考可以查看

Spring Framework Reference Documentation(http://static.springsource.org/spring/docs/3.1.0.RELEASE/reference/htmlsingle/)就是最好的参考文档,另外SpringSource Team Blog的3.1的Category也很不错(http://blog.springsource.org/category/spring/31/page/2/)。
4 楼 xkxkboy 2012-01-12  
请问版主,介绍no xml 配置的spring3.1.0那里有详细的参考可以查看
3 楼 xkxkboy 2012-01-12  
我按照你上面的配置,使用的包也是3.1.0的ralease版本,跑下面的代码后,数据库照样增加一条记录。如果getSession().update(abook);测试是有事务回滚的。
当我换成jdbcTemplate后,无论update跟insert的SQL都是照样执行,没有事务
@Transactional
public void testInsert() {
//jdbcTemplate.update("insert into book (title) values ('testInsert')");
Book aBook =new Book();
aBook.setTitle("33333");
getSession().save(aBook);
throw new RuntimeException("运行期例外");
}

你能不能也这样跑一次看看,是不是支持insert还需要别的配置,还有JdbcTemplate还需要再配置
2 楼 stephansun 2012-01-08  
不需要,我们使用HibernateTransactionManager这个类做Hibernate的事务管理器,默认已经支持纯Jdbc的事务。
而eventListeners.put("merge", merge());其实跟事务没有什么关系哈
可参考:http://static.springsource.org/spring/docs/3.1.0.RELEASE/javadoc-api/org/springframework/orm/hibernate3/support/IdTransferringMergeEventListener.html
1 楼 xkxkboy 2012-01-06  
请问楼主,你上面的配置测试过Hibernate的事务是可以运行,但是JdbcTemplate的失败,是不是JdbcTemplate还需要再配置?  eventListeners.put("merge", merge()); 只是有update事务可以正常,save,delete这些事务又需要怎么配置?

相关推荐

Global site tag (gtag.js) - Google Analytics