您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
使用MyEclipse整合Spring和Hibernate
 
作者:小徐 来源:新浪微博 发布于 2015-7-29
   次浏览      
 

开发环境:

JDK 1.5
Tomcat 6.0
Spring 2.5
Struts 1.3
Hibernate 3.2
DWR 2.0
MyEclipse 6.5
SQL Server 2005

Spring整合Hibernate的价值在于Spring为Hibernate增加了以下内容:

Session management:Spring为Hibernate的session提供了有效、容易和安全的控制

Resource management:Spring控制Hibernate的SessionFactories,JDBC datasources及其它相关资源

Integrated transaction management:完整的事务管理

Exception wrapping:异常的包装

1. 利用Spring IoC容器创建SessionFactory

可以使用org.springframework.orm.hibernate3.LocalSessionFactoryBean创建SessionFactory实例, 共有以下二种方式:

1) 【最佳方案】直接使用Hibernate配置文件hibernate.cfg.xml

Hibernate配置文件hibernate.cfg.xml如下:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="myeclipse.connection.profile">
com.microsoft.sqlserver.jdbc.SQLServerDriver
</property>
<property name="connection.url">
jdbc:sqlserver://localhost:1433;databaseName=SSH
</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="connection.driver_class">
com.microsoft.sqlserver.jdbc.SQLServerDriver
</property>
<property name="dialect">
org.hibernate.dialect.SQLServerDialect
</property>
<mapping resource="cn/qdqn/ssh/entity/UserInfo.hbm.xml" />
</session-factory>
</hibernate-configuration>

pring配置文件中SessionFactory初始化配置方法:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:hibernate.cfg.xml">
</property>
</bean>

2) 在Spring配置文件中整合所有Hibernate配置参数

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"
value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url"
value="jdbc:sqlserver://localhost:1433;databaseName=SSH"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource">
</property>
<property name="mappingResources">
<list>
<value>cn/qdqn/ssh/entity/UserInfo.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</prop>
<prop key="show_sql">true</prop>
</props>
</property>
</bean>

注意:使用MyEclipse集成SSH时,org.apache.commons.dbcp.BasicDataSource所在的包commons-dbcp-1.2.2.jar不会默认加载,另外还需加载commons-pool-1.4.jar,两者均可在Apache网站commons项目下找到。否则运行程序会出现以下异常:

ava.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
……

2. Hibernate DAO开发

1) 使用Hibernate原生API实现DAO

A. 使用原生API实现DAO

public class UserInfoDAORaw implements IUserInfoDAO {
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public List findAll() {
return this.sessionFactory.getCurrentSession()
.createQuery("from UserInfo").list();
} 部分代码省略……
}
   B. 在applicationContext.xml配置原生DAO Bean 

<bean id="userInfoDAORaw" class="cn.qdqn.ssh.dao.UserInfoDAORaw">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>

C. 运行测试

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAORaw dao=(UserInfoDAORaw)ctx.getBean("userInfoDAORaw");
List<UserInfo> list=dao.findAll();
for(UserInfo info : list){
System.out.println(info.getUserName()+"-"+info.getUserPwd());
}

结论:使用Hibernate原生API实现DAO可以做到Hibernate和Spring完全分离,缺点是无法利用Spring封装Hibernate所提供的额外功能。

2)【最佳方案】使用Spring框架所提供的HibernateDaoSupport类实现DAO

A. 使用MyEclipse反向工程生成Spring 整合Hibernate 的DAO,该DAO继承自Spring的

org.springframework.orm.hibernate3.support.HibernateDaoSupport 

public class UserInfoDAO extends HibernateDaoSupport {
private static final Log log = LogFactory.getLog(UserInfoDAO.class);
// property constants
public static final String USER_NAME = "userName";
public static final String USER_PWD = "userPwd";
public void save(UserInfo transientInstance) {
log.debug("saving UserInfo instance");
try {
getHibernateTemplate().save(transientInstance);
log.debug("save successful");
} catch (RuntimeException re) {
log.error("save failed", re);
throw re;
}
}
部分代码省略……
}
   B. 在applicationContext.xml配置DAO Bean 

