如果在你说的add和reduce中采用JTA,而transfer中也采用JTA,会造成嵌套事务(这会出错吧?),而CMT容器会挂起外部事务或者不启动新事务,其实Hibernate配置为JTA事务之后也是获得容器UerTransaction,大致和上面的情况差不多,当然和用JDBC事务效果也一样,没法利用JTA的能力
在这里Hibernate的缺陷手动的JTA也存在,用UserTransaction也难很好利用JTA跨连接的作用,我想hibernate做到这个程度足够了,发挥空间留给了容器
BTW,我没什么经验,这里牛人很多,还请指教:)
> guty,你好!我也在研究Hibernate,并且对Hibernate非常感
> 巳ぁW罱钊氲难芯苛Hibernate的事务机制,感觉存在很大
> 娜毕荨N也煌饽闱懊娑Hibernate事务机制的肯定,对EJB?> 务的否定。
>
> 下面详细阐述一下我对Java中的事务处理以及Hibernate事务?> 观点。
>
> Hibernate的Transaction可以是基于数据库驱动提供的JDBC
> Transaction,也可以是基于App Server提供的JTA
> Transaction的,根据配置文件来配置,默认使用JDBC
> Transaction。但实际上只能使用JDBC
> Transaction。为什么呢?
>
> 因为Hibernate的Transaction是Session创建的,Transaction
> 芷诒Session短,更精确的来说,Transaction要比Sessi
> n下面的Connection短,也就是说Transaction必须在一个Conn
> ction连接会话中启动和结束,如下:
>
> Session s = sf.openSession();
> Transaction tx = s.beginTransaction();
> ...
> s.flush();
> tx.commit();
> s.close();
>
> 因此tx.commit()实质上就是Connection.commit()操作,而数
> 菘馐导手葱腥缦JDBC操作:
>
> Connection conn = ds.getConnection();
> conn.setAutoCommit(false);
> ...
> conn.commit();
> conn.close();
>
> 所以Hibernate的Transaction根本就是换汤不换药的JDBC事务
> 怼K淙辉谂渲梦募校tx可以配置为使用App Server
> JTA,但是JTA的事务范围是跨连接的,跨方法调用的。也就是
> JTA的事务必须要先于Session启动,晚于Session关闭后提交
> 缦拢?>
> tx.begin(); // JTA Transaction
> s = sf.openSession;
> ...
> s.close();
> tx.close();
>
> 而Hibernate中,tx必须晚与Session创建,先于Session关闭?> 前提交。因此当Hibernate的tx是配置使用App
> Server的JTA的Transaction,就出现冲突了。
>
> 此时,再按照上面的代码执行,App
> Server就会报错。必须先s.close(),然后再tx.commit()。当
> 徽庋灿形侍猓褪session没有完整的垃圾回收,会造成内
> 嫘孤┪侍狻?>
> 另外即便上面的操作能够成功,但是由于Hibernate中tx是从s
> ssion获得,因此,JTA的事务范围也会被限制在Connection生
> 芷谀冢敲春Connection事务范围就完全一样了,丧失了J
> A的跨方法调用,跨连接,甚至跨数据库的事务安全能力。
>
> 想像一个简单的情况,银行转账100元:
> bank.transfer(account1, account2, 100)
> {
> account1.reduce(100);
> account2.add(100);
> }
> 该方法分别调用account1的reduce方法和account2的add方法?>
>
> 首先,在account类的reduce和add方法中必须使用tx(等于是c
> nn.commit()),来保证数据库修改成功,如果你不调用tx.com
> it()的话,其实就是不向数据库发送commit的SQL语句,数据?> 根本就不进行真正的写操作。
>
> 同时bank.transfer方法也必须保证操作的原子性,幢Vぷ
> 的安全。所以也必须使用tx,就是这样:
> {
> s = sf.openSession();
> tx = s.beginTransaction();
> account1.reduce(100);
> account2.add(100);
> tx.commit();
> s.close();
> }
> 但是实际上在该方法中tx根本不起作用。因为它们根本都不在
> 桓Session中进行操作,而是分别启动了3个Session,在各自
> Session中commit自己单独的数据库操作,因而根本无法保障
> 说陌踩?除非分别把s和tx保持在ThreadLocal中,不过这
> 嵩斐墒挛穹段Ч螅肽延诟伲菀状嬖谇痹诘哪诖嫖
> 题)
>
> 为了保持转账的安全,你只能直接使用App
> Server的JTA(此时必须配置hibernate.properties使用App
> Server的DataSource,不能使用Hibernate自带的那些连接池,
> 裨JTA不起作用),就是这样:
> {
> tx = (UserTransaction)
> ) ctx.lookup("javax.transaction.UserTransaction");
> tx.begin();
> account1.reduce(100);
> account2.add(100);
> tx.commit();
> }
>
> 但是此时App Server又会报错,因为在JTA中,不能使用JDBC
> Transaction,也就是必须把acount中的tx的操作都去掉。但?> 一旦去掉,account的reduce和add方法在单独调用的时候,根
> 揪筒换崛バ词菘猓蛭揪兔挥邢蚴菘commit,两难?> 处境!
>
> 总结如下:
>
> Hibernate的Transaction就是JDBC的conn.commit,而Session
> 苁悄习Connection.setAutoCommit(false),你要不用Hiber
> ate的tx的话,单独的方法调用根本就不写数据库,你必须tx.
> ommit()来保证数据库提交。但是你要用Hibernate的tx的话,
> 憔捅匦敕牌褂JTA或者EJB的事务功能,这等于是放弃了跨?> 法调用,跨数据库连接的安全保障,就像上面的银行转账的例
> 樱忝话旆ūVぷ说某晒Α?>
> 这样的问题,只有使用EJB的事务处理功能才能很好的解决:?> 先,在DAO代码中放弃所有的Hibernate的tx,不要写任何tx代
> 耄蝗缓笤Session
> Bean中调用DAO到的方法声明为Required,那么一切问题都解?> 了。当然必须要由Session Bean来调用DAO才行。
>
> 我不明白你怎么会觉得EJB的事务是失败的?EJB的事务当然不
> 芾挠茫械EJB方法都声明为Required只有不懂的人才会这
> 觥EJB的事务是智能化和动态的,解决了很多事务控制的难
> 狻6Hibernate的事务控制简直就是一个败笔。
如果在MVC中,在baseaction中是否好一点?
|
大概如此,也比较简单吧,没有实践过,当然也可以用AOP等,回滚和启动事务也可以在其他地方进行。
也许还可以更方便,精力不好就不考虑了,应当适合事务比较简单的情况。
最彻底的解决方法是透明地支持method,可以用aop,也可以用filter。前者比较通用,但概念深奥,不利于理解,如果是web环境,filter是一个比较理想容易实施的机制。
另外,webwork的Action是可以拿掉的,可以写一个通用的Action来访问POJO的方法。Webwork2中最有价值的变化就是采用了OGNL(www.ognl.org),比起el来不知道强了多少倍。
>另外,webwork的Action是可以拿掉的,可以写一个通用的Action来访问POJO的方法。
也就是直接讲request路由到业务对象的某个方法吧?也许在特定应用中可行,但是我想在这些开源的通用框架中会有很多不便吧。如果这样,你的业务对象中可能还有servlet之类的api,还要访问session,request,response之类,这样是非常糟糕的。我觉得很有必要抽象出一层专门处理web相关的事情,更不用说业务对象是EJB或其他远程对象,维护,扩展都可能非常不利。我个人也不喜欢像xpetstore那样把逻辑都放在action里面。
好像你讲过把类似action的方法放到一个类,像webwork这样每个request初始化一个action的模式,每个action都和特定的request,response,form联系,所以用action这种模式是非常简单易行的,把多个action的方法组合到一起反而非常糟糕,因为我觉得action和每个request和数据是一一对应的。
struts的action是被复用的,参数都是action的回调方法传递,所以可以把类似的action组合成一个,事实上struts1.1就是这样,但我个人还是喜欢单独处理action,便于修改,和swing的模式也比较类似,事实证明是成熟的有效的,因为我没有复杂实践经验。
webwork的el和jsp2的el相比如何?
是啊,我也知道你是谁了,放假回家了?
>也就是直接讲request路由到业务对象的某个方法吧?也许在特定应用中可行,但是我想在这些开源的通用框架中会有很多不便吧。如果这样,你的业务对象中可能还有servlet之类的api,还要访问session,request,response之类,这样是非常糟糕的
你的考虑有道理,但是如果把数据绑定技术实现到一定程度,这种问题就会大大减少。在我们目前的项目里面,业务逻辑完全是用普通的Java方法,上面直接是带有数据绑定标签的JSP界面,不需要写任何中间处理代码。还举用户注册的例子:
[业务逻辑]
public class SecurityService
{
...
public User registerUser(User user,String passwd,String confirm)
{
//检查密码
checkConfirm(passwd,confirm);
//利用hibernate的api保存user
HibernateSession.currentSession().save(user);
//创建并保存密码
Password password = new Password();
password.setCipher(decrypt(cleartext));
HibernateSession.currentSession().save(password);
user.setPassword(password);
//记录日志
LogService.getInstance().addLog("registerUser",user.name);
return user;
}
}
SecurityService是一个Java Bean (有缺省的构造方法),业务方法是普通的Java方法,内部使用Hibernate做persistence,由于在web filter中控制事务,所以方法内部不需要事务控制代码。
[页面]
为了简化起见,我把其中的html标签基本上去掉了。你可以看到,我的界面控件几乎都是用的webwork的界面标签,例如
剩下的还有一个问题是,页面如何知道去调用哪个后台方法?这个和其它framework类似,通过配置文件。
[配置文件]
method="com.brs.magic.security.SecurityService.registerUser(user,password,confirm)"
input="/system/user_detail.jsp">
这个配置文件,把registerUser.action映射到SecurityService.registerUser方法上,并且在方法声明中描述了参数的名称(user,password,confirm),这些名称被用作界面数据绑定。
>webwork的el和jsp2的el相比如何
ognl>apache(jsp2.0) el>webwork1.x el
语言的差距体现在很多细微但关键时刻又很要命的地方,apache的我没有实际用过,webwork的el在使用时经常觉得绊手绊脚,但ognl就很好,和hibernate的ql语句一样,都让我觉得没有障碍。
|
> 是啊,我也知道你是谁了,放假回家了?
hehe...
> >也就是直接讲request路由到业务对象的某个方法吧?也许在
> 囟ㄓτ弥锌尚校俏蚁朐谡庑┛吹耐ㄓ每蚣苤谢嵊泻芏嗖
> 便吧。如果这样,你的业务对象中可能还有servlet之类的api
> 挂梦session,request,response之类,这样是非常糟?> 的
>
确实挺简单的,数据绑定也很有用,如果都是直接调用POJO,EJB,WS,都可以通用action进行转发,再用配置文件,我觉得runtime attribute也可以
不过多写action也不麻烦:
execute(..)
{
securiy.rigisterUser(..);
}
多写点代码,就少写点XML:-)
对我来说,很多情况还是要在action中处理servlet相关的,不知道你是怎么处理,比如
execute()
{
ip = request.getRemoteAddr();
locale = request.getLocale();
security.logonUser(..);
session.set(user);
}
> 你的考虑有道理,但是如果把数据绑定技术实现到一定程度,
> 庵治侍饩突岽蟠蠹跎佟T谖颐悄壳暗南钅坷锩妫滴衤呒耆
> 是用普通的Java方法,上面直接是带有数据绑定标签的JSP界?> ,不需要写任何中间处理代码。还举用户注册的例子:
>
> [业务逻辑]
> public class SecurityService
> {
> ...
>
> public User registerUser(User user,String
> ing passwd,String confirm)
> {
> //检查密码
> checkConfirm(passwd,confirm);
>
> //利用hibernate的api保存user
> HibernateSession.currentSession().save(user);
>
> //创建并保存密码
> Password password = new Password();
> password.setCipher(decrypt(cleartext));
>
>
> HibernateSession.currentSession().save(password);
> user.setPassword(password);
>
> //记录日志
>
>
>
>
> LogService.getInstance().addLog("registerUser",user.n
> me);
>
> return user;
> }
> }
>
> SecurityService是一个Java Bean
> (有缺省的构造方法),业务方法是普通的Java方法,内部使用
> ibernate做persistence,由于在web
> filter中控制事务,所以方法内部不需要事务控制代码。
>
> [页面]
>
>
> 为了简化起见,我把其中的html标签基本上去掉了。你可以看
> 剑业慕缑婵丶负醵际怯玫webwork的界面标签,例如
>
> 剩下的还有一个问题是,页面如何知道去调用哪个后台方法?
> 飧龊推渌framework类似,通过配置文件。
>
> [配置文件]
>
>
>
>
>
>
>
>
>
> method="com.brs.magic.security.SecurityService.regist
> rUser(user,password,confirm)"
> input="/system/user_detail.jsp">
>
>
> 这个配置文件,把registerUser.action映射到SecurityServi
> e.registerUser方法上,并且在方法声明中描述了参数的名称
> user,password,confirm),这些名称被用作界面数据绑定。
>
>
> >webwork的el和jsp2的el相比如何
>
> ognl>apache(jsp2.0) el>webwork1.x el
> 语言的差距体现在很多细微但关键时刻又很要命的地方,apac
> e的我没有实际用过,webwork的el在使用时经常觉得绊手绊脚
> ognl就很好,和hibernate的ql语句一样,都让我觉得没有
> 习?>
>
嗯,需要的时候我一定好好学学ognl,仅仅是只需要j2ee13 server就比jsp2 el有吸引力