ZK/操作指南/整合其他框架/如何处理Hibernate会话
初步!
我对HibernateSessionHandling的描述基于享受Tapestry的Web开发中描述的Hibernate/Tapestry配置...... 我已经将此适应ZK,并提供了一组类以几乎无需配置即可提供Hibernate会话支持。
还有一种Spring/Hibernate解决方案用于处理Hibernate会话,它与servlet过滤器一起使用。Spring还提供了更多好处(我只从传闻中得知......)用于Hibernate,并且可能是一个更好的解决方案...... 我不知道Spring Hibernate会话处理是否也可以用于ZK Ajax请求/响应循环...... 我听说有人正在编写描述......
关于Web应用程序中Hibernate会话的主要规则(在我看来...... 请纠正我......)
- Hibernate sessionfactory是一种昂贵的资源...... 它是线程安全的,每个Web应用程序应该只有一个实例
- Hibernate会话是一种廉价且非线程安全的资源。
- Hibernate会话可以被视为一项工作。每个HttpSession应该只有一个
- Hibernate会话和事务通常应该在控制权返回给客户端时关闭
- 创建数据库连接是一个耗时的过程...... 使用连接池!
- 不要在会话关闭后访问分离的Hibernate POJO(普通旧Java对象),因为相关数据可能不会从数据库中检索(延迟初始化)!
- ...
当应用程序第一次需要Hibernate会话时,将使用静态最终HibernateSessionProvider.getHibernateSession(HttpSession)(@tomyeh...... 如何提供zul快捷方式?)来请求一个Hibernate会话。这会检查当前HttpSession的属性以查找HibernateSessionOwner...... 当不可用时,它会创建一个并将其存储在当前HTTPSession中。HibernateSessionOwner的构造函数检查WebApp属性以查找属性HibernateSessionCreator()...... 当不可用时,它会创建一个并将它存储在WebApp的属性中...... 因此我们在Web应用程序中只有一个HibernateSessionCreator实例,它是一个用于创建Hibernate会话的Hibernate session factory的包装器...... 当HibernateSessionOwner和-Creator实例化时,getHibernateSession()会向HibernateSessionOwner请求一个Hibernate会话...... SessionOwner从创建者那里获取它,并进一步管理它...... HibernateSessionOwner持有会话,并在正在进行的线程中每次需要会话时提供它...... 为了在线程结束时清理Hibernate会话,使用HibernateSessionEventThreadCleanup类(并在web.xml中配置),它实现了zk接口EventThreadCleanup...... 在清理例程中,调用当前会话的HibernateSessionOwner的threadDidDiscardService(),停止事务并关闭Hibernate会话......
在Tomcat的server.xml中配置jdbc连接池
...
<Context docBase="TKTestWeb"
path="/TZTestWeb"
reloadable="true"
source="org.eclipse.jst.j2ee.server:ZKTest">
<Resource auth="Container"
defaultAutoCommit="false"
driverClassName="com.mysql.jdbc.Driver"
maxActive="20" name="jdbc/zktest"
password="testpw"
timeBetweenEvictionRunsMillis="60000"
type="javax.sql.DataSource"
url="jdbc:mysql://:3306/zktest"
username="testuser"/>
</Context>
...
在web.xml中引用jdbc数据源
...
<resource-ref>
<res-ref-name>jdbc/infratour</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
...
在zk.xml中配置EventThreadCleanup
<zk>
<listener>
<listener-class>com.potix.hibernate.HibernateSessionEventThreadCleanup</listener-class>
</listener>
</zk>
在web.xml中配置SessionFilter ??? TODO:提供一个与EventThreadCleanup对普通HttpSession请求/响应循环执行相同操作的类
将以下java类添加到您的类路径(WEB-INF/classes)
HibernateSessionProvider.java
import org.hibernate.Session;
import com.potix.util.logging.Log;
import com.potix.zk.ui.http.SessionImpl;
public class HibernateSessionProvider {
private static final Log log = Log.lookup(HibernateSessionProvider.class);
private static final String HIBERNATE_SESSION_OWNER = "de.test.web.services.DefaultSessionOwner";
//TODO add some debug logging
public static final Session getHibernateSession(SessionImpl httpSession){
Session hs;
HibernateSessionOwner sessionOwner = (HibernateSessionOwner)httpSession.getAttribute(HIBERNATE_SESSION_OWNER);
if(sessionOwner==null){
sessionOwner = new HibernateSessionOwner(httpSession);
httpSession.setAttribute(HIBERNATE_SESSION_OWNER, sessionOwner );
}
return hs;
}
}
HibernateSessionOwner.java
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.potix.util.logging.Log;
import com.potix.zk.ui.http.SessionImpl;
public class HibernateSessionOwner implements SessionOwner {
private static final String HIBERNATE_SESSION_CREATOR = "de.test.web.services.DefaultSessionCreator";
private static final Log log = Log.lookup(HibernateSessionCreator.class);
private SessionCreator creator;
private Session hibernateSession;
private Transaction tx;
private boolean isToRollback;
//TODO add some debug logging
public HibernateSessionOwner(SessionImpl httpSession) {
creator = (SessionCreator)httpSession.getWebApp()
.getAttribute(HIBERNATE_SESSION_CREATOR);
if(creator == null){
httpSession.getWebApp()
.setAttribute(HIBERNATE_SESSION_CREATOR, new HibernateSessionCreator());
creator = (SessionCreator)httpSession
.getWebApp()
.getAttribute(HIBERNATE_SESSION_CREATOR);
if(creator == null){
log.error("Could not install SessionCreatorService" );
throw new RuntimeException("Could not install SessionCreatorService");
}
}
}
public Session getSession() {
if (hibernateSession == null) {
hibernateSession = creator.createSession();
if (tx == null) {
tx = hibernateSession.beginTransaction();
isToRollback = false;
}
}
return hibernateSession;
}
public void threadDidDiscardService() {
if (hibernateSession != null) {
try {
endTransaction();
} finally {
hibernateSession.close();
hibernateSession = null;
}
}
}
public void setToRollback() {
isToRollback = true;
}
public void endTransaction() {
if (tx != null) {
try {
if (isToRollback) {
tx.rollback();
} else {
tx.commit();
}
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
tx = null;
}
}
}
public void setSessionCreator(SessionCreator creator) {
this.creator = creator;
}
}
HibernateSessionCreator.java
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.potix.util.logging.Log;
public class HibernateSessionCreator implements SessionCreator {
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
/** A session attribute. */
private static final Log log = Log.lookup(HibernateSessionCreator.class);
private SessionFactory sessionFactory;
public HibernateSessionCreator() {
Configuration cfg = new Configuration();
cfg.configure(CONFIG_FILE_LOCATION);
sessionFactory = cfg.buildSessionFactory();
if (sessionFactory == null){
log.error("Could not crate SessionFactory" );
throw new RuntimeException("Could not crate SessionFactory");
}
}
public Session createSession() {
return sessionFactory.openSession();
}
}
HibernateSessionEventThreadCleanup.java
import com.potix.zk.ui.Component;
import com.potix.zk.ui.event.Event;
import com.potix.zk.ui.event.EventThreadCleanup;
public class HibernateSessionEventThreadCleanup implements EventThreadCleanup{
private static final String HIBERNATE_SESSION_OWNER = "de.infratour.web.services.DefaultSessionOwner";
public void cleanup(Component comp, Event event) {
if(event.getPage().getSession().getAttribute(HIBERNATE_SESSION_OWNER)==null)
return;
((HibernateSessionOwner)event.getPage().getSession()
.getAttribute(HIBERNATE_SESSION_OWNER))
.threadDidDiscardService();
}
}
HibernateSessionHttpRequestCleanup.java
TODO!!!
将Hibernate发行版中的以下jar添加到您的类路径(WEB-INF/lib)
- hibernate3.jar
- ...
通过上述步骤,ZK启用了Hibernate...... 现在您可以插入您的Hibernate配置并在您的*.zul模板中使用它......
将您的Hibernate POJO类与Hibernate映射和配置文件一起添加到类路径
WEB-INF/lib +-*myhibernate.jar +-*package | +-*Person.class | +-*Person.hbm.xml *hibernate.cfg.xml
注册表单示例
<window title="Registration" border="normal">
<zscript>{
import de.infratour.hbm.*;
import de.infratour.web.services.HibernateSessionProvider;
import org.hibernate.Session;
void submit() {
Person person = new Person();
person.setNickname(nickname.value);
person.setEmail(email.value);
person.setForename(forename.value);
person.setSurename(surename.value);
if(password1.value!=password2.value){
alert("The repeat password is different from password! please enter again!");
password1.value="";
password2.value="";
return;
//TODO ...mark textbox red ???
}
person.setPasswd(password1.value);
try {
org.hibernate.Session hs = (org.hibernate.Session)HibernateSessionProvider.getHibernateSession(session);
hs.persist(person);
}catch (Exception e) {
alert(e.getMessage());
}
}
}
</zscript>
<grid>
<rows>
<row>Name : <textbox id="forename" /></row>
<row>Surename : <textbox id="surename" /></row>
<row>Login Name : <textbox id="nickname" /></row>
<row>Email: <textbox id="email" /></row>
<row>Password : <textbox id="password1" type="password"/></row>
<row>Repeat Password : <textbox id="password2" type="password"/></row>
<row><button label="submit" onClick="submit()"/></row>
</rows>
</grid>
</window>
- 提供对HibernateSessionProvider的静态最终例程的快捷方式(@Tom... 如何做?)
- 每个桌面只有一个线程...... 多个浏览器窗口但同一个HttpSession的Hibernate会话怎么办?
- 不要在发生异常后刷新会话...... 希望处理源于数据库约束的异常(例如唯一......)“昵称已分配......”。
- ...