<bean id="userInfoDAO" class="cn.qdqn.ssh.dao.UserInfoDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>

C. 运行测试

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAORaw dao=(UserInfoDAORaw)ctx.getBean("userInfoDAO");
List<UserInfo> list=dao.findAll();
for(UserInfo info : list){
System.out.println(info.getUserName()+"-"+info.getUserPwd());
}

注意:HibernateDaoSupport通过getHibernateTemplate()方法得到HibernateTemplate实例进行保存、删除等操作,但是HibernateTemplate默认不进行事务处理,而在Hibernate中这些操作必须在事务下执行才能得到正确的结果,因此必须使用Spring声明式事务管理。

3. 使用Spring声明式事务管理

1) 使用Spring 1.x 的事务代理类进行事务管理

A. 在applicationContext.xml中声明事务管理器,注入sessionFactory属性

<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>

B. 在applicationContext.xml中使用Spring AOP代理方式实现声明式事务

<bean id="userInfoDAOProxy" class=
"org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!--必须为true时CGLIB才不用强制编写DAO接口-->
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<ref bean="userInfoDAO"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

C. 通过代理Bean获取DAO Bean,进行数据库操作

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAO dao=(UserInfoDAO)ctx.getBean("userInfoDAOProxy");
UserInfo user=new UserInfo();
user.setUserName("比尔盖茨");
user.setUserPwd("windows");
dao.save(user);

问题1:运行程序会报以下异常:

java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit
(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
at net.sf.cglib.core.ClassEmitter.begin_class(ClassEmitter.java:77)
at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:173)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:117)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104)
at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69)
…………

解决方法:原因是Spring与Hibernate所使用的asm版本冲突,删除asm.2.2.3.jar即可。

问题2:对每个业务逻辑Bean或DAO Bean都要设置事务代理Bean将是一个非常庞大的工作量!

改进方法: 可以通过定义“基类”来解决重复性编码!如:

<bean id="baseDAOProxy" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<bean id="userInfoDAOProxy" parent="baseDAOProxy">
<property name="target">
<ref bean="userInfoDAO"/>
</property>
</bean>

结论:采用Spring 1.x配置事务要额外配置一个代理对象,原来Bean的获取方式也要修改,因此,也是一种“侵入式”的解决方案,虽然没有侵入到Bean程序代码中。

2) 使用Spring 2.x 的aop 和tx 声明式配置进行事务管理

A. 在applicationContext.xml中添加aop和tx名称空间

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
…………
</beans>

B. 在applicationContext.xml中声明事务管理器,注入sessionFactory属性

<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>

C. 通过 <tx:advice>定义事务通知

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="do*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>

D. 将事务通知advice和切面pointcut组合起来

<aop:config>
<aop:pointcut id="daoMethods" expression="execution(* cn.qdqn.ssh.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/>
</aop:config>

E. 两种应用测试:

a) 对于Java Application,直接获取DAO Bean,进行数据库操作

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAO dao=(UserInfoDAO)ctx.getBean("userInfoDAO");
UserInfo user=new UserInfo();
user.setUserName("比尔盖茨");
user.setUserPwd("windows");
dao.save(user);

问题:运行程序会报以下异常

Exception in thread "main" java.lang.ClassCastException: $Proxy1
at cn.qdqn.ssh.test.AddUserInfo.main(AddUserInfo.java:18)

解决方法:此时唯有JDK 基于接口的代理将起作用,因此每个BO或DAO类必须要有对应的Interface,可以使用MyEclipse的重构功能生成BO或DAO类的接口定义,将获取的BO或DAO Bean放在相应接口对象的引用中即可。代码修改如下:

ApplicationContext ctx=
new ClassPathXmlApplicationContext("applicationContext.xml");
IUserInfoDAO dao=(IUserInfoDAO)ctx.getBean("userInfoDAO");
UserInfo user=new UserInfo();
user.setUserName("比尔盖茨");
user.setUserPwd("windows");
dao.save(user);

b) 对于Web Application,在Struts Action定义BO或DAO,通过Spring在action-servlet.xml中进行注入

