Chinaunix首页 | 论坛 | 博客
  • 博客访问: 438745
  • 博文数量: 161
  • 博客积分: 5005
  • 博客等级: 上校
  • 技术积分: 1090
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-20 16:38
文章分类

全部博文(161)

文章存档

2011年(21)

2010年(33)

2009年(89)

2008年(18)

我的朋友

分类: Java

2009-04-19 21:01:25

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如下:


          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
         "">

   
       
                com.microsoft.sqlserver.jdbc.SQLServerDriver
       

       
                jdbc:sqlserver://localhost:1433;databaseName=SSH
       

        sa
       
       
                com.microsoft.sqlserver.jdbc.SQLServerDriver
       

       
                org.hibernate.dialect.SQLServerDialect
       

       
   


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


                    value="classpath:hibernate.cfg.xml">
       

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


                                  value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
                                  value="jdbc:sqlserver://localhost:1433;databaseName=SSH"/>
       
       


       
       

       
           
               cn/qdqn/ssh/entity/UserInfo.hbm.xml
           

       

       
           
               
                     org.hibernate.dialect.SQLServerDialect
               

                true
           

       


  注意:使用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实现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 


       
           
       


   C. 运行测试

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAORaw dao=(UserInfoDAORaw)ctx.getBean("userInfoDAORaw");
List 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 


       
           
       


   C. 运行测试

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoDAORaw dao=(UserInfoDAORaw)ctx.getBean("userInfoDAO");
List 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属性

    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       
           
       


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

                                                        "org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
       
       
            true
       

       
           
       

       
           
       

       
           
                PROPAGATION_REQUIRED
           

       


   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.(Enhancer.java:69)
…………
解决方法:原因是Spring与Hibernate所使用的asm版本冲突,删除asm.2.2.3.jar即可。
  问题2:对每个业务逻辑Bean或DAO Bean都要设置事务代理Bean将是一个非常庞大的工作量!
  改进方法: 可以通过定义“基类”来解决重复性编码!如:


       
            true
       

       
           
       

       
           
                PROPAGATION_REQUIRED
           

       


    

       
           
       


  结论:采用Spring 1.x配置事务要额外配置一个代理对象,原来Bean的获取方式也要修改,因此,也是一种“侵入式”的解决方案,虽然没有侵入到Bean程序代码中。
2) 使用Spring 2.x 的aop 和tx 声明式配置进行事务管理
   A. 在applicationContext.xml中添加aop和tx名称空间

    xmlns:aop=""
    xmlns:tx=""
    xmlns:xsi=""
    xsi:schemaLocation="
       
       
       
       
       
        ">

…………

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

class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       
           
       


   C. 通过 定义事务通知


       
           
           
           
           
           
       


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


       
       

   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;
    }

…………
}

     
       
     


        问题:启动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和

            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       
           
       


                                       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);

阅读(1464) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~