Hibernate in Spring

来源:互联网 发布:qq飞车迈凯轮数据 编辑:程序博客网 时间:2024/05/01 23:06

Hibernate in Spring

Hibernate在开源的持久层框架中无疑是近期最为鲜亮的角色,其作者甚至被邀请加入
新版EJB设计工作之中,足见Hibernate设计的精彩贴切。关于Hibernate的使用,在笔者
的另外一篇文档中进行了探讨:
《Hibernate开发指南》 http://www.xiaxin.net/Hibernate_DEV_GUIDE.rar。
下面主要就Hibernate在Spring中的应用加以介绍,关于Hibernate本身就不多加描
述。
另外考虑到Spring对容器事务的良好支持,笔者建议在基于Spring Framework的应
用开发中,尽量使用容器管理事务,以获得数据逻辑代码的最佳可读性。下面的介绍中,将
略过代码控制的事务管理部分,而将重点放在参数化的容器事务管理应用。代码级事务管理
实现原理与上面JdbcTemplate中基本一致,感兴趣的读者可以参见Spring-Reference中
的相关内容。
出于简洁,我们还是沿用上面的示例。首先,针对Hibernate,我们需要进行如下配置:
Hibernate-Context.xml: 

<beans>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>net.sourceforge.jtds.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value>
</property>
<property name="username">
<value>test</value>
</property>
<property name="password">
<value>changeit</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>net/xiaxin/dao/entity/User.hbm.xml</value>
</list></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.SQLServerDialect
</prop>
<prop key="hibernate.show_sql">
true
</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionMana
ger">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="userDAO" class="net.xiaxin.dao.UserDAO">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<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="get*">PROPAGATION_REQUIRED,readOnly</prop>
</props></property>
</bean>
</beans>

与上面JDBC中的配置相对比,区别主要在于:
1. SessionFactory的引入
Hibernate中通过SessionFactory创建和维护Session。Spring对
SessionFactory的配置也进行了整合,无需再通过Hibernate.cfg.xml对
SessionFactory进行设定。
SessionFactory节点的mappingResources属性包含了映射文件的路径,list
节点下可配置多个映射文件。
hibernateProperties节点则容纳了所有的属性配置。
可以对应传统的Hibernate.cfg.xml文件结构对这里的SessionFactory配置
进行解读。
2. 采用面向Hibernate的TransactionManager实现:
org.springframework.orm.hibernate.HibernateTransactionManag
er
可以看到,对于事务管理配置,基本与上一章节中相同。
对应刚才的Users表,建立如下映射类:
User.java:

/**
* @hibernate.class table="users"
*/
public class User {
public Integer id;
public String username;
public String password;
/**
* @hibernate.id
* column="id"
* type="java.lang.Integer"
* generator-class="native"
*/
public Integer getId() {
return id;}
public void setId(Integer id) {
this.id = id;
}
/**
* @hibernate.property column="password" length="50"
*/
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
* @hibernate.property column="username" length="50"
*/
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

上面的代码中,通过xdoclet指定了类/表;属性/字段的映射关系,通过xdoclet ant
task 我们可以根据代码生成对应的user.hbm.xml文件。具体细节请参见《hibernate开
发指南》一文。
下面是生成的user.hbm.xml:

<hibernate-mapping>
<class
name="net.xiaxin.dao.entity.User"
table="users"
dynamic-update="false"
dynamic-insert="false"
>
<id
name="id"
column="id"type="java.lang.Integer"
>
<generator class="native">
</generator>
</id>
<property
name="password"
type="java.lang.String"
update="true"
insert="true"
access="property"
column="password"
length="50"
/>
<property
name="username"
type="java.lang.String"
update="true"
insert="true"
access="property"
column="username"
length="50"
/>
</class>
</hibernate-mapping>

UserDAO.java:
public class UserDAO extends HibernateDaoSupport implements IUserDAO
{
public void insertUser(User user) {
getHibernateTemplate().saveOrUpdate(user);
}
}
看到这段代码想必会有点诧异,似乎太简单了一点……,不过这已经足够。短短一行
代码我们已经实现了与上一章中示例相同的功能,这也正体现了Spring+Hibernate
的威力所在。

