jdon框架整合hibernate后的事务处理

08-11-10 oojdon
默认的事务边界是延伸到了表现层渲染之后,如果我想把数据库事务完全控制在业务层的话,如下代码:

public void createTopicMessage(EventModel em) throws Exception {
    	
        logger.debug(" enter service: createMessage ");
        ForumMessage forumMessage = (ForumMessage)em.getModelIF();
        Forum forum = forumBuilder.getForum(forumMessage.getForum().getForumId());
        if (forum == null){
            logger.error(" no this forum, forumId = " + forumMessage.getForum().getForumId());
            em.setErrors(Constants.ERRORS);
            return;            
        }
        forumMessage.setForum(forum);
        TransactionManager tx = jtaTransactionUtil.getTransactionManager();        
        try {
          	tx.begin();
            ForumThread forumThread = super.createThread(forumMessage);
            forumMessage.setForumThread(forumThread);
            messageDaoFacade.getMessageDao().createMessage(forumMessage);
            tx.commit();
        } catch (Exception e) {
            logger.error(e);
            jtaTransactionUtil.rollback(tx);
            throw new Exception(e);
        }
    }
<p>

session是否会在JTA事务结束之后关闭?如果会,那就避免不了LazyInitializationException异常了,要解决它又回到OSIV。

[该贴被oojdon于2008-11-10 17:30修改过]

banq
2008-11-10 17:30
CloseSessionIbView如果Session没有关闭,进行关闭。

你可以在事务内自己关闭session

oojdon
2008-11-10 17:44
session都是期望它在表现层渲染完成之后关闭,所以还是交给CloseSessionInView,关键是事务,如果不是JTA事务,直接交给hibernate就行了,但是事务边界是固定的,要自己控制事务边界又期望sessin保持打开,我现在似乎模模糊糊的理解了spring的良苦用心,就OpenSessionInView了,爱用不用,高手们知道有问题,但是菜鸟们不好意思,我spring没有照顾好你们!!

banq
2008-11-10 18:01
可以使用flush ,将缓存和数据库同步一下就可以,不必关闭Session

如果你不想手工flush,让Hibernate自动flush,必须配置Hibernate的hibernate.transaction.manager_lookup_class 和 hibernate.transaction.factory_class

不是让Hibernate启动事务,而是让他自动侦测,他如果发现事务关闭,会自动auto flush

这些和Spring 的OSIV,没有关系,不要瞎猜,要理解原理,这里一篇文章:

http://blog.xebia.com/2008/07/18/configuring-hibernate-and-spring-for-jta/

[该贴被banq于2008-11-10 18:14修改过]

oojdon
2008-11-10 18:43
好文章,谢谢

我把jivejdon用jboss再测试一下

oojdon
2008-11-11 11:52
请教banq,用jdon框架做项目,下面几种程序写法在底层运行的异同,有没有会出问题的写法?我的担心是在使用hibernate的时候,先把jdon整合了hibernate的事务代码抓出来:

//数据库请求到来,从当前ThreadLocal取session并开始事务
public Session getSession() throws HibernateException {
		Session sess = (Session)SessionFactoryHolder.getSession();
		if (sess == null) {
			sess = getFactory().openSession();
			Transaction tr = sess.beginTransaction();
			SessionFactoryHolder.setSession(sess);
			SessionFactoryHolder.setTransaction(tr);
		}
		return sess;
}
<p>

//CloseSessionInView,提交事务
public void closeSession() throws HibernateException {
		Transaction tr = (Transaction) SessionFactoryHolder.getTransaction();
		try {
			if (tr != null && !tr.wasCommitted() && !tr.wasRolledBack()) {
				tr.commit();
			}
		} catch (Exception e) {
		}finally{
			SessionFactoryHolder.setTransaction(null);
			SessionFactoryHolder.getSession().close();
			SessionFactoryHolder.setSession(null);			
		}
}
<p>

1,没有使用hibernate的jivejdon,持久层使用JdbcTemp,没有事务,默认自动提交

public void createTopicMessage(EventModel em) throws Exception {
ForumMessage forumMessage = (ForumMessage)em.getModelIF();
        Forum forum = forumBuilder.getForum(forumMessage.getForum().getForumId());
        forumMessage.setForum(forum);
        ForumThread forumThread = super.createThread(forumMessage);
        forumMessage.setForumThread(forumThread);
        messageDaoFacade.getMessageDao().createMessage(forumMessage);
}

2,没有使用hibernate的jivejdon,持久层使用JdbcTemp,使用JTA事务接口

