使用Hibernate模板
与Hibernate进行交互的主要接口是org.hibernate.Session。这个Session接口提供了基本的数据访问功
能,比如从数据库保存、更新、删除和加载对象。通过Hibernate的Session接口,程序的DAO能够执行任
何存留功能。
获得Hibernate Session对象引用的标准方式是实现Hibernate的SessionFactory接口。SessionFactory
负责打开、关闭和管理Hibernate Session,以及其他一些功能。
就像JdbcTemplate把JDBC的繁琐工作抽离出去一样,Spring的HibernateTemplate在Hibernate Session
之上提供了一个抽象层,其主要功能是简化打开和关闭Hibernate会话,并且把Hibernate的特定异常转
化为表5.1列出的Spring ORM异常之一。(对于Hibernate 2来说,这意味着把受检异常
HibernateException转化为一个免检的Spring异常。)
========
SPRING中HIBERNATETEMPLATE类的使用
实际情况中,对于编程事务的操作最好还是使用
org.springframework.transaction.support.TransactionTemplate,因为HibernateTemplate在实际操
作中并不是想象的那样,具体如下:
目的:使用HibernateTemplate执行execute(new HibernateCallback())方法,从
HibernateCallback中得到session,
在此session中做多个操作,并希望这些操作位于同一个事务中。
如果你这样写(1):
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
session.flush();//实际上,如果不是程序员"手痒"来调用这个flush(),HibernateTemplate中
session的事务处理
还是很方便的
Student stu2 = new Student();
session.save(stu2);// 没有设置name字段,预期会报出例外
session.flush();
return null;
}
});
}
你期望spring在执行完execute回调后,在关闭session的时候提交事务,想法是很好的,但spring
并不会这么做。
让我们来看看在 Hibernate的源代码中,session.beginTransation()做了什么事。看如下代码(2)
:
public Transaction beginTransaction() throws HibernateException {
errorIfClosed();
if ( rootSession != null ) {
// todo : should seriously consider not allowing a txn to begin from a child session
// can always route the request to the root session
log.warn( "Transaction started on non-root session" );
}
Transaction result = getTransaction();
result.begin();
return result;
}
这个方法中的result是一个org.hibernate.transaction.JDBCTransaction实例,而方法中的
getTransaction()
方法源代码为(3):
public Transaction getTransaction() throws HibernateException {
if (hibernateTransaction==null) {
log.error(owner.getFactory().getSettings()
.getTransactionFactory().getClass());
hibernateTransaction = owner.getFactory().getSettings()
.getTransactionFactory()
.createTransaction( this, owner );
}
return hibernateTransaction;
}
再次追踪,owner.getFactory()。getSettings() .getTransactionFactory()的
createTransaction()方法
源代码如下(4):
public Transaction createTransaction(JDBCContext jdbcContext, Context
transactionContext)
throws HibernateException {
return new JDBCTransaction( jdbcContext, transactionContext );
}
它返回了一个JDBCTransaction,没什么特别的。
在代码2中,执行了result.begin(),其实也就是JDBCTransaction实例的begin()方法,来看看
(5):
public void begin() throws HibernateException {
if (begun) {
return;
}
if (commitFailed) {
throw new TransactionException("cannot re-start transaction after failed commit");
}
log.debug("begin");
try {
toggleAutoCommit = jdbcContext.connection().getAutoCommit();
if (log.isDebugEnabled()) {
log.debug("current autocommit status: " + toggleAutoCommit);
}
if (toggleAutoCommit) {
log.debug("disabling autocommit");
jdbcContext.connection().setAutoCommit(false);//把自动提交设为了false
}
} catch (SQLException e) {
log.error("JDBC begin failed", e);
throw new TransactionException("JDBC begin failed: ", e);
}
callback = jdbcContext.registerCallbackIfNecessary();
begun = true;
committed = false;
rolledBack = false;
if (timeout > 0) {
jdbcContext.getConnectionManager().getBatcher().setTransactionTimeout(timeout);
}
jdbcContext.afterTransactionBegin(this);
}
在直接使用Hibernate时,要在事务结束的时候,写上一句:tx.commit(),这个commit()的源码为
:
public void commit() throws HibernateException {
if (!begun) {
throw new TransactionException("Transaction not successfully started");
}
log.debug("commit");
if (!transactionContext.isFlushModeNever() && callback) {
transactionContext.managedFlush(); // if an exception occurs during
// flush, user must call
// rollback()
}
notifyLocalSynchsBeforeTransactionCompletion();
if (callback) {
jdbcContext.beforeTransactionCompletion(this);
}
try {
commitAndResetAutoCommit();//重点代码,它的作用是提交事务,并把connection的autocommit属
性恢复为true
log.debug("committed JDBC Connection");
committed = true;
if (callback) {
jdbcContext.afterTransactionCompletion(true, this);
}
notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_COMMITTED);
} catch (SQLException e) {
log.error("JDBC commit failed", e);
commitFailed = true;
if (callback) {
jdbcContext.afterTransactionCompletion(false, this);
}
notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
throw new TransactionException("JDBC commit failed", e);
} finally {
closeIfRequired();
}
}
上面代码中,commitAndResetAutoCommit()方法的源码如下:
private void commitAndResetAutoCommit() throws SQLException {
try {
jdbcContext.connection().commit();//这段不用说也能理解了
} finally {
toggleAutoCommit();//这段的作用是恢复connection的autocommit属性为true
}
}
上述代码的toggleAutoCommit()源代码如下:
private void toggleAutoCommit() {
try {
if (toggleAutoCommit) {
log.debug("re-enabling autocommit");
jdbcContext.connection().setAutoCommit(true);//这行代码的意义很明白了吧
}
} catch (Exception sqle) {
log.error("Could not toggle autocommit", sqle);
}
}
因此,如果你是直接使用hibernate,并手动管理它的session,并手动开启事务关闭事务的话,完
全可以保证你的
事务(好像完全是废话)。
但是,如果你用的是HibernateTemplate,如同源代码1一样,则不要指望spring在关闭session的时
候为你提交事务
(罪魁祸首就是在代码1中调用了session.flush())。因为在使用代码1时,spring中得到session的
方式如下:
Session session = (entityInterceptor != null ? sessionFactory.openSession
(entityInterceptor) :
sessionFactory。openSession());简单地说它就是得到了一个session,而没有对connection的
autocommit()
作任何操作,spring管理范围内的session所持有的connection是autocommit=true 的,spring借助这个
属性,在它关
闭session时,提交数据库事务。,因此如果你在源代码1中加上一句话:
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
log.info(session.connection().getAutoCommit());//打印一下事务提交方式
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
session.flush();
Student stu2 = new Student();
session.save(stu2);// 没有设置name字段,预期会报出例外
session.flush();
return null;
}
});
}
运行后,它打出的结果是true,也就是说,虽然保存stu2时会报出例外,但如果commit属性为true
,则每一个到
达数据库的sql语句会立即被提交。换句话说,在调用完session.save(stu1)后,调用session.flush
(),会发送
sql语句到数据库,再根据commit 属性为true,则保存stu1的操作已经被持久到数据库了,尽管后面的
一条insert语
句出了问题。
因此,如果你想在HibernateCallback中使用session的事务,需要如下写:
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
//保存stu1
Student stu1=new Student();
stu1.setName("aaaa");//在数据库中,name字段不允许为null
session.save(stu1);
session.flush();
Student stu2 = new Student();
session.save(stu2);//没有设置name字段,预期会报出例外
session.flush();
session.connection().commit();
//至于session的关闭就不用我们操心了
return null;
}
});
}
运行上述代码,没问题了。至此,可能有些读者早就对代码1不满意了:为什么每次save()以后要调用
flush()?这是
有原因的。下面我们来看看把session.flush()去掉后会出什么问题。改掉后的代码如下:
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
// session.flush();
Student stu2 = new Student();
session.save(stu2);// 没有设置name字段,预期会报出例外
// session.flush();
session.connection().commit();
return null;
}
});
}
运行上述代码,后台报数据库的not null错误,这个是合理的,打开数据库,没有发现新增记录,
这个也是合理的。
你可能会说:由于事务失败,数据库当然不可能会有任何新增记录。好吧,我们再把代码改一下,去除
not null的错误,
以确保它能正常运行。代码如下:
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
// session.flush();
Student stu2 = new Student();
stu2.setName("asdfasdf");//好了,这个字段设过值,不会再报not null错误了
session.save(stu2);
// session.flush();
session.connection().commit();
return null;
}
});
}
至此再运行上述代码,出现了一个奇怪的问题:虽然控制台把insert语句打出来了,但是:数据库
没有出现任何新的记录。
究其原因,有二:
一、 session.connection()。commit()确实导致数据库事务提交了,但是此刻session并没有
向数据库发送任何语句。
二、在spring后继的flushIfNecessary()和closeSessionOrRegisterDeferredClose()方法中,
第一个方法向数据
库发送sql语句,第二个方法关闭session,同时关闭connection,然后问题在于:connection已经在程
序中被手动设置为
auttocommit=false了,因此在关闭数据库时,也不会提交事务。
解决这个问题很容易,在程序中手动调用session.flush()就可以了。如下代码:
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
//保存stu1
Student stu1=new Student();
stu1.setName("aaaa");//在数据库中,name字段不允许为null
session.save(stu1);
Student stu2 = new Student();
session.save(stu2);//没有设置name字段,预期会报出例外
session.flush();//向数据库发送sql
session.connection().commit();
return null;
}
});
}
运行上述代码,打开数据库查看,没有新增任何记录。在代码中新加一行stu2.setName("aaa");
再次运行代码,
发现数据库表中多了两条记录。事务操作成功。
至此,虽然操作成功,但事情还没有结束。这是因为spring在调用doInHibernate()的后继的步骤
中,还要进行
flushIfNecessary()操作,这个操作其实最后调用的还是session.flush()。因为在程序中已经手动
调用过
session.flush(),所以由spring调用的session.flush()并不会对数据库发送sql(因为脏数据比对
的原因)。
虽然不会对结果有什么影响,但是多调了一次flush(),还是会对性能多少有些影响。能不能控制让
spring不调
用session.flush()呢?可以的,只要加上一句代码,如下所示:
public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().setFlushMode(0);//0也就是FLUSH_NEVER
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
//保存stu1
Student stu1=new Student();
stu1.setName("aaaa");//在数据库中,name字段不允许为null
session.save(stu1);
Student stu2 = new Student();
stu2.setName("sdf");
session.save(stu2);//没有设置name字段,预期会报出例外
session.flush();
session.connection().commit();
return null;
}
});
}
通过设置HibernateTemplate的flushMode=FLUSH_NEVER来通知spring不进行session.flush()的调
用,则spring
的flushIfNecessary()将不进行任何操作,它的flushIfNecessary()源代码如下:
protected void flushIfNecessary(Session session, boolean existingTransaction) throws
HibernateException {
if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() !=
FLUSH_NEVER)) {
logger.debug("Eagerly flushing Hibernate session");
session.flush();
}
}
至此,代码1中的main()终于修改完毕。但事实上,这样的操作无疑是比较麻烦的,因此如果在
spring中想利
用session进行事务操作时,最好还是用TransactionTemplate(编程式事务)或是声明式事务比较方便
一些。
本例通过这么一个虽然简单但又绕来绕去的例子,主要是说明hibernate事务的一些内在特性,以及
HibernateTemplate中如何处理 session和事务的开关,让读者对HibernateTemplate的源代码处理细节
有一些了解,
希望能给读者有抛砖引玉的作用。
========
HibernateTemplate用法
private HibernateTemplate hibernateTemplate;
?
使用HbernateTemplate
HibernateTemplate提供持久层访问模板化,使用HibernateTemplate无须实现特定接口,它只需要提供
一个SessionFactory的引用,就可执行持久化操作。SessionFactoyr对象可通过构造参数传入,或通过
设值方式传入。如下:
//获取Spring上下文
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//通过上下文获得SessionFactory
SessionFactory sessionFactory = (SessionFactory) ctx.getBean(“sessionFactory”);
然后创建HibernateTemplate实例。HibernateTemplate提供如下三个构造函数
q HibernateTemplate()
q HibernateTemplate(org.hibernate.SessionFactory sessionFactory)
q HibernateTemplate(org.hibernate.SessionFactory sessionFactory, boolean allowCreate)
第一个构造函数,构造一个默认的HibernateTemplate实例,因此,使用HibernateTemplate实例之前,
还必须使用方法setSessionFactory(SessionFactory sessionFactory)来为HibernateTemplate传入
SessionFactory的引用。
第二个构造函数,在构造时已经传入SessionFactory引用。
第三个构造函数,其boolean型参数表明:如果当前线程已经存在一个非事务性的Session,是否直接返
回此非事务性的Session。
对于在Web应用,通常启动时自动加载ApplicationContext,SessionFactory和DAO对象都处在Spring上
下文管理下,因此无须在代码中显式设置,可采用依赖注入解耦SessionFactory和DAO,依赖关系通过配
置文件来设置,如下所示:
<?xml version="1.0" encoding="gb2312"?>
<!-- Spring配置文件的DTD定义-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- Spring配置文件的根元素是beans-->
<beans>
<!--定义数据源,该bean的ID为dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 指定数据库驱动-->
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<!-- 指定连接数据库的URL-->
<property name="url"><value>jdbc:mysql://wonder:3306/j2ee</value></property>
<!-- root为数据库的用户名-->
<property name="username"><value>root</value></property>
<!-- pass为数据库密码-->
<property name="password"><value>pass</value></property>
</bean>
<!--定义Hibernate的SessionFactory-->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 依赖注入数据源,注入正是上文定义的dataSource>
<property name="dataSource"><ref local="dataSource"/></property>
<!-- mappingResouces属性用来列出全部映射文件>
<property name="mappingResources">
<list>
<!--以下用来列出所有的PO映射文件-->
<value>lee/Person.hbm.xml</value>
</list>
</property>
<!--定义Hibernate的SessionFactory的属性 -->
<property name="hibernateProperties">
<props>
<!-- 指定Hibernate的连接方言-->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<!-- 不同数据库连接,启动时选择create,update,create-drop-->
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 配置Person持久化类的DAO bean-->
<bean id="personDao" class="lee.PersonDaoImpl">
<!-- 采用依赖注入来传入SessionFactory的引用>
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
</beans>
DAO实现类中,可采用更简单的方式来取得HibernateTemplate的实例。代码如下:
public class PersnDAOImpl implements PersonDAO
{
//以私有的成员变量来保存SessionFactory。
private SessionFactory sessionFactory;
//设值注入SessionFactory必需的setter方法
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
public List loadPersonByName(final String name)
{
HibernateTemplate hibernateTemplate =
new HibernateTemplate(this.sessionFactory);
//此处采用HibernateTemplate完成数据库访问
}
}
10.6.1 HibernateTemplate的常规用法
HibernateTemplate提供非常多的常用方法来完成基本的操作,比如通常的增加、删除、修改、查询等操
作,Spring 2.0更增加对命名SQL查询的支持,也增加对分页的支持。大部分情况下,使用Hibernate的
常规用法,就可完成大多数DAO对象的CRUD操作。下面是HibernateTemplate的常用方法简介:
q void delete(Object entity):删除指定持久化实例
q deleteAll(Collection entities):删除集合内全部持久化类实例
q find(String queryString):根据HQL查询字符串来返回实例集合
q findByNamedQuery(String queryName):根据命名查询返回实例集合
q get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例
q save(Object entity):保存新的实例
q saveOrUpdate(Object entity):根据实例状态,选择保存或者更新
q update(Object entity):更新实例的状态,要求entity是持久状态
q setMaxResults(int maxResults):设置分页的大小
下面是一个完整DAO类的源代码:
public class PersonDAOHibernate implements PersonDAO
{
//采用log4j来完成调试时的日志功能
private static Log log = LogFactory.getLog(NewsDAOHibernate.class);
//以私有的成员变量来保存SessionFactory。
private SessionFactory sessionFactory;
//以私有变量的方式保存HibernateTemplate
private HibernateTemplate hibernateTemplate = null;
//设值注入SessionFactory必需的setter方法
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
//初始化本DAO所需的HibernateTemplate
public HIbernateTemplate getHibernateTemplate()
{
//首先,检查原来的hibernateTemplate实例是否还存在
if ( hibernateTemplate == null)
{
//如果不存在,新建一个HibernateTemplate实例
hibernateTemplate = new HibernateTemplate(sessionFactory);
}
return hibernateTemplate;
}
//返回全部的人的实例
public List getPersons()
{
//通过HibernateTemplate的find方法返回Person的全部实例
return getHibernateTemplate().find("from Person");
}
public void savePerson(Person person)
{
getHibernateT
========
使用HibernateTemplate进行数据库功能开发完成Spring+Hibernate架构
在Spring架构基础上 将其中的JdbcTemplate访问数据库的代码改成用HibernateTemplate访问数据库 最
后形成Spring+Hibernate的软件架构
准备工作:
1 删除目录\src\com\demo\spring\dao 该目录下面的代码是基于JdbcTemplate开发的DAO层代码 我们
将开发基于HibernateTemplate的DAO层来替换它
2 复制jar文件到当前项目的\WEB-INF\lib目录下 使项目来支持Hibernate事务 以及能够提供对
Hibernate映射文件的解析
dom4j-1.6.jar
antlr-2.7.5H3.jar
jta.jar
3 基于Hibernate的开发 都需要创建数据库表的映射文件和持久化类 将User.hbm.xml
AbstractUser.java User.java 复制到当前项目中\src\com\demo\hibernate\beans
开发过程:
需要在Bean配置文件中添加一系列组件 这些组件相互注入
dataSource 为sessionFactory提供数据源
sessionFactory Hibernate管理工厂 为userDAO的事务管理策略对象transactionManager提供
sessionFactory
transactionManager 为userDAO提供POJO类 利用被注入的数据源 定义一系列的业务操作函数 使用
JdbcTemplate来操作POJO类 实现对数据库的操作
UserDAOProxy userDAO的事务管理策略对象 该对象规定了userDAO的管理策略 因此他需要注入userDAO
作为被管理的对象 进行事务管理也需要提供事务管理器 因此需要注入transactionManager作为事务管
理器
Action类 该类是响应处理类 负责调度userDAO函数即可实现数据库的访问 而对于userDAO的访问都将在
UserDAOProxy的监管之下
图示:
dataSource 定义数据源
| 注入数据源
sessionFactory
注入Hibernate | | 注入Hibernate
POJO userDAO transactionManager
注入DAO | | 注入事务 |
Action类 UserDAOProxy
步骤:
1 创建数据源dataSource
2 创建sessionFactory 并注入dataSource
3 创建事务管理对象transactionManager 并注入sessionFactory
4 创建userDAO 并注入sessionFactory
5 创建事务管理策略对象UserDAOProxy 并注入管理策略对象userDAO和所用的事务管理器
transactionManager
6 创建Action 并注入userDAO
详细步骤:
1 数据源配置
在applicationContext.xml中添加一个名称为dataSource的 指向的类为DBCP的数据源类
<!-- 配置数据源 -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>org.gjt.mm.mysql.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/demo</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
2 SessionFactory的配置
配置SessionFactory对象 为DAO层提供HIbernate的数据库连接对象 注入配置的dataSource对象
<!-- 配置Hibernate -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>com/demo/hibernate/beans/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
3 配置事务
为SessionFactory对象增加事务配置组件 并注入上面配置的SessionFactory对象
<!-- 配置事务 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
4 配置DAO组件
我们先配置该组件的Bean对象 一个基于HibernateTemplate的DAO类
com.demo.hibernate.dao.UserDAO.java 并为这个对象注入SessionFactory对象
<!-- 定义DAO -->
<bean id="userDAO" class="com.demo.hibernate.dao.UserDAO">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
5 配置DAO事务
为上面配置的DAO对象配置事务组件 使得对userDAO的访问都在spring的事务监管下 改组件需要注入上
面配置的事务对象transactionManager DAO对象userDAO 并配置事务管理的策略
<!-- 定义DAO代理 -->
<bean id="UserDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="userDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
6 创建Hibernate DAO 类
首先创建一个接受类 com.demo.hibernate.dao.IUserDAO.java 创建接口的原因是为了让Spring的
AOP机制能够进行事务的管理 因为事务的管理是基于AOP实现的
在接口中声明要实现的操作函数
package com.demo.hibernate.dao;
import java.util.List;
import com.demo.hibernate.beans.User;
public interface IUserDAO {
public boolean isValid(final String username, final String password);
public boolean i***ist(String username);
public void insertUser(User user);
public User getUser(String userid);
public List getUsers();
public void deleteUser(String userid);
}
在UserDAO.java 中实现声明的函数
package com.demo.hibernate.dao;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.demo.hibernate.beans.User;
public class UserDAO extends HibernateDaoSupport implements IUserDAO {
public boolean isValid(final String username, final String password) {
List list = (List) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException {
List result = session.createCriteria(User.class).add(
Restrictions.eq("username", username)).add(
Restrictions.eq("password", password)).list();
return result;
}
});
if (list.size() > 0) {
return true;
} else {
return false;
}
}
public boolean i***ist(final String username) {
List list = (List) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException {
List result = session.createCriteria(User.class).add(
Restrictions.eq("username", username)).list();
return result;
}
});
if (list.size() > 0) {
return true;
} else {
return false;
}
}
public void insertUser(User user) {
getHibernateTemplate().saveOrUpdate(user);
}
public User getUser(String userid) {
return (User) getHibernateTemplate().get(User.class,
new Integer(userid));
}
public List getUsers() {
return getHibernateTemplate().find("from User");
}
public void deleteUser(String userid) {
Object p = getHibernateTemplate().load(User.class, new Integer(userid));
getHibernateTemplate().delete(p);
}
}
至此 就完成了使用HibernateTemplate访问数据库的DAO代码的开发了 下面修改Action中的业务函数来
调用DAO中的函数
7 修改LoginAction访问UserDAO进行登录验证
首先为LoginAction添加一个变量
UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
为applicationContext.xml中的loginAction的配置注入userDAO变量
<!---定义Action-->
<bean id="loginAction"
class="com.demo.spring.actions.LoginAction">
<property name="commandClass">
<value>com.demo.spring.forms.LoginForm</value>
</property>
<!-- 指定DAO类 -->
<property name="userDAO">
<ref local="userDAO" />
</property>
<!-- 指定验证类 -->
<property name="validator">
<ref local="loginValidator" />
</property>
<!-- 指定失败要返回的页面 -->
<property name="formView">
<value>login</value>
</property>
<!-- 指定成功要返回的页面 -->
<property name="successView">
<value>welcome</value>
</property>
</bean>
修改LoginAction处理类中用户登录验证函数isValid() 使用被注入对象userDAO的isValid()函数
public boolean isValid(LoginForm loginForm) {
/*if (loginForm.getUsername().equals("admin")
|| loginForm.getPassword().equals("admin")) {
return true;
} else {
return false;
}*/
if(userDAO.isValid(loginForm.getUsername(), loginForm.getPassword())) {
return true;
} else {
return false;
}
}
8 RegisterAction访问UserDAO进行用户注册
方法如7
这样就形成了Spring+Hibernate架构
========
hibernateTemplate内部实现原理
Spring中 Callback模式和Template模式合用 随处可见。下面以常用的HibernateTemplate为例进行简要
简述。
在HibernateTemplate模板类中有一个核心的方法:doExecute,这个核心的方法采用模板方法 完成相关
的固定 操作(建立连接,执行操作,释放连接) ,其中的具体步骤通过回调传入的对象(这个对象就
是实现了Callback接口的类)来完成。
[java] view plain copy print?
import org.springframework.orm.hibernate3.HibernateTemplate;
@Component("userDaoImpl")
public class UserDaoImpl implements UserDao {
private HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate()
{
return hibernateTemplate;
}
@Resource(name="hibernatTemplate")
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
@Override
public void save(User u) {
hibernateTemplate.save(u);
//这里可以直接save了,session已经被hibernateTemplate处理了。我们不需要关心它了。
//因为HibernateTemplate中已经注入了SessionFactory了,因为它自己会处理好session及其
事务的。
System.out.println("user save...");
}}
下面讲一下HibernateTemplate的内部实现原理
[java] view plain copy print?
//建立HibernateTemplate回调函数
interface HibernateCallback
{
//内部只有doInHibernate方法
public Object doInHibernate(Session session);
}
//建立Session类为实际该类为sessionFactory创建
class Session
{
//这里只列出createQuery方法,实际存在save,update,load等方法
public Object createQuery(String hql)
{
System.out.println(hql);
return "添加成功";
}
}
//HibernateTemplate类
class BackCallTemplate
{
//这里是执行的方法
public Object execute(HibernateCallback backCall)
{
//这个方法的请求处理交给doExceute;
return doExecute(backCall);
}
//它来做请求处理
public Object doExecute(HibernateCallback action)
{
//首先去判断是否已经有一个session对象,如果没有则创建一个,存在就返回Session对象
//Session session = (enforceNewSession ?SessionFactoryUtils.getNewSession
(getSessionFactory(), getEntityInterceptor()) : getSession());
Session session = new Session();
//这里执行架设函数
return action.doInHibernate(session);
}
}
public class TempateMain
{
//这里仅仅使用main作测试
public static void main(String[] args)
{
//传递:
//1.得到HibernateTemplate对象
//2.向HibernateTemplate.execute方法中传递HibernateCallback对象
//3.覆写HibernateCallback对象
BackCallTemplate getHibernateTemplate = new BackCallTemplate();
//我们使用的是回调函数,在回调函数里处理行为
//疑问:怎么才能调用getHibernateTempleate.save(X x)呢?
//答:可将Main方法进行封装,通过方法参数传递参数即可!
String str = (String) getHibernateTemplate.execute(new HibernateCallback()
{
public Object doInHibernate(Session session)
{
return session.createQuery("select count(id) from table");
}
});
System.out.println(str);
}
}
参考文档:http://blog.csdn.net/itpinpai/article/details/8236547
========