public class AddAction extends Action {
private UserInfoDAO userInfoDAO;
public UserInfoDAO getUserInfoDAO() {
return userInfoDAO;
}
public void setUserInfoDAO(UserInfoDAO userInfoDAO) {
this.userInfoDAO = userInfoDAO;
}
…………
} <bean name="/add" class="cn.qdqn.ssh.struts.action.AddAction">
<property name="userInfoDAO">
<ref bean="userInfoDAO"/>
</property>
</bean>

问题:启动Tomcat会报以下异常

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name '/add' defined in ServletContext resource
 [/WEB-INF/action-servlet.xml]: Error setting property values;
 nested exception is org.springframework.beans.PropertyBatchUpdateException; 
nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.TypeMismatchException: Failed to convert property value of type [$Proxy1] to required type [cn.qdqn.ssh.dao.UserInfoDAO] for property 'userInfoDAO'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [$Proxy1] to required type [cn.qdqn.ssh.dao.UserInfoDAO] for property 'userInfoDAO': no matching editors or conversion strategy found
Caused by:
org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessException details (1) are:
PropertyAccessException 1:
org.springframework.beans.TypeMismatchException: Failed to convert property value of type [$Proxy1] to required type [cn.qdqn.ssh.dao.UserInfoDAO] for property 'userInfoDAO'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [$Proxy1] to required type [cn.qdqn.ssh.dao.UserInfoDAO] for property 'userInfoDAO': no matching editors or conversion strategy found
…………

解决方法:同Java Application所遇错误相类似,只需将Struts Action定义的等待被注入的BO或DAO替换为其相应的Interface形式即可纠正该错误。如下代码:

public class AddAction extends Action {
private IUserInfoDAO userInfoDAO;
public IUserInfoDAO getUserInfoDAO() {
return userInfoDAO;
}
public void setUserInfoDAO(IUserInfoDAO userInfoDAO) {
this.userInfoDAO = userInfoDAO;
}
…………
}

3) 【最佳方案】使用Spring 2.x 的@Transactional标注进行事务管理

A. 在BO或DAO类中添加事务标注@Transactional

import org.springframework.transaction.annotation.Transactional;
@Transactional
public class UserInfoDAO extends HibernateDaoSupport {
private static final Log log = LogFactory.getLog(UserInfoDAO.class);
public static final String USER_NAME = "userName";
public static final String USER_PWD = "userPwd";
public void save(UserInfo transientInstance) {
log.debug("saving UserInfo instance");
try {
getHibernateTemplate().save(transientInstance);
log.debug("save successful");
} catch (RuntimeException re) {
log.error("save failed", re);
throw re;
}
} 部分代码省略……
}

B. 在applicationContext.xml中添加transactionManager和 <tx:annotation-driven>

<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true"/>

注意:proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用。 

C. 测试运行,一切正常

ApplicationContext ctx=
new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAO dao=(UserInfoDAO)ctx.getBean("userInfoDAOProxy");
UserInfo user=new UserInfo();
user.setUserName("比尔盖茨");
user.setUserPwd("windows");
dao.save(user);
   
次浏览       
相关文章

Java微服务新生代之Nacos
深入理解Java中的容器
Java容器详解
Java代码质量检查工具及使用案例
相关文档

Java性能优化
Spring框架
SSM框架简单简绍
从零开始学java编程经典
相关课程

高性能Java编程与系统性能优化
JavaEE架构、 设计模式及性能调优
Java编程基础到应用开发
JAVA虚拟机原理剖析
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

Java 中的中文编码问题
Java基础知识的三十个经典问答
玩转 Java Web 应用开发
使用Spring更好地处理Struts
用Eclipse开发iPhone Web应用
插件系统框架分析
更多...   

Struts+Spring+Hibernate
基于J2EE的Web 2.0应用开发
J2EE设计模式和性能调优
Java EE 5企业级架构设计
Java单元测试方法与技术
Java编程方法与技术

Struts+Spring+Hibernate/EJB+性能优化
华夏基金 ActiveMQ 原理与管理
某民航公司 Java基础编程到应用开发
某风电公司 Java 应用开发平台与迁移
日照港 J2EE应用开发技术框架与实践
某跨国公司 工作流管理JBPM
东方航空公司 高级J2EE及其前沿技术
更多...