Hibernate事务处理机制
Hibernate事务处理机制(1)
Hibernate是对JDBC的轻量级对象封装,Hibernate本身是不具备Transaction处理功能的,Hibernate的
Transaction实际上是底层的JDBC Transaction的封装,或者是JTA Transaction的封装,下面我们详细
的分析:
Hibernate可以配置为JDBCTransaction或者是JTATransaction,这取决于你在hibernate.properties中
的配置:
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
如果你什么都不配置,默认情况下使用JDBCTransaction,如果你配置为:
hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
将使用JTATransaction
不管你准备让Hibernate使用JDBCTransaction,还是JTATransaction,我的忠告就是什么都不配,将让
它保持默认状态,如下:
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
在下面的分析中我会给出原因。
一、JDBC Transaction
看看使用JDBC Transaction的时候我们的代码例子:
Session session = sf.openSession();
Transaction tx = session.beginTransactioin();
...
session.flush();
tx.commit();
session.close();
这是默认的情况,当你在代码中使用Hibernate的Transaction的时候实际上就是JDBCTransaction。那么
JDBCTransaction究竟是什么东西呢?来看看源代码就清楚了:
Hibernate2.0.3源代码中的类
net.sf.hibernate.transaction.JDBCTransaction:
public void begin() throws HibernateException {
...
if (toggleAutoCommit) session.connection().setAutoCommit(false);
...
}
这是启动Transaction的方法,看到 connection().setAutoCommit(false) 了吗?是不是很熟悉?
再来看
public void commit() throws HibernateException {
...
try {
if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush();
try {
session.connection().commit();
committed = true;
}
...
toggleAutoCommit();
}
这是提交方法,看到connection().commit() 了吗?下面就不用我多说了,这个类代码非常简单易懂,
通过阅读使我们明白Hibernate的Transaction都在干了些什么?我现在把用Hibernate写的例子翻译成
JDBC,大家就一目了然了:
Connection conn = ...; <--- session = sf.openSession();
conn.setAutoCommit(false); <--- tx = session.beginTransactioin();
... <--- ...
conn.commit(); <--- tx.commit(); (对应左边的两句)
conn.setAutoCommit(true);
conn.close(); <--- session.close();
看明白了吧,Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫无神秘可言,只不过在
Hibernate中,Session打开的时候,就会自动conn.setAutoCommit(false),不像一般的JDBC,默认都是
true,所以你最后不写commit也没有关系,由于Hibernate已经把AutoCommit给关掉了,所以用
Hibernate的时候,你在程序中不写Transaction的话,数据库根本就没有反应。
(未完待续)
Hibernate事务处理机制(2)
责任编辑:admin 更新日期:2005-8-6
Hibernate事务处理机制(2)
二、JTATransaction
如果你在EJB中使用Hibernate,或者准备用JTA来管理跨Session的长事务,那么就需要使用
JTATransaction,先看一个例子:
javax.transaction.UserTransaction tx = new InitialContext().lookup
("javax.transaction.UserTransaction");
Session s1 = sf.openSession();
...
s1.flush();
s1.close();
...
Session s2 = sf.openSession();
...
s2.flush();
s2.close();
tx.commit();
这是标准的使用JTA的代码片断,Transaction是跨Session的,它的生命周期比Session要长。如果你在
EJB中使用Hibernate,那么是最简单不过的了,你什么Transaction代码统统都不要写了,直接在EJB的
部署描述符上配置某某方法是否使用事务就可以了。
现在我们来分析一下JTATransaction的源代码, net.sf.hibernate.transaction.JTATransaction:
public void begin(InitialContext context, ...
...
ut = (UserTransaction) context.lookup(utName);
...
看清楚了吗? 和我上面写的代码 tx = new Initial Context?().lookup
("javax.transaction.UserTransaction"); 是不是完全一样?
public void commit() ...
...
if (newTransaction) ut.commit();
...
JTATransaction的控制稍微复杂,不过仍然可以很清楚的看出来Hibernate是如何封装JTA的Transaction
代码的。
但是你现在是否看到了什么问题? 仔细想一下,Hibernate Transaction是从Session中获得的,tx =
session.beginTransaction(),最后要先提交tx,然后再session.close,这完全符合JDBC的
Transaction的操作顺序,但是这个顺序是和JTA的Transactioin操作顺序彻底矛盾的!!! JTA是先启
动Transaction,然后启动Session,关闭Session,最后提交Transaction,因此当你使用JTA的
Transaction的时候,那么就千万不要使用Hibernate的Transaction,而是应该像我上面的JTA的代码片
断那样使用才行。
总结:
1、在JDBC上使用Hibernate
必须写上Hibernate Transaction代码,否则数据库没有反应。此时Hibernate的Transaction就是
Connection.commit而已
2、在JTA上使用Hibernate
写JTA的Transaction代码,不要写Hibernate的Transaction代码,否则程序会报错
3、在EJB上使用Hibernate
什么Transactioin代码都不要写,在EJB的部署描述符里面配置
|---CMT(Container Managed Transaction)
|
|---BMT(Bean Managed Transaction)
|
|----JDBC Transaction
|
|----JTA Transaction
--------------------------------------------------------------------------------
提问:
javax.transaction.UserTransaction tx = new InitialContext().lookup
("javax.transaction.UserTransaction");
Session s1 = sf.openSession();
...
s1.flush();
s1.close();
...
Session s2 = sf.openSession();
...
s2.flush();
s2.close();
tx.commit();
s1不关闭,使用s2进行操作的代码中使用s1可不可以(我觉得这样更加节约资源,不需要反复的连接、
关闭)
但sf.opengSession()时,并没有setAutoCommit(false),我想问的是,如果不编写任何事务代码,如:
Session s = sf.openSession();
......
s.close();
数据库会不会有反应(此时应该是默认AutoCommit为true)。
不会有反应。在sf.openSession() 创建Session实例的时候,就已经调用了conn.setAutoCommit(false)
了。
另外,我想问一下:
1. s.flush()是不是必须的
2. s.close()是不是一定要关闭
--------------------------------------------------------------------------------
回答:
s.flush不是必须的,s.close()会调用一次s.flush()
s.close()正常情况下应该关闭,除非你是用ThreadLocal管理Session。
s1不关闭,使用s2进行操作的代码中使用s1可不可以(我觉得这样更加节约资源,不需要反复的连接、
关闭)
在这个例子中看不出来JTA的作用。
假设
Class A {
find() {
Session s1 = sf.openSession();
...
s1.flush();
s1.close();
}
}
Class B {
find() {
Session s2 = sf.openSession();
...
s2.flush();
s2.close();
}
}
Main {
tx = ...;
A.find();
B.find();
tx.commit();
} (全文完)
如何学习Hibernate
责任编辑:admin 更新日期:2005-8-6
如何学习Hibernate
Hibernate入门容易,掌握精通我也不敢自夸。我第一遍看Hibernate文档的时候也觉得很吃力,但不是
因为Hibernate难掌握而感到吃力,是因为Hibernate文档处处都是持久层设计的经验和最佳实践。
Hibernate文档准确的来说,绝大部分内容都在讲对象的持久层设计,而不是简单的Hibernate使用,使
用问题查Java doc就够了。所以学习Hibernate,主要是在学习持久层的设计模式,如果你把Hibernate
文档都看完了,还整天只会提那些 Hibernate的配置问题,Hibernate的类调用问题,我觉得这样的人还
没有真正的入门,算是白学了。
我对Hibernate 的那些配置也不是特别纯熟,每次写hbm,都要对照文档一点点的检查;类调用参数也不
太记得,写代码也要Java doc随时备查。但是我在学习Hibernate的时候即集中所有精力来理解
Hibernate的运行原理,集中精力来掌握持久层设计应该把握的原则和技巧,这些才对我是最重用的东西
。毫不夸张的说,学习完Hibernate,我对JDBC的编程也提高了一大截,更不要说对于J2EE架构的持久层
的框架设计,基本上是了然于胸了,即使将来换了API,不用Hibernate的,改用JDO,Castor什么的,这
些经验一样照用。
学习Hibernate主要不是在学习Hibernat怎么配置,用工具怎么生成hbm文件,如果你把重点放在这里,
基本上等于白学了Hibernate。Hibernate的精华在于无与伦比的灵巧的对象持久层设计,这些持久层设
计经验不会因为你不用Hibernate而丧失掉,我自己学习Hibernate,已经明显感觉到对持久层设计能力
已经长了很多经验值了,这些经验甚至不光可以用在Java上,用在.net上也是一样。所以Hibernate配置
的学习,我只是简单看看,用的时候知道到那里去查就行了,一堆复杂的生成工具我根本就看都不去看
,这样算下来,掌握Hibernate的配置,可以用Hibernate来替代JDBC写程序,不过花上3天时间就足够了
。我想3天时间对你来说不算很奢侈的学习代价吧。
为什么我这么强调学习Hibernate的对象持久层设计理念呢?那就看你的理想是想一辈子做一个程序员呢
?还是想向更高的方向发展呢?从纯做技术的角度来说,职业发展的最高点是“系统架构师”,Bill
Gates不是还叫做微软的首席系统架构师吗?System Architect职位需要的是你的学习和领悟能力,如果
你不能把学习Hibernate得到的设计经验运用到其它地方,那么你是失败的,也没有资格做 System
Architect。
不管JDO也好,Hibernate也好,TopLink也好,CocoBase也好,还是 Castor,还是什么Torque,OJB,软
件的使用和配置方法可以各异,但本质上都是ORM,都是对JDBC的对象持久层封装,所以万变不离其宗,
如果你完整的学习和掌握Hibernate花了1个月的时间,那么你再学习OJB的时间不应该超过1个星期,因
为你已经把对象持久层设计都了然于胸了,你需要的只是熟悉一下OJB的API和配置罢了,至于怎么运用
OJB进行持久层的开发你早就已经熟悉了。
所以当你掌握了两种以上的ORM,你应该能够不拘于使用的ORM软件的限制,设计出适合于你的项目的持
久层来,这才是System Architect的水准。用金庸小说来打个比方来说吧,张无忌学太极剑,只记剑意
,不记剑招,这才是真正的高手,而低手就只会去学习剑招,而不去领会剑招背后蕴含的剑意,所以一
辈子都是低手,永远不能真正学会太极剑。所以周颠看到张三丰第二次演示太极剑,招式完全不同就以
为是另一套东西,其实本质上都一样。学习Hibernate也不要舍本逐末的去学各种五花八门的工具,重点
掌握它的对象持久层设计理念。
Struts中不同的Action和ActionForm组合
责任编辑:admin 更新日期:2005-8-6
Struts中不同的Action和ActionForm组合
在TSS.com上看到一篇好文,有关Struts使用中各种不同的Action和ActionForm组合的利弊。我先消化一
下,整理好,供大家参考。原文标题:Struts action mappings: Divide Et Impera,作者:Michael
Juravlev。在TSS上的URL:http://www.theserverside.com/articles/article.tss?
l=StrutsActionMapping
说明:阅读本文需要一定的Struts基础。
注:文中小写的action不一定代表具体的Struts Action类,有时也指作为一个整体的action mapping。
[1] 完整的action
<action path="/aFullAction"
type="somePackage.someActionClass">
name="someForm"
input="someJSP.jsp"
<forward name="successful" path="someJSP.jsp"/>
<forward name="failed" path="someOtherJSP.jsp"/>
</action>
首先,Struts的ActionServlet接收到一个请求,然后根据struts-config.xml的配置定位到相应的
mapping(映射);接下来如果form的范围是request或者在定义的范围中找不到这个form,创建一个新
的form实例;取得form实例以后,调用其reset()方法,然后将表单中的参数放入form,如果validate属
性不为false,调用validate()方法;如果validate()返回非空的ActionErrors,将会被转到input属性
指定的URI,如果返回空的ActionErrors,那么执行Action的execute()方法,根据返回的ActionForward
确定目标URI。
这样做的效果是:execute()仅当validate()成功以后才执行;input属性指定的是一个URI。
[2] 仅有Form的action
<action path="/aFormOnlyAction"
type="org.apache.struts.actions.ForwardAction"
name="someForm"
input="someJSP.jsp"
parameter="someOtherJSP.jsp"
/>
首先,Struts会在定义的scope搜寻someForm,如果找到则重用,如果找不到则新建一个实例;取得form
实例以后,调用其reset()方法,然后将表单中的参数放入form,如果validate属性不为false,调用
validate()方法;如果validate()返回非空的ActionErrors,将会被转到input属性指定的URI,如果返
回空的ActionErrors,那么转到parameter属性指定的目标URI。
这样做的效果是:没有action类可以存放我们的业务逻辑,所以所有需要写入的逻辑都只能写到form的
reset()或者validate()方法中。validate()的作用是验证和访问业务层。因为这里的action映射不包括
forward(也没有意义),所以不能重定向,只能用默认的那个forward。这种仅有form的action可以用
来处理数据获取并forward到另一个JSP来显示。
[3] 仅有Action的action
<action path="/anActionOnlyAction"
type="somePackage.someActionClass">
input="someJSP.jsp"
<forward name="successful" path="someJSP.jsp"/>
<forward name="failed" path="someOtherJSP.jsp"/>
</action>
首先,ActionServlet接收到请求后,取得action类实例,调用execute()方法;然后根据返回的
ActionForward在配置中找forward,forward到指定的URI或action。
这样做的效果是:没有form实例被传入execute()方法,于是execute()必须自己从请求中获取参数。
Action可以被forward或者重定向。这种action不能处理通过HTML FORM提交的请求,只能处理链接式的
请求。
[4] 仅有JSP的action
<action path="/aJSPOnlyAction"
type="org.apache.struts.actions.ForwardAction"
parameter="someOtherJSP.jsp"
/>
首先,ActionServlet接到请求后调用ForwardAction的execute()方法,execute()根据配置的parameter
属性值来forward到那个URI。
这样做的效果是:没有任何form被实例化,比较现实的情形可能是form在request更高级别的范围中定义
;或者这个action被用作在应用程序编译好后充当系统参数,只需要更改这个配置文件而不需要重新编
译系统。
[5] 两个action对应一个form
<action path="/anAction"
type="somePackage.someActionClass">
name="someForm"
input="someJSP.jsp"
<forward name="successful" path="/anotherAction.do"/>
</action>
<action path="/anotherAction"
type="somePackage.someOtherActionClass">
name="someForm"
input="someOtherJSP.jsp"
<forward name="successful" path="someResultJSP.jsp"/>
</action>
就每个单独的action来讲,处理上并没有和完整的action有什么实质的区别。这个组合模式可以被用来
传递命令对象(form)。需要注意的是在后一个action中同样会调用form的reset()和validate()方法,
因此我们必须确保form中的信息不被重写。
处理的方式大致分为两种:a) 在request中放入一个指示器表明前一个action有意向后一个action传递
form,从而在后一个action可以保留那个form中的值,这一方式只能在使用forward时使用。b) 当使用
redirect而不是forward时,可以把指示器放在session或更高的级别,在命令链的最后一环将这个指示
器清除。
[6] 两个action对应两个form
<action path="/anAction"
type="somePackage.someActionClass">
name="someForm"
input="someJSP.jsp"
<forward name="successful" path="/anotherAction.do" redirect="true"/>
</action>
<action path="/anotherAction"
type="somePackage.someOtherActionClass">"
name="someOtherForm"
input="someOtherJSP.jsp"
<forward name="successful" path="someResultJSP.jsp"/>
</action>
这个组合方式跟前一种在流程上没有太大区别,只是我们现在对于两个action分别提供了form,于是代
码看上去更加清晰。于是我们可以分别处理WEB应用程序的输入和输出。值得注意的是,后一个action同
样会尝试往form中写入那些参数,不过我们可以这样处理:a) 在后一个form中使用另一套属性名;b)
只提供getter而不提供setter。
大致的处理是这样:
前一个action接收输入、验证、然后将数据写入业务层或持久层,重定向到后一个action,后一个
action手动的从业务层/持久层取出数据,写入form(通过其他方式),交给前台JSP显示。
这样做的好处是不必保留输入form中的值,因此可以使用redirect而不是forward。这样就降低了两个
action之间的耦合度,同时也避免了不必要的重复提交。