跳转到内容

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 源代码

[编辑 | 编辑源代码]

将以下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
  • ...

使用Hibernate会话

[编辑 | 编辑源代码]

通过上述步骤,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会话怎么办?
  • 不要在发生异常后刷新会话...... 希望处理源于数据库约束的异常(例如唯一......)“昵称已分配......”。
  • ...
华夏公益教科书