public void createTopicMessage(EventModel em) throws Exception {
ForumMessage forumMessage = (ForumMessage)em.getModelIF();
        Forum forum = forumBuilder.getForum(forumMessage.getForum().getForumId());
        forumMessage.setForum(forum);
        TransactionManager tx = jtaTransactionUtil.getTransactionManager();        
        try {
          	tx.begin();
            ForumThread forumThread = super.createThread(forumMessage);
            forumMessage.setForumThread(forumThread);
            messageDaoFacade.getMessageDao().createMessage(forumMessage);
            tx.commit();
        } catch (Exception e) {
            logger.error(e);
            jtaTransactionUtil.rollback(tx);
            throw new Exception(e);
        }
    }

3,使用了hibernate的jivejdon,使用jdon框架整合hibernate的事务处理----事务边界是数据库请求—>页面渲染结束

public void createTopicMessage(EventModel em) throws Exception {
ForumMessage forumMessage = (ForumMessage)em.getModelIF();
        Forum forum = forumBuilder.getForum(forumMessage.getForum().getForumId());
        forumMessage.setForum(forum);
        ForumThread forumThread = super.createThread(forumMessage);
        forumMessage.setForumThread(forumThread);
        messageDaoFacade.getMessageDao().createMessage(forumMessage);
}

4, 使用了hibernate的jivejdon,但手工使用JTA接口

public void createTopicMessage(EventModel em) throws Exception {
ForumMessage forumMessage = (ForumMessage)em.getModelIF();
        Forum forum = forumBuilder.getForum(forumMessage.getForum().getForumId());
        forumMessage.setForum(forum);

        TransactionManager tx = jtaTransactionUtil.getTransactionManager();        
        try {
          	tx.begin();
            ForumThread forumThread = super.createThread(forumMessage);
            forumMessage.setForumThread(forumThread);
            messageDaoFacade.getMessageDao().createMessage(forumMessage);
            tx.commit();
        } catch (Exception e) {
            logger.error(e);
            jtaTransactionUtil.rollback(tx);
            throw new Exception(e);
        }
    }

5,jivejdon已经向EJB平滑升级,所以方法是这样的

@TransactionAttribute(REQUIRES_NEW)
public void createTopicMessage(EventModel em) throws Exception {
ForumMessage forumMessage = (ForumMessage)em.getModelIF();
        Forum forum = forumBuilder.getForum(forumMessage.getForum().getForumId());
        forumMessage.setForum(forum);

            ForumThread forumThread = super.createThread(forumMessage);
            forumMessage.setForumThread(forumThread);
            messageDaoFacade.getMessageDao().createMessage(forumMessage);
}

我比较郁闷的就是这四,五两种写法,事务在方法返回后结束,但是hibernate的sessin呢?是还打开,还是已经被关闭,如果关闭我的懒加载怎么办?

[该贴被oojdon于2008-11-11 12:14修改过]

banq
2008-11-11 17:36
>但是Hibernate的sessin呢?是还打开,还是已经被关闭,如果关闭我的懒加载怎么办?

我也赞成使用第四 五种方式,在这两个方式下,我前面帖子写了,不必关闭session的两种方式处理事务和Session的关系:

1. 手工在事务结束前session.flush()

2. 配置Hibernate的transaction.mamager为JTA,让Hibernete能够自动进行flush,它会侦测当前是否在一个事务,如果事务结束,它就进行flush,将内存中状态和数据库数据进行同步。

事务和Session本来特别关系,事务主要是保证数据库资源操作的一致性,所以,需要flush来特别操作。

使用flush之后,就不必在事务中关闭session,这样,由closeSessionInView来关闭。

xmuzyu
2008-11-14 13:31
hibernate.transaction.flush_before_completion和hibernate.transaction.auto_close_session为true,就可以在JTA事务结束的时候关闭,默认的话,要自己刷新和关闭(如果采用JTA接口)。

oojdon
2008-11-14 21:25
谢谢,现在基本上理解了,在hibernae in action一书上有这样一段:

当使用Hibernate的事务API的时候,在不同部署环境中切换的时候,事务的启动和提交有可能导致一个你无法预料的空操作,当需要事务划分的可移植性时这永远是最有一种选择。

这也是我怀疑第四种写法会出问题的原因。当然就类似jivejdon只用一个JdbcTemp,不管哪种写法我觉得都没有问题,问题是出在使用hibernate的时候。

>>hibernate.transaction.flush_before_completion和Hibernate.transaction.auto_close_session为true,就可以在JTA事务结束的时候关闭,默认的话,要自己刷新和关闭(如果采用JTA接口)。

如果设置了,事务在方法返回后结束同时session关闭,这样讨厌的LazyInitializationException就来了,是吧?

