Spring與Hibernate的整合與解耦

Hibernate與Spring整合後,就可以使用IoC及AOP的功能了,好處不在多言。另外一個好處,就是可以通過使用Spring的HibernateTemplate來簡化數據庫CRUD代碼。然而,正是這個HibernateTemplate,存在着以下的缺點:

一是功能不全,不如Hibernate的儲如createQuery()等方法方便、靈活與強大,使用頗受限制;
二是HibernateTemplate中的SessionFacotry封裝得太死,且session常常會自動過早關閉,使用上頗多不便;
三是Spring1.2.7實際上只支持Hibernate3.0.5,HibernateTemplate無法使用Hibernate3.1以後新加的功能。

正是由於這三點,使我抵制住了使用HibernateTemplate的誘惑,在將Spring與Hibernate整合後,通過簡單的配置,在Spring程序中自由地使用Hibernate3.1.2的功能。這樣既可以實現強強聯手,又可以在Hibernate新版本出來後,馬上進行重新整合,無需等着新版的Spring出來。

在實現上,主要有三點。

一是在Spring的配置文件中,無需定義長長的hibernateTemplate,只需定義sessionFacotry就行了。

例如,設有一"組織DaoImpl",實現了"組織Dao"及"HibernateDao"兩個接口。而"組織Service"通過將"組織DaoImpl"封裝起來,對外提供相應的數據庫功能服務。

public interface HibernateDao {
    public void setSessionFactory(SessionFactory sessionFactory);
}

public interface 組織Dao {
    public void 增加組織(組織 組織);
    public void 刪除組織(Long 組織編號);
    public 組織 get組織By名稱(String 組織名稱);
    ......
}

public class 組織DaoImpl implements 組織Dao, HibernateDao {
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public 組織 get組織By名稱(String 組織名稱) {
        ......
    }
}

public interface 組織Service {
    public 組織 get組織By名稱(String 組織名稱);
    ......
}

(實際代碼中,組織Service與組織DaoI的接口是一樣的)

先在Spring的配置文件中定義"組織Service"與"組織Dao":

    <bean id="組織Service"
        class="com.sarkuya.service.組織ServiceImpl">
        <property name="組織Dao">
            <ref bean="組織Dao" />
        </property>
    </bean>

    <bean id="組織Dao"
        class="com.sarkuya.model.dao.hibernate.組織DaoImpl">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>

這一步比較好理解。問題是如何將我們所需的sessionFactory傳過來。Hibernate的sessionFactory不是一個可以用構造方法或setter方法就可以直接生成的類,而是需要進行一定的"運算"後才得出的類,典型的形式如:

sessionFactory = new Configuration().buildSessionFactory();

因此,簡單地在Spring中如下配置行不通。

    <bean id="sessionFactory"
        class="org.hibernate.SessionFactory">
        <property name="xxx">
            <value>xxx</valu>
        </property>
    </bean>

所幸,Spring提供了一種可定義由工廠方法所產生的bean的方式。

    <bean id="sessionFactory"
        class="com.sarkuya.hibernate.util.HibernateUtil"
        factory-method="getSessionFactory" />

這種方式,當我們需要得到sessionFacotry的bean時,Spring就會從com.sarkuya.hibernate.util.HibernateUtil的getSessionFactory()方法中獲得。這種方式,我們最熟悉不過了:

public class HibernateUtil {
    private static final SessionFactory sessionFactory;
   
    static {
        try {
            sybaseSessionFactory = new Configuration()
                .addClass(com.sarkuya.model.cfg.hibernate.組織.class)
                .configure("/hibernate_hsql.cfg.xml")
                .buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
   
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

好了,以上步驟,我們已經成功地將sessionFactory注入組織Dao中,再注入組織Service中,根據測試先行原則,看看如何在TestCase中獲取組織Service這個bean。

二,在代碼中獲取組織Service。

public class 組織ServiceTest extends TestCase {
    private 組織Service 組織Service;

    protected void setUp() throws Exception {
        String[] configLocation = new String[] {
            "/web/WEB-INF/training-service.xml",
            "/web/WEB-INF/training-data.xml"
        };
        ApplicationContext ac = new FileSystemXmlApplicationContext(configLocation);
        組織Service = (組織Service)ac.getBean("組織Service");
    }

    public void testGet組織By名稱() {
        System.out.println("get組織By名稱");
       
        組織 組織 = 組織Service.增加組織(new 組織("aaa"));
       
        組織 = 組織Service.get組織By名稱("aaa");
        assertEquals("aaa", 組織.get名稱());
       
        組織 = 組織Service.get組織By名稱("abc");
        assertNull(組織);
    }
}

三,組織DaoImpl的代碼:

    /*
     * @param 組織名稱
     * @return 組織 或者 null
     */
    public 組織 get組織By名稱(String 組織名稱) {
        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();                             //Hiberante3.1以後的代碼      
        
        組織 組織 = (組織)session.createQuery("from 組織 c where c.名稱 = '" + 組織名稱  + "'")
            .uniqueResult();
       
        session.getTransaction().commit();
       
        return 組織;
    }

由上可見,Hibernate的核心代碼一點未變,從而在與Spring整合的基礎上,實現了與Spring的解耦。


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章