關於在 Spring 中對 DAO 進行單元測試的一些問題

最近在使用 Spring 2.0 和 Hibernate 3.2.0 進行開發,在對 DAO 進行單元測試的時候,出現了一些問題,因爲對新環境不太熟悉,折騰了很久才把問題略爲妥善的解決。

程序員喜歡用代碼說話,所以先將測試的相關代碼展示如下:

public class FilterSetDaoTest extends TestCase {

    private FilterSetDao filterSetDao;


    public void testCreateFilterSet() {

       FilterSet filterSet = new FilterSet();

       filterSet.setName("test10");

       filterSet.setCreateTime(new Date());

       Set<Filter> filters = new HashSet<Filter>();

       Filter filter = new Filter();

       filter.setRule("testrule10");

       filter.setType(FilterType.PLAIN);

       filter.setCreateTime(new Date());

       filters.add(filter);

       filterSet.setFilters(filters);

       filterSet.setUpdateTime(new Date());


       filterSetDao.saveOrUpdate(filterSet);


       FilterSet persistedFilterSet = filterSetDao.find(filterSet.getId());


       assertEquals(filterSet, persistedFilterSet);

       assertEquals(1, persistedFilterSet.getFilters().size());

    }

}


public interface FilterSetDao {

    public FilterSet find(int id);

}


public class FilterSetDaoHibernateImpl extends HibernateDaoSupport implements FilterSetDao {

    public FilterSet find(int id) {

       return (FilterSet) getHibernateTemplate().load(FilterSet.class,  id);

    }

}


@Entity

@Table(name = "filterset")

public class FilterSet implements Serializable {

    private Set<Filter> filters;


    @ManyToMany(cascade = CascadeType.ALL)

    public Set<Filter> getFilters() {

       return filters;

    }

}

因爲在 Hibernate 中,Session.load() 方法返回的是實體類的一個代理類的實例,而此時因爲沒有合適的事務處理代碼,相應的 session 已經關閉,所以在執行第一條 assertEquals() 方法時,即報告“LazyInitializationException: could not initialize proxy-the owning Session was closed”異常。可以將 DAO 實現中的 load() 換成 get()。

return (FilterSet) getHibernateTemplate().get(FilterSet.class, id);

因爲 get() 是返回的不是代理類的實例,而直接返回實體類的實例,所以上面的異常將不會出現。但是 FilterSet 中的 filters 屬性爲 Set 類型,而在 Hibernate 3 中,默認對所有 Collection 和 Map 都採用 lazy initialization 的方式,因此在這裏,基於上面所說的原因,又會報告“org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: net.patrickhe.FilterSet.filters, no session or session was closed” 這樣的錯誤。所以,將 load() 換成 get() 其實並不是良好的解決辦法。

按照 Spring Reference 上的說明,如果想在事務支持的環境下進行單元測試,可以讓自己的測試用例類繼承 org.springframework.test.annotation.AbstractAnnotationAwareTransactionalTests,如此即可。AbstractAnnotationAwareTransactionalTests 默認會關閉自動提交的特性,在測試方法執行完畢之後即進行回滾操作,以便清除方纔測試時對數據庫造成的修改變更。如果需要將測試數據提交到數據庫中的話,那也很容易,直接調用 繼承而來的 setComplete() 方法即可。關於 AbstractAnnotationAwareTransactionalTests 的更加詳盡說明可以參考 Spring Reference - 8.3.3 Transaction management 一節。

注:在需要事務支持的環境中進行開發,如果使用 MySQL 作爲持久化介質,一定要採用 InnoDB 之類的支持事務管理的存儲引擎,否則一定會出現各種奇怪的邏輯錯誤。

發佈了105 篇原創文章 · 獲贊 3 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章