读书人

Spring调整Hibernate (转)

发布时间: 2012-10-08 19:54:56 作者: rapoo

Spring整合Hibernate (转)

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>   Spring配置文件中SessionFactory初始化配置方法:<bean id="sessionFactory"             value="classpath:hibernate.cfg.xml">         </property> </bean>2) 在Spring配置文件中整合所有Hibernate配置参数<bean id="dataSource" 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" 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项目下找到。否则运行程序会出现以下异常:java.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实现DAOpublic 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" />         </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" />         </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"     />         </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" 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" />         </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"             />         </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);

读书人网 >软件架构设计

热点推荐