??xml version="1.0" encoding="utf-8" standalone="yes"?>
以下以《Java与模式》中的示例ؓ例:
抽象角色Q?
abstract public class Subject {
abstract public void request();
}
真实角色Q实CSubject的request()Ҏ?
public class RealSubject extends Subject {
public RealSubject() { }
public void request() {
System.out.println("From real subject.");
}
}
代理角色Q?
public class ProxySubject extends Subject {
private RealSubject realSubject; //以真实角色作Z理角色的属?
public ProxySubject() { }
public void request() { //该方法封装了真实对象的requestҎ
preRequest();
if( realSubject == null ) {
realSubject = new RealSubject();
}
realSubject.request(); //此处执行真实对象的requestҎ
postRequest();
}
private void preRequest() {
//something you want to do before requesting
}
private void postRequest() {
//something you want to do after requesting
}
}
客户端调用:
Subject sub=new ProxySubject();
Sub.request();
׃上代码可以看出,客户实际需要调用的是RealSubjectcȝrequest()ҎQ现在用ProxySubject来代?RealSubjectc,同样辑ֈ目的Q同时还装了其他方?preRequest(),postRequest())Q可以处理一些其他问题?
另外Q如果要按照上述的方法用代理模式,那么真实角色必须是事先已l存在的Qƈ其作ؓ代理对象的内部属性。但是实际用时Q一个真实角色必d应一?代理角色Q如果大量用会Dcȝ急剧膨胀Q此外,如果事先q不知道真实角色Q该如何使用代理呢?q个问题可以通过Java的动态代理类来解冟?br />
2.动态代理类
Java动态代理类位于Java.lang.reflect包下Q一般主要涉及到以下两个c:
(1). Interface InvocationHandlerQ该接口中仅定义了一个方法ObjectQinvoke(Object obj,Method method, Object[] args)。在实际使用ӞW一个参数obj一般是指代理类Qmethod是被代理的方法,如上例中的request()QargsҎ的参数数l?q个抽象Ҏ在代理类中动态实现?
(2).ProxyQ该cd为动态代理类Q作用类g上例中的ProxySubjectQ其中主要包含以下内容:
Protected Proxy(InvocationHandler h)Q构造函敎ͼ估计用于l内部的h赋倹{?
Static Class getProxyClass (ClassLoader loader, Class[] interfaces)Q获得一个代理类Q其中loader是类装蝲器,interfaces是真实类所拥有的全部接口的数组?
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)Q返回代理类的一个实例,q回后的代理cd以当作被代理cM?可用被代理cȝ在Subject接口中声明过的方??
所谓Dynamic Proxy是这样一UclassQ它是在q行时生成的classQ在生成它时你必L供一linterfacel它Q然后该class宣U它实现了这?interface。你当然可以把该class的实例当作这些interface中的M一个来用。当然啦Q这个Dynamic Proxy其实是一个ProxyQ它不会替你作实质性的工作Q在生成它的实例时你必须提供一个handlerQ由它接实际的工作?/p>
在用动态代理类Ӟ我们必须实现InvocationHandler接口Q以W一节中的示例ؓ例:
抽象角色(之前是抽象类Q此处应改ؓ接口)Q?
public interface Subject {
abstract public void request();
}
具体角色RealSubjectQ?br />public class RealSubject implements Subject{
public RealSubject(){}
public void request(){
System.out.println("From real subject.");
}
}
代理处理器:
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class DynamicSubject implements InvocationHandler {
private Object sub;
public DynamicSubject() {}
public DynamicSubject(Object obj) {
sub = obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling " + method);
method.invoke(sub,args);
System.out.println("after calling " + method);
return null;
}
}
该代理类的内部属性ؓObjectc,实际使用旉过该类的构造函数DynamicSubject(Object obj)对其赋|此外Q在该类q实CinvokeҎQ该Ҏ中的
method.invoke(sub,args);
其实是调用被代理对象的要被执行的ҎQ方法参数sub是实际的被代理对象,args为执行被代理对象相应操作所需的参数。通过动态代理类Q我们可以在调用之前或之后执行一些相x作?
客户端:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Client {
static public void main(String[] args) throws Throwable {
RealSubject rs = new RealSubject(); //在这里指定被代理c?
InvocationHandler ds = new DynamicSubject(rs);
Class cls = rs.getClass();
//以下是一ơ性生成代?br /> Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds );
subject.request();
}
E序q行l果Q?br />before calling public abstract void Subject.request()
From real subject.
after calling public abstract void Subject.request()
通过q种方式Q被代理的对?RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变Q控制的方式(DynamicSubjectc?也可以动态改变,从而实C非常灉|的动态代理关pR?br />
转蝲地址Q?br />http://www.java3z.com/cwbwebhome/article/article2/2300.jsp?id=846
http://www.csdn.net/develop/Article/26/26750.shtm
public HashMap getData() {
return data;
}
public void setData(HashMap data) {
this.data = data;
}
public String get(String key) {
return (String) data.get(key);
}
public void set(String key, Object value) {
data.put(key, value);
}
}
struts-config.xml中写Q?br /> <form-beans>
<form-bean name="baseform" type="com.web.system.base.BaseForm"/>
</form-beans>
JTA主要用于分布式的多个数据源的两阶D|交的事务,而JDBC的Connection提供的单个数据源的事? 后者因为只涉及C个数据源,所以其事务可以由数据库自己单独实现, 而JTA事务因ؓ其分布式和多数据源的Ҏ? 不可能由M"一?数据源实C? 因此JTA中的事务是由"事务理?实现?它会在多个数据源之间l筹事务,具体使用的技术就是所谓的"两阶D|?, 一般JTA事务都是用于EJB?因ؓEJB本n也是分布式的), 所以一般的应用服务器都有自q事务理器用来管理JTA事务,注意qƈ不表CEJB容器有管理事务的功能; 事实上也有单独的事务理器比如开源的Tyrex. 如果只用Tomcat做应用服务器的话是不能用JTA事务?
使用 JDBC 事务界定Ӟ您可以将多个 SQL 语句l合C个事务中。JDBC 事务的一个缺Ҏ事务的范围局限于一个数据库q接。一?JDBC 事务不能跨越多个数据库。在下面Q我们将看一下如何用 JTA q行事务界定。因?JTA 不像 JDBC 那样有名Q所以我们首先做一个简介?BR>JTA ?BR>Java 事务 API(JTA) 及其同门兄弟 Java 事务服务(Java Transaction Service JTS)?J2EE q_提供了分布式事务服务。一个分布式的事务涉及一个事务管理器和一个或者多个资源管理器。一个资源管理器是Q何类型的持久性的数据存储。事务管理器负责协调所有事务参与者之间的通信?BR>JTA 事务?JDBC 事务功能更强。JDBC 事务局限ؓ一个数据库q接Q?JTA 事务可以有多个参与者。所有下?Java q_lg都可以参?JTA 事务Q?BR>JDBC q接
JDO PersistenceManager 对象
JMS 队列
JMS 主题
企业 JavaBeans
W合 J2EE q接体系l构(J2EE Connector Architecture)规范的资源适配器?BR>
摘要Q虽然session机制在web应用E序中被采用已经很长旉了,但是仍然有很多h不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制ƈ且对在Java web application中应用session机制时常见的问题作出解答?/P>
目录Q?BR>一、术语session
二、HTTP协议与状态保?BR>三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用E序的session׃n
八、ȝ
参考文?/P>
一、术语session
在我的经验里Qsessionq个词被滥用的程度大概仅ơ于transactionQ更加有的是transaction与session在某些语境下的含义是相同的?/P>
sessionQ中文经常翻译ؓ会话Q其本来的含义是指有始有l的一pd动作/消息Q比如打电话时从拿v电话拨号到挂断电话这中间的一pdq程可以UCZ个session。有时候我们可以看到这L话“在一个浏览器会话期间Q?..”,q里的会话一词用的就是其本义Q是指从一个浏览器H口打开到关闭这个期间①。最混ؕ的是“用P客户端)在一ơ会话期间”这样一句话Q它可能指用L一pd动作Q一般情况下是同某个具体目的相关的一pd动作Q比如从d到选购商品到结账登样一个网上购物的q程Q有时候也被称Z个transactionQ,然而有时候也可能仅仅是指一ơ连接,也有可能是指含义①,其中的差别只能靠上下文来推断②?/P>
然而当session一词与|络协议相关联时Q它又往往隐含了“面向连接”和/或“保持状态”这样两个含义,“面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到Ҏ接了电话通信才能开始,与此相对的是写信Q在你把信发出去的时候你q不能确认对方的地址是否正确Q通信渠道不一定能建立Q但对发信h来说Q通信已经开始了。“保持状态”则是指通信的一方能够把一pd的消息关联v来,使得消息之间可以互相依赖Q比如一个服务员能够认出再次光的老顾客ƈ且记得上ơ这个顾客还Ơ店里一块钱。这一cȝ例子有“一个TCP session”或者“一个POP3 session”③?/P>
而到了web服务器蓬勃发展的时代Qsession在web开发语境下的语义又有了新的扩展Q它的含义是指一cȝ来在客户端与服务器之间保持状态的解决Ҏ④。有时候session也用来指q种解决Ҏ的存储结构,如“把xxx保存在session里”⑤。由于各U用于web开发的语言在一定程度上都提供了对这U解x案的支持Q所以在某种特定语言的语境下Qsession也被用来指代该语a的解x案,比如l常把Java里提供的javax.servlet.http.HttpSessionUCؓsession⑥?/P>
鉴于q种混ؕ已不可改变,本文中session一词的q用也会Ҏ上下文有不同的含义,请大家注意分辨?BR>在本文中Q用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表辑义⑤Q用具体的“HttpSession”来表达含义?/P>
二、HTTP协议与状态保?BR>HTTP协议本n是无状态的Q这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器h下蝲某些文gQ无论是客户端还是服务器都没有必要纪录彼此过ȝ行ؓQ每一ơ请求之间都是独立的Q好比一个顾客和一个自动售货机或者一个普通的Q非会员Ӟ大卖Z间的关系一栗?/P>
然而聪明(或者贪心?Q的Z很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用Q就像给有线电视加上Ҏ功能一栗这U需求一斚wqHTML逐步d了表单、脚本、DOM{客L行ؓQ另一斚w在服务器端则出现了CGI规范以响应客L的动态请求,作ؓ传输载体的HTTP协议也添加了文g上蝲、cookieq些Ҏ。其中cookie的作用就是ؓ了解决HTTP协议无状态的~陷所作出的努力。至于后来出现的session机制则是又一U在客户端与服务器之间保持状态的解决Ҏ?/P>
让我们用几个例子来描qC下cookie和session机制之间的区别与联系。笔者曾l常ȝ一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠Q然而一ơ性消?杯咖啡的Z微乎其微Q这时就需要某U方式来U录某位֮的消Ҏ量。想象一下其实也无外乎下面的几种ҎQ?BR>1、该店的店员很厉宻I能记住每位顾客的消费数量Q只要顾客一走进咖啡店,店员q道该怎么对待了。这U做法就是协议本w支持状态?BR>2、发l顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每ơ消ҎQ如果顾客出C张卡片,则此ơ消费就会与以前或以后的消费相联pv来。这U做法就是在客户端保持状态?BR>3、发l顾客一张会员卡Q除了卡号之外什么信息也不纪录,每次消费Ӟ如果֮出示该卡片,则店员在店里的纪录本上找到这个卡号对应的U录d一些消费信息。这U做法就是在服务器端保持状态?/P>
׃HTTP协议是无状态的Q而出于种U考虑也不希望使之成ؓ有状态的Q因此,后面两种Ҏ成为现实的选择。具体来说cookie机制采用的是在客L保持状态的ҎQ而session机制采用的是在服务器端保持状态的Ҏ。同时我们也看到Q由于采用服务器端保持状态的Ҏ在客L也需要保存一个标识,所以session机制可能需要借助于cookie机制来达C存标识的目的Q但实际上它q有其他选择?/P>
三、理解cookie机制
cookie机制的基本原理就如上面的例子一L单,但是q有几个问题需要解冻I“会员卡”如何分发;“会员卡”的内容Q以及客户如何用“会员卡”?/P>
正统的cookie分发是通过扩展HTTP协议来实现的Q服务器通过在HTTP的响应头中加上一行特D的指示以提C浏览器按照指示生成相应的cookie。然而纯_的客户端脚本如Javascript或者VBscript也可以生成cookie?/P>
而cookie的用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器查所有存储的cookieQ如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置Q则把该cookie附在h资源的HTTPh头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示Q如果某家分店还发行了自q会员卡,那么q这家店的时候除了要出示麦当劳的会员卡,q要出示q家店的会员卡?/P>
cookie的内容主要包括:名字Q|q期旉Q\径和域?BR>其中域可以指定某一个域比如.google.comQ相当于d招牌Q比如宝z公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.comQ可以用飘柔来做比?BR>路径是跟在域名后面的URL路径Q比?或?foo{等Q可以用某飘柔专柜做比?BR>路径与域合在一起就构成了cookie的作用范围?BR>如果不设|过期时_则表C个cookie的生命期为浏览器会话期间Q只要关闭浏览器H口Qcookie消׃。这U生命期为浏览器会话期的cookie被称Z话cookie。会话cookie一般不存储在硬盘上而是保存在内存里Q当然这U行为ƈ不是规范规定的。如果设|了q期旉Q浏览器׃把cookie保存到硬盘上Q关闭后再次打开览器,q些cookie仍然有效直到过讑֮的过期时间?/P>
存储在硬盘上的cookie可以在不同的览器进E间׃nQ比如两个IEH口。而对于保存在内存里的cookieQ不同的览器有不同的处理方式。对于IEQ在一个打开的窗口上按Ctrl-NQ或者从文g菜单Q打开的窗口可以与原窗口共享,而用其他方式新开的IEq程则不能共享已l打开的窗口的内存cookieQ对于Mozilla Firefox0.8Q所有的q程和标{N都可以共享同Lcookie。一般来说是用javascript的window.open打开的窗口会与原H口׃n内存cookie。浏览器对于会话cookie的这U只认cookie不认人的处理方式l常l采用session机制的web应用E序开发者造成很大的困扰?/P>
下面是一个goolge讄cookie的响应头的例?BR>HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html
q是使用HTTPLookq个HTTP Sniffer软g来俘LHTTP通讯U录的一部分
览器在再次讉Kgoolge的资源时自动向外发送cookie
使用Firefox可以很容易的观察现有的cookie的?BR>使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理?/P>
IE也可以设|在接受cookie前询?/P>
q是一个询问接受cookie的对话框?/P>
四、理解session机制
session机制是一U服务器端的机制Q服务器使用一U类g散列表的l构Q也可能是使用散列表)来保存信息?/P>
当程序需要ؓ某个客户端的h创徏一个session的时候,服务器首先检查这个客L的请求里是否已包含了一个session标识 - UCؓsession idQ如果已包含一个session id则说明以前已lؓ此客L创徏qsessionQ服务器按照session id把这个session索出来用(如果索不刎ͼ可能会新Z个)Q如果客Lh不包含session idQ则为此客户端创Z个sessionq且生成一个与此session相关联的session idQsession id的值应该是一个既不会重复Q又不容易被扑ֈ规律以仿造的字符Ԍq个session id被在本ơ响应中q回l客L保存?/P>
保存q个session id的方式可以采用cookieQ这样在交互q程中浏览器可以自动的按照规则把q个标识发挥l服务器。一般这个cookie的名字都是类gSEEESIONIDQ而。比如weblogic对于web应用E序生成的cookieQJSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764Q它的名字就是JSESSIONID?/P>
׃cookie可以被h为的止Q必L其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一U技术叫做URL重写Q就是把session id直接附加在URL路径的后面,附加方式也有两种Q一U是作ؓURL路径的附加信息,表现形式?A href="http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一U是作ؓ查询字符串附加在URL后面Q表现Ş式ؓhttp://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
q两U方式对于用h说是没有区别的,只是服务器在解析的时候处理的方式不同Q采用第一U方式也有利于把session id的信息和正常E序参数区分开来?BR>Z在整个交互过E中始终保持状态,必d每个客户端可能请求的路径后面都包含这个session id?/P>
另一U技术叫做表单隐藏字Dc就是服务器会自动修改表单,d一个隐藏字D,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
q种技术现在已较少应用Q笔者接触过的很古老的iPlanet6(SunONE应用服务器的前n)׃用了q种技术?BR>实际上这U技术可以简单的用对action应用URL重写来代ѝ?/P>
在谈论session机制的时候,常常听到q样一U误解“只要关闭浏览器Qsession消׃”。其实可以想象一下会员卡的例子,除非֮d对店家提出销卡,否则店家l对不会L删除֮的资料。对session来说也是一LQ除非程序通知服务器删除一个sessionQ否则服务器会一直保留,E序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会d在关闭之前通知服务器它要关闭Q因此服务器Ҏ不会有机会知道浏览器已经关闭Q之所以会有这U错觉,是大部分session机制都用会话cookie来保存session idQ而关闭浏览器后这个session id消׃Q再ơ连接服务器时也无法找到原来的session。如果服务器讄的cookie被保存到盘上,或者用某U手D|写浏览器发出的HTTPh_把原来的session id发送给服务器,则再ơ打开览器仍然能够找到原来的session?/P>
恰恰是由于关闭浏览器不会Dsession被删除,q服务器ؓseesion讄了一个失效时_当距dL上一ơ用session的时间超q这个失效时间时Q服务器可以认为客L已经停止了活动,才会把session删除以节省存储空间?/P>
五、理解javax.servlet.http.HttpSession
HttpSession是Javaq_对session机制的实现规范,因ؓ它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作ؓ例子来演C?/P>
首先QWeblogic Server提供了一pd的参数来控制它的HttpSession的实玎ͼ包括使用cookie的开关选项Q用URL重写的开关选项Qsession持久化的讄Qsession失效旉的设|,以及针对cookie的各U设|,比如讄cookie的名字、\径、域Qcookie的生存时间等?/P>
一般情况下Qsession都是存储在内存里Q当服务器进E被停止或者重启的时候,内存里的session也会被清I,如果讄了session的持久化Ҏ,服务器就会把session保存到硬盘上Q当服务器进E重新启动或q些信息能够被再次使用QWeblogic Server支持的持久性方式包括文件、数据库、客Lcookie保存和复制?/P>
复制严格说来不算持久化保存,因ؓsession实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进E中Q这样即使某个服务器q程停止工作也仍然可以从其他q程中取得session?/P>
cookie生存旉的设|则会媄响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解?/P>
cookie的\径对于web应用E序来说是一个非帔R要的选项QWeblogic Server对这个选项的默认处理方式得它与其他服务器有明昄区别。后面我们会专题讨论?/P>
关于session的设|参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
六、HttpSession常见问题
Q在本小节中session的含义ؓ⑤和⑥的混合Q?/P>
1、session在何时被创徏
一个常见的误解是以为session在有客户端访问时p创徏Q然而事实是直到某server端程序调用HttpServletRequest.getSession(true)q样的语句时才被创徏Q注意如果JSP没有昄的?<%@page session="false"%> 关闭sessionQ则JSP文g在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);q也是JSP中隐含的session对象的来历?/P>
׃session会消耗内存资源,因此Q如果不打算使用sessionQ应该在所有的JSP中关闭它?/P>
2、session何时被删?BR>l合前面的讨论,session在下列情况下被删除a.E序调用HttpSession.invalidate();或b.距离上一ơ收到客L发送的session id旉间隔过了session的超时设|?或c.服务器进E被停止Q非持久sessionQ?/P>
3、如何做到在览器关闭时删除session
严格的讲Q做不到q一炏V可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进E这些非常规手段仍然无能为力?/P>
4、有个HttpSessionListener是怎么回事
你可以创Llistenerȝ控session的创建和销毁事Ӟ使得在发生这L事g时你可以做一些相应的工作。注意是session的创建和销毁动作触发listenerQ而不是相反。类似的与HttpSession有关的listenerq有HttpSessionBindingListenerQHttpSessionActivationListener和HttpSessionAttributeListener?/P>
5、存攑֜session中的对象必须是可序列化的?BR>不是必需的。要求对象可序列化只是ؓ了session能够在集中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在Weblogic Server的session中放|一个不可序列化的对象在控制C会收C个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象Q在session销毁时会有一个ExceptionQ很奇怪?/P>
6、如何才能正的应付客户端禁止cookie的可能?BR>Ҏ有的URL使用URL重写Q包括超链接Qform的actionQ和重定向的URLQ具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
7、开两个览器窗口访问应用程序会使用同一个sessionq是不同的session
参见W三节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器Q不同的H口打开方式以及不同的cookie存储方式都会对这个问题的{案有媄响?/P>
8、如何防止用h开两个览器窗口操作导致的session混ؕ
q个问题与防止表单多ơ提交是cM的,可以通过讄客户端的令牌来解冟뀂就是在服务器每ơ生成一个不同的idq回l客LQ同时保存在session里,客户端提交表单时必须把这个id也返回服务器Q程序首先比较返回的id与保存在session里的值是否一_如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表C层模式的部分。需要注意的是对于用javascript window.open打开的窗口,一般不讄q个idQ或者用单独的idQ以防主H口无法操作Q徏议不要再window.open打开的窗口里做修Ҏ作,q样可以不用设|?/P>
9、ؓ什么在Weblogic Server中改变session的值后要重新调用一ơsession.setValue
做这个动作主要是Z在集环境中提示Weblogic Server session中的值发生了改变Q需要向其他服务器进E复制新的session倹{?/P>
10、ؓ什么session不见?BR>排除session正常失效的因素之外,服务器本w的可能性应该是微乎其微的,虽然W者在iPlanet6SP1加若q补丁的Solaris版本上倒也遇到q;览器插件的可能性次之,W者也遇到q?721插g造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题?BR>出现q一问题的大部分原因都是E序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨个问题?/P>
七、跨应用E序的session׃n
常常有这L情况Q一个大目被分割成若干项目开发,Z能够互不q扰Q要求每个小目作ؓ一个单独的web应用E序开发,可是C最后突然发现某几个项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on)Q在session中保存login的用户信息,最自然的要求是应用E序间能够访问彼此的session?/P>
然而按照Servlet规范Qsession的作用范围应该仅仅限于当前应用程序下Q不同的应用E序之间是不能够互相讉KҎ的session的。各个应用服务器从实际效果上都遵守了q一规范Q但是实现的l节却可能各有不同,因此解决跨应用程序session׃n的方法也各不相同?/P>
首先来看一下Tomcat是如何实现web应用E序之间session的隔ȝQ从Tomcat讄的cookie路径来看Q它对不同的应用E序讄的cookie路径是不同的Q这样不同的应用E序所用的session id是不同的Q因此即使在同一个浏览器H口里访问不同的应用E序Q发送给服务器的session id也可以是不同的?/P>
Ҏq个Ҏ,我们可以推测Tomcat中session的内存结构大致如下?/P>
W者以前用q的iPlanet也采用的是同L方式Q估计SunONE与iPlanet之间不会有太大的差别。对于这U方式的服务器,解决的思\很简单,实际实行h也不难。要么让所有的应用E序׃n一个session idQ要么让应用E序能够获得其他应用E序的session id?/P>
iPlanet中有一U很单的Ҏ来实现共享一个session idQ那是把各个应用程序的cookie路径都设?Q实际上应该?NASAppQ对于应用程序来讲它的作用相当于根)?BR><session-info>
<path>/NASApp</path>
</session-info>
需要注意的是,操作׃n的session应该遵@一些编E约定,比如在session attribute名字的前面加上应用程序的前缀Q得setAttribute("name", "neo")变成setAttribute("app1.name", "neo")Q以防止命名I间冲突Q导致互相覆盖?/P>
在Tomcat中则没有q么方便的选择。在Tomcat版本3上,我们q可以有一些手D|׃nsession。对于版?以上的TomcatQ目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文g、数据库、JMS或者客LcookieQURL参数或者隐藏字D늭手段?/P>
我们再看一下Weblogic Server是如何处理session的?/P>
从截屏画面上可以看到Weblogic ServerҎ有的应用E序讄的cookie的\径都?Q这是不是意味着在Weblogic Server中默认的可以共享session了呢Q然而一个小实验卛_证明即不同的应用程序用的是同一个sessionQ各个应用程序仍然只能访问自己所讄的那些属性。这说明Weblogic Server中的session的内存结构可能如?/P>
对于q样一U结构,在session机制本n上来解决session׃n的问题应该是不可能的了。除了借助于第三方的力量,比如使用文g、数据库、JMS或者客LcookieQURL参数或者隐藏字D늭手段Q还有一U较为方便的做法Q就是把一个应用程序的session攑ֈServletContext中,q样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,
应用E序A
context.setAttribute("appA", session);
应用E序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");
值得注意的是q种用法不可ULQ因为根据ServletContext的JavaDocQ应用服务器可以处于安全的原因对于context.getContext("/appA");q回I|以上做法在Weblogic Server 8.1中通过?/P>
那么Weblogic ServerZ么要把所有的应用E序的cookie路径都设?呢?原来是ؓ了SSOQ凡是共享这个session的应用程序都可以׃n认证的信息。一个简单的实验可以证明这一点,修改首先d的那个应用程序的描述Wweblogic.xmlQ把cookie路径修改?appA讉K另外一个应用程序会重新要求dQ即使是反过来,先访问cookie路径?的应用程序,再访问修改过路径的这个,虽然不再提示dQ但是登录的用户信息也会丢失。注意做q个实验时认证方式应该用FORMQ因为浏览器和web服务器对basic认证方式有其他的处理方式Q第二次h的认证不是通过session来实现的。具体请参看[7] secion 14.8 AuthorizationQ你可以修改所附的CZE序来做q些试验?/P>
八、ȝ
session机制本nq不复杂Q然而其实现和配|上的灵zL却使得具体情况复杂多变。这也要求我们不能把仅仅某一ơ的l验或者某一个浏览器Q服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析?/P>
关于作者:
郎云鹏(dev2dev ID: hippiewolfQ,软g工程师,从事J2EE开?BR>电子邮gQ?A href="mailto:langyunpeng@yahoo.com.cn">langyunpeng@yahoo.com.cn
地址Q大qY件园?1L技大厦A座大q博涵咨询服务有限公?/P>
参考文档:
[1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html
[2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt
[3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt
[4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/
[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
[6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
[7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt
代码下蝲Q?A >http://dev2dev.bea.com.cn/images/paihang_article/041020/sampleApp.zip
转蝲地址Q?A >http://www.supcode.com/Article/html/4/43/2005/06/29/216335472190.shtml
王森在他的《Java深度历险》(强力推荐q本书,内容而精Q的W一章就解释了JDKQJREQJVM之间的关pR解释了我们执行java.exe?BR>发生的事情。其中提刎ͼjava.exe依照一套逻辑来寻扑֏以用的JREQ首先查找自己�诘哪柯枷掠忻挥蠮REQ据王森讲这栯不确切,我没?BR>JDK全部的源代码Q在此无从考证Q;其次查找自己的父目录下有没有JREQ最后才是查询Windows的注册表?/FONT>
通常我们在安装好了JRE的机器上的Q何一个目录下都可以执行java.exe。因为它在安装时被复制到了windows的system32目录下,而后?BR>无论如何都会在path环境变量中。这个java.exe最l必然会讉K注册表来定真正的JRE的所在地。若我们要求每一个应用程序都自带JREQ?BR>必然不能走这条\。但Q逻辑的第二条Ԍjava.exe会在它的父目录下查找JREQ解x案就在这一条中?/FONT>
假设我们的应用程序打好了包,叫做MyApp.jarQ放在MyApp的目录下。我们在MyApp目录下,可以执行java –jar MyApp.jar来运行我?BR>的程序。我们安装的是JRE 1.5Q在C:\Program Files\Java\jre1.5.0下。现在,我们只需要简单的jre1.5.0目录搬到MyApp目录下,?BR>便改个容易写的名字比如叫jre。现在,我们的应用程序就象这P
MyApp
MyApp.jar
Jre
Jre1.5.0目录下的全部内容
Java.exe在jre目录下的bin目录中。根据第二条逻辑Qjava.exe会在它的父目录中查找jreQ实验证实,它会查找lib目录Q而lib在jre?BR>录下。因此,q样java.exe׃定jre的所在然后正常执行javaE序Q不会去我们是否安装了JREQ注册表中是否有注册这些杂事了?/FONT>
试一下,在命令行下进入MyApp的目录下Q假讑֮在C盘,path指向MyApp下的JREQ?/FONT>
set path=c:\MyApp\jre\bin
然后q行Q?/FONT>
java –verbose –jar MyApp.jar
加上verbose参数以确定我们确实用了这一套被搬出了家的JRE?/FONT>
E序可以q行Qƈ且在命o行输出的前几行,可以看到Q?/FONT>
[Opened C:\MyApp\jre\lib\rt.jar]
[Opened C:\MyApp\jre\lib\jsse.jar]
[Opened C:\MyApp\jre\lib\jce.jar]
[Opened C:\MyApp\jre\lib\charsets.jar]
因此E序d的确实是它的U有的JRE?/FONT>
xQ我们似乎完成了d。但是现在我们的U有JRE仍不完美Q缺Ҏ太大。JRE 1.5有接q?0MBQ作为我们的U有的JREQ好多内定w?BR>可以抛弃的。Jre目录下的license都可以不要,bin下的执行文g只需要保留java.exe或者javaw.exeQlib下只要保?BR>rtQjsseQjceQcharsets几个库就可以了。除了i386和zi两个子目录外Q其余的子目录都可以不要。Zi下只需要保留自己地区的子目录和其下
的一些文件就可以。Lib下除了库之外的属性文件等{都要保留。这h理一番,JRE仍然有接q?0MB。还可以l箋清理几个库文仉面不需
要的内容Q这需要仔l的整理Q会很费功夫。最好能写出一个自动工具帮助我们整理它们。从Sun公司上下到的JMF里面附带的用Java写的?BR>体播攑֙p带了JREQ只有几个MB?/FONT>
清理q后需要运行几遍我们的应用E序Q以保我们的JRE不缺东ѝ?/FONT>
如果我们希望能有一个程序直接启动我们的应用E序Q那p要费些功夫。最单的Ҏ是弄Z个快h式来Q但是快h式的路径不能是相
对的Q不方便我们安装。我惛_的方案就是用Win32E序包装一下。在VS.NET下写一个Win32程序:
int PASCAL WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
if( !CreateProcess( "jre\\bin\\javaw.exe",//执行的程序名
"jre\\bin\\javaw.exe -jar MyApp.jar", // 带参数的执行E序
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
FALSE, // Set handle inheritance to FALSE.
0, // No creation flags.
NULL, // Use parent’s environment block.
NULL, // Use parent’s starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi ) // Pointer to PROCESS_INFORMATION structure.
)
{
ErrorExit( "CreateProcess failed." );
}
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
基本上是按照MSDN文档中的例子照搬的。将它编译成一个EXE文gQ我们的d才全部完成。双击这个EXE文gQ我们的E序启动了,看v?BR>和传l的Win32E序没有两样QJRE完全被隐藏在底层?BR>
转蝲地址 http://www.matrix.org.cn/resource/article/44/44085_java.html
<struts-config>
<form-beans> <form-bean name="lazyForm" type="org.apache.struts.validator.LazyValidatorForm"/> </form-beans> <action-mappings> <action path="/myActionPath" type="myPackage.MyAction" name="lazyForm" validate="true"/> </action-mappings> </struts-config> |
<html:form action="/myActionPath">
<h2>Simple Property Example</h2> Customer Number: <html:text property="custNo"/> Customer Name: <html:text property="custName"/> <h2>Mapped Property Example</h2> Street: <html:text property="address(street)"/> Town: <html:text property="address(town)"/> State: <html:text property="address(state)"/> Country: <html:text property="address(country)"/> <h2>Indexed Property Example</h2> <logic:iterate id="products" property="products"> Product Code:<html:text name="products" property="code" indexed="true"/> Product Description:<html:text name="products" property="description" indexed="true"/> Product Price:<html:text name="products" property="price" indexed="true"/> </logic:iterate> </html:form> |
java代码: |
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServeletRequest request, HttpServletResponse response)throwsException{ // Cast form to DynaBean DynaBean dynaForm = (DynaBean)form; // Use the DynaBean String custNo = (String)dynaForm.get("custNo"); // simple Map address = (Map)dynaForm.get("address"); // mapped List products = (List)dynaForm.get("products"); // indexed //... etc } |
RequestUtil.java
public static void populate(Object bean, String prefix, String suffix,
HttpServletRequest request)
throws ServletException {
//initialize a MultipartRequestHandler
MultipartRequestHandler multipart = null;
String multipartClass = (String)
request.getAttribute(Action.MULTIPART_KEY);
request.removeAttribute(Action.MULTIPART_KEY);
......
//在这里取处理MultipartRequest的类
multipart = (MultipartRequestHandler) Class.forName(multipartClass).newInstance();
......
}
// 自定义一个DiskMultipartRequestHandlerX
ActionServlet.java
/**
* The MultipartRequestHandler class name used for handling
* multipart form requests. This is the global default value,
* the handler can also be set in individual mapping entries
*/
protected String multipartClass = "org.apache.struts.upload.DiskMultipartRequestHandler";
public class DefaultActionServlet extends ActionServlet {
protected void process(HttpServletRequest request,
HttpServletResponse response) {
try {
String contentType = request.getContentType();
String method = request.getMethod();
//if this is a multipart request, wrap the HttpServletRequest object
//with a MultipartRequestWrapper to keep the process sub-methods
//from failing when checking for certain request parameters
//for command tokens and cancel button detection
if ((contentType != null) && (contentType.startsWith("multipart/form-data"))
&& (method.equals("POST"))) {
//request.getAttribute(Action.MULTIPART_KEY);
// 讄处理MultipartRequest的类Q也可以在struts-config.xml中设|?BR> request.setAttribute(Action.MULTIPART_KEY, "com.struts.upload.DiskMultipartRequestHandlerX");
}
request.setCharacterEncoding("Shift_JIS");
super.process(request, response);
} catch(Exception e) {
log.error("encode error: ", e);
}
}
}
q个问题在struts1.1中得C解决.
java代码: |
<property name="connection.useUnicode">true</property> <property name="connection.characterEncoding">UTF-8</property> |
java代码: |
Properties extraProperties = new Properties(); extraProperties.put("hibernate.connection.useUnicode", "true"); extraProperties.put("hibernate.connection.characterEncoding", "UTF-8"); myConfiguration.addProperties(extraProperties); |
java代码: |
Query q = s.createQuery("select c.id from Cat as c");
List l = q.list(); for (i=0; i< l.size(); i++) { Long id = (Long) l.get(i); System.out.println(id.longValue()); } |
java代码: |
Query q = s.createQuery("select Cat.id, Cat.name, from Cat ");
List l = q.list(); for (int i=0; i< l.size(); i++) { Object[] row = (Object[]) l.get(i); Long id = (Long) row[0]; String name = (String) row[1]; } |
java代码: |
Query q = s.createQuery("select c.id, c.name,c from Cat as c");
List l = q.list(); for (int i=0; i< l.size(); ; i++) { Object[] row = (Object[]) l.get(i); Long id = (Long) row[0]; String name = (String) row[1]; Cat c = (Cat) row[2]; } |
Beanutils用了术般的反射技术,实现了很多夸张有用的功能Q都是C/C++时代不敢想的。无的项目,始终一天都会用得上它。我是后知后觉了,W一回看到它的时候居焉q?BR>
1.属性的动态getter、setter
在这框架满天飞的q代Q不能事事都保证执行getter,setter函数了,有时候属性是要根据名字动态取得的Q就像这P
BeanUtils.getProperty(myBean,"code");
而Common BeanUtils的更强功能在于可以直接访问内嵌对象的属性,只要使用点号分隔?BR>BeanUtils.getProperty(orderBean, "address.city");
相比之下其他cd的BeanUtils通常都很单,不能讉K内嵌的对象,所以有时要用Commons BeanUtils来替换它们?BR>
BeanUtilsq支持List和Mapcd的属性,如下面的语法卛_取得Order的顾客列表中W一个顾客的名字
BeanUtils.getProperty(orderBean, "customers[1].name");
其中BeanUtils会用ConvertUtilscL字符串{为Bean属性的真正cdQ方便从HttpServletRequest{对象中提取beanQ或者把bean输出到页面?BR>而PropertyUtils׃原色的保留Bean原来的类型?/FONT>
2.BeanCompartor 动态排?
q是通过反射Q动态设定Bean按照哪个属性来排序Q而不再需要在实现bean的Compare接口q行复杂的条件判断?
List peoples = ...; // Person对象的列?BR> Collections.sort(peoples, new BeanComparator("age"));
如果要支持多个属性的复合排序Q如"Order By lastName,firstName"
ArrayList sortFields = new ArrayList();
sortFields.add(new BeanComparator("lastName"));
sortFields.add(new BeanComparator("firstName"));
ComparatorChain multiSort = new ComparatorChain(sortFields);
Collections.sort(rows,multiSort);
其中ComparatorChain属于jakata commons-collections包?BR>如果age属性不是普通类型,构造函数需要再传入一个comparator对象为age变量排序?BR>另外, BeanCompartor本n的ComparebleComparator, 遇到属性ؓnull׃抛出异常, 也不能设定升序还是降序。这个时候又要借助commons-collections包的ComparatorUtils.
Comparator mycmp = ComparableComparator.getInstance();3.Converter 把Request或ResultSet中的字符串绑定到对象的属?
mycmp = ComparatorUtils.nullLowComparator(mycmp); //允许null
mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
Comparator cmp = new BeanComparator(sortColumn, mycmp);
MyBean bean = ...;
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
map.put(name, request.getParameterValues(name));
}
BeanUtils.populate(bean, map);
其中BeanUtils的populateҎ或者getProperty,setPropertyҎ其实都会调用convertq行转换?BR> 但Converter只支持一些基本的cdQ甚臌java.util.Datecd也不支持。而且它比较笨的一个地Ҏ当遇C认识的类型时Q居然会抛出异常来?nbsp;对于DatecdQ我参考它的sqldatecd实现了一个ConverterQ而且d了一个设|日期格式的函数?BR>要把q个Converter注册Q需要如下语句:
ConvertUtilsBean convertUtils = new ConvertUtilsBean();4 其他功能
DateConverter dateConverter = new DateConverter();
convertUtils.register(dateConverter,Date.class);
//因ؓ要注册converter,所以不能再使用BeanUtils的静态方法了Q必d建BeanUtilsBean实例
BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
beanUtils.setProperty(bean, name, value);
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=277748
net.sf.hibernate.*
该包的类基本上都是接口类和异常类
net.sf.hibernate.cache.*
JCS的实现类
net.sf.hibernate.cfg.*
配置文gdc?
net.sf.hibernate.collection.*
Hibernate集合接口实现c,例如ListQSetQBag{等QHibernate之所以要自行~写集合接口实现cLZ支持lazy loading
net.sf.hibernate.connection.*
几个数据库连接池的Provider
net.sf.hibernate.dialect.*
支持多种数据库特性,每个Dialect实现cM表一U数据库Q描qC该数据库支持的数据类型和其它特点Q例如是否有AutoIncrementQ是否有SequenceQ是否有分页sql{等
net.sf.hibernate.eg.*
Hibernate文档中用到的例子
net.sf.hibernate.engine.*
q个包的cM用比较散
net.sf.hibernate.expression.*
HQL支持的表辑ּ
net.sf.hibernate.hq.*
HQL实现
net.sf.hibernate.id.*
ID生成?
net.sf.hibernate.impl.*
最核心的包Q一些重要接口的实现c,如果SessionQSessionFactoryQQuery{?
net.sf.hibernate.jca.*
JCA支持Q把Session包装为支持JCA的接口实现类
net.sf.hibernate.jmx.*
我不懂JMXQ只知道JMX是用来编写App Server的管理程序的Q大概是JMX部分接口的实玎ͼ使得App Server可以通过JMX接口理Hibernate
net.sf.hibernate.loader.*
也是很核心的包,主要是生成sql语句?
net.sf.hibernate.lob.*
Blob和Clob支持
net.sf.hibernate.mapping.*
hbm文g的属性实?
net.sf.hibernate.metadata.*
PO的Meta实现
net.sf.hibernate.odmg.*
ODMG是一个ORM标准Q这个包是ODMG标准的实现类
net.sf.hibernate.persister.*
核心包,实现持久对象和表之间的映?
net.sf.hibernate.proxy.*
Proxy和Lazy Loading支持
net.sf.hibernate.ps.*
该包是PreparedStatment Cache
net.sf.hibernate.sql.*
生成JDBC sql语句的包
net.sf.hibernate.test.*
试c,你可以用junit来测试Hibernate
net.sf.hibernate.tool.hbm2ddl.*
用hbm配置文g生成DDL
net.sf.hibernate.transaction.*
Hibernate Transaction实现c?
net.sf.hibernate.type.*
Hibernate中定义的持久对象的属性的数据cd
net.sf.hibernate.util.*
一些工LQ作用比较散
net.sf.hibernate.xml.*
XML数据l定
Hibernate入门 - 基础配置
java代码: |
hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N' |
java代码: |
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class org.gjt.mm.mysql.Driver hibernate.connection.driver_class com.mysql.jdbc.Driver hibernate.connection.url jdbc:mysql:///test hibernate.connection.username root hibernate.connection.password |
java代码: |
hibernate.connection.pool_size 1 hibernate.statement_cache.size 25 |
java代码: |
hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider |
java代码: |
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect hibernate.connection.datasource mypool hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider |
java代码: |
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect hibernate.connection.datasource mypool hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider hibernate.jndi.class weblogic.jndi.WLInitialContextFactory hibernate.jndi.url t3://servername:7001/ |
java代码: |
hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory |
java代码: |
hibernate.show_sql false |
java代码: |
#hibernate.connection.isolation 4 |
java代码: |
hibernate.jdbc.fetch_size 50 hibernate.jdbc.batch_size 25 |
java代码: |
C = create, R = read, U = update, D = delete |
java代码: |
#hibernate.jdbc.use_scrollable_resultset true |
java代码: |
#hibernate.cglib.use_reflection_optimizer false |
默认打开Q启用cglib反射优化。cglib是用来在Hibernate中动态生成PO字节码的Q打开优化可以加快字节码构造的速度?
不过Q当你在调试E序q程中,特别是和proxyQlazy loading相关的应用中Q代码出错,但是出错提示信息有语焉不详,那么你可以把cglib优化xQ这样Hibernate会输出比较详l的调试信息Q帮助你debug?BR>
Hibernate的灵zL也是一把双刃剑Q用好了q别舒服,用不好,q别头疹{把我原来在jdon一个帖子{q来Q谈Cq个问题Q?
一、Hibernate是JDBC的轻量的对象封装,它是一个独立的对象持久层框Ӟ和App ServerQ和EJB没有什么必然的联系。Hibernate可以用在MJDBC可以使用的场合,例如Java应用E序的数据库讉K代码QDAO接口的实现类Q甚臛_以是BMP里面的访问数据库的代码。从q个意义上来_Hibernate和EB不是一个范畴的东西Q也不存在非此即彼的关系?
二、Hibernate是一个和JDBC密切兌的框Ӟ所以Hibernate的兼Ҏ和JDBC驱动Q和数据库都有一定的关系Q但是和使用它的JavaE序Q和App Server没有M关系Q也不存在兼Ҏ问题?
三、Hibernate不能用来直接和Entity Bean做对比,只有攑֜整个J2EE目的框架中才能比较。ƈ且即使是攑֜软g整体框架中来看,Hibernate也是做ؓJDBC的替代者出现的Q而不是Entity Bean的替代者出现的Q让我再列一ơ我已经列nơ的框架l构Q?
传统的架构:
1) Session Bean <-> Entity Bean <-> DB
Z解决性能障碍的替代架构:
2) Session Bean <-> DAO <-> JDBC <-> DB
使用Hibernate来提高上面架构的开发效率的架构Q?
3) Session Bean <-> DAO <-> Hibernate <-> DB
׃?个架构来分析Q?
1、内存消耗:采用JDBC的架?无疑是最省内存的QHibernate的架?ơ之QEB的架?最差?
2、运行效率:如果JDBC的代码写的非怼化,那么JDBC架构q行效率最高,但是实际目中,q一点几乎做不到Q这需要程序员非常_NJDBCQ运用Batch语句Q调整PreapredStatement的Batch Size和Fetch Size{参敎ͼ以及在必要的情况下采用结果集cache{等。而一般情况下E序员是做不到这一点的。因此Hibernate架构表现出最快的q行效率。EB的架构效率会差的很远?
3、开发效率:在有JBuilder的支持下以及单的目QEB架构开发效率最高,JDBCơ之QHibernate最差。但是在大的目Q特别是持久层关pL很复杂的情况下QHibernate效率高的惊hQJDBCơ之Q而EB架构很可能会p|?
4、分布式Q安全检查,集群Q负载均衡的支持
׃有SB做ؓFacadeQ?个架构没有区别?
四、EB和Hibernate学习隑ֺ在哪里?
EB的难度在哪里Q不在复杂的XML配置文g上,而在于EBq用E微不慎Q就有严重的性能障碍。所以难在你需要学习很多EJB设计模式来避开性能问题Q需要学习App Server和EB的配|来优化EB的运行效率。做EB的开发工作,E序员的大部分精力都被放CEB的性能问题上了Q反而没有更多的_֊x本n׃要投入精力去考虑的对象持久层的设计上来?
Hibernate隑֜哪里Q不在Hibernate本n的复杂,实际上Hibernate非常的简单,隑֜Hibernate太灵zM?
当你用EB来实现持久层的时候,你会发现EB实在是太W拙了,W拙CҎ没有什么可以选择的余圎ͼ所以你Ҏ׃用花费精力去设计ҎQ去qҎ的好坏,去费脑筋考虑选择哪个ҎQ因为只有唯一的方案摆在你面前Q你只能q么做,没得选择?
Hibernate相反Q它太灵zMQ相同的问题Q你臛_可以设计出十几种Ҏ来解冻I所以特别的犯难Q究竟用q个Q还是用那个呢?q些Ҏ之间到底有什么区别呢Q他们的q行原理有什么不同?q行效率哪个比较好?光是主键生成Q就有七八种Ҏ供你选择Q你为难不ؓ难?集合属性可以用SetQ可以用ListQ还可以用BagQ到底哪个效率高Q你为难不ؓ难?查询可以用iteratorQ可以用listQ哪个好Q有什么区别?你ؓ难不为难Q复合主键你可以直接在hbm里面配置Q也可以自定义CustomerTypeQ哪U比较好些?你ؓ难不为难Q对于一个表Q你可以选择单一映射一个对象,也可以映成父子对象Q还可以映射成两?:1的对象,在什么情况下用哪U方案比较好Q你为难不ؓ难?
q个列表可以一直开列下去,直到你不惛_看下Mؓ止。当你面前摆着无数的眼qqҎ的时候,你会觉得q福呢?q是悲哀呢?如果你是一个负责的E序员,那么你一定会仔细研究每种Ҏ的区别,每种Ҏ的效率,每种Ҏ的适用场合Q你会觉得你已经陷入q去拔不出来了。如果是用EBQ你W一U种已l做Z军_Q根本没得选择Q比如说集合属性,你只能用CollectionQ如果是HibernateQ你会在BagQList和Set之间来回犹U不决Q甚x不清楚的话,E序都没有办法写?/SPAN>