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會話怎麼辦?
- 不要在發生異常後重新整理會話...... 希望處理源於資料庫約束的異常(例如唯一......)“暱稱已分配......”。
- ...