另外让我疑惑的还有,按照hibernate in action的说法,第五种容器事务声明的写法,方法返回时事务提交同时sessin自动关闭,同样LazyInitializationException。

Spring号称without EJB所以提供了支持多种事务API(JPA、Hibernate、JDO和JTA)的可扩展事务管理抽象,但是像Jdon这种支持单机小系统向分布式计算大系统迁移的框架就应该做好各种系统升级测试,对不起banq,目前如此种种都是我的臆测并没有实战编程。

BTW:如今的Spring不是和EJB统一了吗?被banq一直诟病的状态管理缺陷已经在2.x版本种修复,其他呢?支持单机小系统向分布式计算大系统平滑升级吗?还有,既然状态管理缺陷已经修复,它的案例JpetStore就应该改写呀,这么还是HttpSession?这个问题seam回答了我:

Seam集成包中同样允许你像Spring 2.0风格的自定义作用域那样来使用Seam的上下文。你可以在任意Seam上下文中定义Spring Bean。但是,需要重申的是,Spring的组件模型并非设计为支持状态(Statefullness)的,所以请小心使用这一特性。特别是Session和Conversation作用域的Spring Bean集群很有问题,从大作用域注入到小作用域时也要格外小心。

[该贴被oojdon于2008-11-14 21:37修改过]

banq
2008-11-15 10:14
>提供了支持多种事务API(JPA、Hibernate、JDO和JTA)的可扩展事务管理抽象,

不要将事务搞神秘,事务基本只有两个JTA长事务(包括跨段2PC事务)和JDBC短事务,Hibernate本质就是JDBC+缓存。事务是JavaEE基础功能,可以下载JavaEE标准看看,和任何框架都不搭架(除非专门提供事务的框架)。

Spring和EJB一样,只是提供事务的AOP切入方式,也就是说,无需自己手工写transaction.begin,因为AOP拦截器做了。Spring本身没有提供除JTA/JDBC事务以外任何神秘新的功能。

如果了解AOP,就知道:transaction.begin不在当前程序写,只是移到拦截器中写,写的地方不一样了。这就是区别,因为不需要你在程序自己写transaction.begin(这叫显式调用),也就是隐式调用JTA了,从你眼前隐去了。这个区别就是对JTA事务调用方式的不同,而不是JTA事务本身的不同,不要将这两者混同在一起。

不要把"JPA、Hibernate、JDO和JTA"混同在一起,JTA是JavaEE基础功能,而JPA、Hibernate、JDO只是持久化框架,和JTA无关。也就是说,JPA、Hibernate、JDO没有JTA也可以用,JTA没有JPA、Hibernate、JDO也可以用。

所以,如果你搞不定JTA,就不急于使用它。JTA通常在Service或业务层来使用(transaction.beigin显式调用或AOP/EJB的隐式调用),可以跨多个JDBC或其他资源,这个可以参考本站事务标签了解一下。

因为JTA在业务层使用,才可能和Spring/EJB这样框架有点关系,这个关系也就是显式调用或隐式调用的关系,就像两个男女,本是独立的,发生关系了,要么是显式声明的,登记结婚大张旗鼓(表现为需要写代码),要么就是偷情隐式的,悄悄的(表现为不需要写代码了)。

事务是JavaEE中最复杂的知识,它和原子性 多线程 锁等有关(所以才有取款机吐钱,记录数据没变化等不一致性,这些都充分说明国人对事务安全非常薄弱),说大了也和并行计算有关(在多个CPU同时执行你的程序情况下,如何保证你业务真正原子性和唯一性,又不能丧失多线程多核的优势。)。这个世界上最缺的就是编写并发计算的人,这是有道理的。

另外,要注意:框架和JavaEE组件要有区分。框架实际就是把这些组件扒拉扒拉在一起,方便或约束你使用,从设计上让你的程序更所谓优雅,当然也有弄巧成拙,搞复杂了,Spring至少是这样,一些人以为复杂就是优雅实际被误导(最可悲就是不自知),所以Spring没热多长时间,Ruby on Rails以其简洁性成为热点,这是有原因的,现在Scala又开始热起来,因为大家发现RoR慢啊,如果能够使用多核的DSL多好啊,设计又优雅性能又优雅,双优雅,这才是真优雅。

[该贴被banq于2008-11-15 10:50修改过]

xmuzyu
2009-05-11 20:06
学习JTA最好看看JTA规范,好像只有71页,我以前学习JTA的时候就是直接看的规范,网上很多文章都是越看越糊涂。所以建议学习JTA的人看看JTA规范。

猜你喜欢