 上面的UserDAO实现了自定义的IUserDAO接口(这里的IUserDAO接口仅包含
insertUser方法的定义,不过除此之外,它还有另一层含义,见下面的代码测试部分),并扩展了抽象类:
HibernateDaoSupport
HibernateSupport实现了HibernateTemplate和SessionFactory实例的关联。
与JdbcTemplate类似,HibernateTemplate对Hibernate Session操作进行了封
装,而HibernateTemplate.execute方法则是一封装机制的核心,感兴趣的读者可以
研究一下其实现机制。
借助HibernateTemplate我们可以脱离每次数据操作必须首先获得Session实例、启
动事务、提交/回滚事务以及烦杂的try/catch/finally的繁琐操作。从而获得以上代码
中精干集中的逻辑呈现效果。
对比下面这段实现了同样功能的Hibernate原生代码,想必更有体会:

Session session
try {
Configuration config = new Configuration().configure();
SessionFactory sessionFactory =
config.buildSessionFactory();
session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setName("erica");
user.setPassword("mypass");
session.save(user);
tx.commit();
} catch (HibernateException e) {
e.printStackTrace();
tx.rollback();
}finally{
session.close();
}

测试代码:
InputStream is = new FileInputStream("Hibernate-Context.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
IUserDAO userDAO = (IUserDAO)factory.getBean("userDAOProxy");User user = new User();
user.setUsername("erica");
user.setPassword("mypass");
userDAO.insertUser(user);

这段代码似乎并没有什么特殊,但有一个细微之处:
IUserDAO userDAO = (IUserDAO)factory.getBean("userDAOProxy");
这里并没有直接用UserDAO对获得的Bean实例进行强制转型。这与上面
JdbcTemplate的测试代码不同。并非完全出自设计上的考虑,这里情况有些特殊,我们可
以尝试一下用UserDAO类对bean实例进行强制转型,不过将得到一个
ClassCastException,程序异常中止。
为什么会出现这样的问题?是不是只有在使用Hibernate才会出现这样的问题?事实
并非如此,如果对上面基于JdbcTempate的UserDAO进行改造,使之实现IUserDAO接口,
同样的问题也将会出现。IUserDAO接口本身非常简单(仅包含一个insertUser方法的定
义),显然也不是导致异常的原因所在。
原因在于Spring的AOP实现机制,前面曾经提及,Spring中的事务管理实际上是基于
动态AOP机制实现,为了实现动态AOP,Spring在默认情况下会使用Java Dynamic
Proxy,但是,Dynamic Proxy要求其代理的对象必须实现一个接口,该接口定义了准备
进行代理的方法。而对于没有实现任何接口的Java Class,需要采用其他方式,Spring通
过CGLib10实现这一功能。
当UserDAO没有实现任何接口时(如JdbcTemplate示例中)。Spring通过CGLib对
UserDAO进行代理,此时getBean返回的是一个继承自UserDAO类的子类实例,可以通
过UserDAO对其强制转型。而当UserDAO实现了IUserDAO接口之后,Spring将通过Java
Dynamic Proxy机制实现代理功能,此时返回的Bean,是通过
java.lang.reflect.Proxy.newProxyInstance方法创建的IUserDAO接口的一个代理实
现,这个实例实现了IUserDAO接口,但与UserDAO类已经没有继承关系,因此无法通过
UserDAO强制转型。
由于此问题牵涉到较为底层的代理机制实现原理,下面的AOP章节中我们再进行详细
探讨。
实际开发中,应该面向接口编程,通过接口来调用Bean提供的服务。

原创粉丝点击