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://127.0.0.1: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会话怎么办?
- 不要在发生异常后刷新会话...... 希望处理源于数据库约束的异常(例如唯一......)“昵称已分配......”。
- ...