<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    京山游俠

    專注技術(shù),拒絕扯淡
    posts - 50, comments - 868, trackbacks - 0, articles - 0
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    距離上一篇SpringSide 3 中的Struts 2已經(jīng)有一段時(shí)間了,中間因?yàn)檠芯苛艘幌翭edora 10,所以就把對(duì)SpringSide 3的學(xué)習(xí)擱置了下來(lái)。以目前的Web開(kāi)發(fā)來(lái)看,主流的模式還是MVC,在SpringSide 3中,控制器使用的是Struts 2,前面我們已經(jīng)探討過(guò)了,接下來(lái)毫無(wú)疑問(wèn)應(yīng)該探討Model層,也就是和數(shù)據(jù)庫(kù)訪問(wèn)有關(guān)的內(nèi)容。

    在SpringSide 3 中,數(shù)據(jù)庫(kù)訪問(wèn)層使用的是Hibernate,Hibernate是一個(gè)很優(yōu)秀的ORM框架,是大家耳熟能詳?shù)臇|西了。關(guān)于Hibernate的內(nèi)容,很多人是寫(xiě)了又寫(xiě),我想我是很難寫(xiě)出新意了。不過(guò)我的思路是這樣的,我從實(shí)際開(kāi)發(fā)的過(guò)程出發(fā),寫(xiě)出在SpringSide 3中使用Hibernate的步驟,在這些步驟中,探討SpringSide 3對(duì)Hibernate的封裝,探討數(shù)據(jù)持久層的單元測(cè)試,探討二級(jí)緩存和性能優(yōu)化。

    我創(chuàng)建一個(gè)虛擬的應(yīng)用場(chǎng)景來(lái)做示范,假設(shè)我們開(kāi)發(fā)的是一個(gè)簡(jiǎn)單的文章發(fā)布系統(tǒng),實(shí)現(xiàn)對(duì)文章簡(jiǎn)單的增刪查改功能。同時(shí)為了演示多個(gè)表之間的關(guān)聯(lián)查詢,假設(shè)每篇文章有多篇評(píng)論。這時(shí),我們需要在數(shù)據(jù)庫(kù)中創(chuàng)建兩個(gè)表,如下:

    create ? table ?articles(
    id?
    int ? primary ? key ?auto_increment,
    subject?
    varchar ( 20 )? not ? null ,
    content?
    text );

    create ? table ?comments(
    id?
    int ? primary ? key ?auto_increment,
    content?
    varchar ( 255 ),
    article_id?
    int ? not ? null ,
    foreign ? key ?(article_id)? references ?articles(id)
    );


    我的開(kāi)發(fā)習(xí)慣是先寫(xiě)數(shù)據(jù)庫(kù)Schema,再寫(xiě)Hibernate的Entity類,再寫(xiě)DAO類,最后在Action里面使用DAO類。這只是我個(gè)人的習(xí)慣,大家都知道,Hibernate有通過(guò)Entity類自動(dòng)生成數(shù)據(jù)庫(kù)Schema的工具,這說(shuō)明很多人習(xí)慣先寫(xiě)Entity類而不關(guān)注數(shù)據(jù)庫(kù)的細(xì)節(jié)。但是我從沒(méi)有用過(guò)這樣的工具,我喜歡了解數(shù)據(jù)庫(kù)的細(xì)枝末節(jié),所以我總是自己寫(xiě)數(shù)據(jù)庫(kù)Schema。

    在MySQL的客戶端直接執(zhí)行上面的SQL語(yǔ)句就可以創(chuàng)建這兩個(gè)表了。這里需要額外提一下的是我使用的數(shù)據(jù)庫(kù)是MySQL,而不是默認(rèn)的Derby,要把SpringSide創(chuàng)建的項(xiàng)目的數(shù)據(jù)庫(kù)更換為MySQL并不難,只需要如下幾個(gè)步驟:
    1、更改數(shù)據(jù)庫(kù)地址、用戶名、密碼(MySQL需要在數(shù)據(jù)庫(kù)地址中指定UTF-8編碼);
    2、更改數(shù)據(jù)庫(kù)驅(qū)動(dòng)、Dialect,同時(shí),需要自己下載MySQL的JDBC驅(qū)動(dòng)放到項(xiàng)目中;
    3、SQL文件,因?yàn)镈erby的語(yǔ)法和MySQL的有點(diǎn)不一樣,比如MySQL中就應(yīng)該使用AUTO_INCREMENT,而不是GENERATED ALWAYS as IDENTITY,并且Drop數(shù)據(jù)表的時(shí)候,MySQL可以加上IF EXISTS選項(xiàng)。

    下一步,編寫(xiě)Entity類:

    package ?cn.puretext.entity.web;

    import ?java.util.LinkedHashSet;
    import ?java.util.Set;

    import ?javax.persistence.CascadeType;
    import ?javax.persistence.Entity;
    import ?javax.persistence.JoinColumn;
    import ?javax.persistence.OneToMany;
    import ?javax.persistence.OrderBy;
    import ?javax.persistence.Table;

    import ?org.hibernate.annotations.Cache;
    import ?org.hibernate.annotations.CacheConcurrencyStrategy;
    import ?org.hibernate.annotations.Fetch;
    import ?org.hibernate.annotations.FetchMode;

    import ?cn.puretext.entity.IdEntity;

    @Entity
    // ?表名與類名不相同時(shí)重新定義表名.
    @Table(name? = ? " articles " )
    // ?默認(rèn)的緩存策略.
    @Cache(usage? = ?CacheConcurrencyStrategy.READ_WRITE)
    public ? class ?Article? extends ?IdEntity?{
    ????
    private ?String?subject;
    ????
    private ?String?content;
    ????
    private ?Set < Comment > ?comments? = ? new ?LinkedHashSet < Comment > ();
    ????
    ????
    public ?String?getSubject()?{
    ????????
    return ?subject;
    ????}

    ????
    public ? void ?setSubject(String?subject)?{
    ????????
    this .subject? = ?subject;
    ????}

    ????
    public ?String?getContent()?{
    ????????
    return ?content;
    ????}

    ????
    public ? void ?setContent(String?content)?{
    ????????
    this .content? = ?content;
    ????}

    ????@OneToMany(cascade?
    = ?{?CascadeType.ALL?})
    ????@JoinColumn(name?
    = ? " article_id " )
    ????
    // ?Fecth策略定義
    ????@Fetch(FetchMode.SUBSELECT)
    ????
    // ?集合按id排序.
    ????@OrderBy( " id " )
    ????
    // ?集合中對(duì)象id的緩存.
    ????@Cache(usage? = ?CacheConcurrencyStrategy.READ_WRITE)
    ????
    public ?Set < Comment > ?getComments()?{
    ????????
    return ?comments;
    ????}

    ????
    public ? void ?setComments(Set < Comment > ?comments)?{
    ????????
    this .comments? = ?comments;
    ????}
    }

    package ?cn.puretext.entity.web;

    import ?javax.persistence.Entity;
    import ?javax.persistence.Table;

    import ?org.hibernate.annotations.Cache;
    import ?org.hibernate.annotations.CacheConcurrencyStrategy;

    import ?cn.puretext.entity.IdEntity;

    @Entity
    // ?表名與類名不相同時(shí)重新定義表名.
    @Table(name? = ? " comments " )
    // ?默認(rèn)的緩存策略.
    @Cache(usage? = ?CacheConcurrencyStrategy.READ_WRITE)
    public ? class ?Comment? extends ?IdEntity?{
    ????
    private ?String?content;
    ????
    ????
    public ?String?getContent()?{
    ????????
    return ?content;
    ????}

    ????
    public ? void ?setContent(String?content)?{
    ????????
    this .content? = ?content;
    ????}
    }


    通過(guò)上面的代碼,大家可以注意到如下的信息:
    1、上面的Entity類都沒(méi)有了id,為什么呢?因?yàn)榘滓掳阉槌鰜?lái)了,做了一個(gè)IdEntity基類讓我們繼承,所以,以后只要是數(shù)據(jù)庫(kù)中含有id的表,編寫(xiě)Entity類的時(shí)候都可以從IdEntity繼承。
    2、Entity中使用的Annotation就不用多說(shuō)了,JPA Annotation已經(jīng)不是什么新東西,在上面的Entity中,我演示了一下@OneToMany,而白衣在項(xiàng)目里面大量演示了@ManyToMany,我以前寫(xiě)的一篇博文《打通數(shù)據(jù)持久層的任督二脈》中討論了@OneToOne和@ManyToOne,這回算是補(bǔ)齊了。
    3、上面的Entity中涉及到了抓取策略和緩存策略,使用注解設(shè)置起來(lái)也很簡(jiǎn)單。

    下一步,編寫(xiě)DAO類:

    package ?cn.puretext.dao;

    import ?org.springframework.stereotype.Repository;
    import ?org.springside.modules.orm.hibernate.HibernateDao;

    import ?cn.puretext.entity.web.Article;

    @Repository
    public ? class ?ArticleDao? extends ?HibernateDao < Article,?Long > ?{

    }


    可以看到該類非常之簡(jiǎn)單,原因嘛,自然是因?yàn)镾pringSide的基類做了大量的工作。這這里,該DAO類的繼承層次是這樣的:


    從截圖中可以看出,SpringSide提供了HibernateDao和SimpleHibernateDao兩個(gè)基類,在這兩個(gè)基類中,封裝了CRUD操作,而HibernateDao類更提供了分頁(yè)查詢函數(shù)。這個(gè)封裝的思路和前一代的SpringSide是一樣的,但是有幾個(gè)區(qū)別:
    1、可以不創(chuàng)建自己的DAO類,什么意思呢?舉例說(shuō)明,上面為Article創(chuàng)建了ArticleDao類,那么在Action中可以這樣用:
    ArticleDao articleDao = new ArticleDao();(這只是一個(gè)示范,事實(shí)上不需要顯示創(chuàng)建,因?yàn)樵赟pringSide 3中,靠的都是注入)
    但是也可以不要ArticleDao,而直接這樣用:
    HibernateDao<Article,Long> articleDao = new HibernateDao<Article,Long>();
    這樣做有什么好處呢?當(dāng)然是可以有效減少Dao層類的數(shù)量,如果有的Dao類使用得比較少,那么就沒(méi)有必要專門為它創(chuàng)造一個(gè)Dao類了。

    寫(xiě)到這里,我又忍不住要評(píng)論一下江南白衣在項(xiàng)目架構(gòu)方面的一些習(xí)慣了,他的層次太多,這應(yīng)該是他在實(shí)際項(xiàng)目中錘煉出來(lái)的經(jīng)驗(yàn),但是和教科書(shū)上的就不大一樣了,教科書(shū)上的三層就是三層,而白衣可以把它擴(kuò)展到4層甚至5層,白衣的層次可以總結(jié)成Entity->DAO->Service(Manager)->Action->View,其中Service這一層命名還不統(tǒng)一,包名是Service,類名中用的是Manager。我覺(jué)得這個(gè)大家可以探討探討,也許白衣認(rèn)為DAO里面不應(yīng)該包含業(yè)務(wù)邏輯,只應(yīng)該包含CRUD和分頁(yè)操作,而Action里面也不應(yīng)該包含業(yè)務(wù)邏輯,所以就單獨(dú)抽出一層來(lái)了吧,所以這一層應(yīng)該稱為Bussiness層比較合適,而白衣也認(rèn)為,有時(shí)候DAO層和Bussiness層可以合并在一起。另外,我認(rèn)為白衣在項(xiàng)目中搞的package也太多了一點(diǎn),在IDE里面不方便,所以我的實(shí)際項(xiàng)目中,我會(huì)對(duì)包重新進(jìn)行整理。

    2、在DAO類中可以使用Hibernate的原生API。我們來(lái)總結(jié)一下在Hibernate中通常采用的查詢方式:一是使用HQL語(yǔ)言,它的過(guò)程基本上是先獲取Session,然后創(chuàng)建Query對(duì)象,最后通過(guò)Query對(duì)象執(zhí)行HQL語(yǔ)句;二是使用條件查詢,它的過(guò)程基本上是先獲取Session,然后創(chuàng)建Creteria對(duì)象,然后執(zhí)行Creteria對(duì)象的list()方法。而在Dao類中,我們可以很簡(jiǎn)單的通過(guò)sessionFactory.getCurrentSession()來(lái)獲得Session對(duì)象,進(jìn)而很方便的使用到HQL或者Creteria。

    3、在SpringSide 2中,我們可以對(duì)數(shù)據(jù)表中的數(shù)據(jù)不做物理刪除,該特性得益于白衣提供的@Undeletable注解和HibernateEntityExtendDao類,在SpringSide 3中,該特性沒(méi)有了。現(xiàn)在回想起來(lái),我覺(jué)得該特性也沒(méi)有什么存在的必要。

    后面再繼續(xù)探討分頁(yè)查詢和性能優(yōu)化。現(xiàn)在的任務(wù)是趕緊確認(rèn)一下這Entity層和Dao層能否正常工作,完成該任務(wù)的最佳途徑,當(dāng)然是單元測(cè)試了。

    在SpringSide 3中,編寫(xiě)單元測(cè)試非常方便,只需要繼承白衣提供的SpringContextTestCase類或者SpringTxTestCase類即可,事實(shí)上,只有繼承SpringTxTestCase類才能正常工作,因?yàn)槲覀兊捻?xiàng)目的配置無(wú)法讓我們工作在非事務(wù)的環(huán)境下。繼承這個(gè)類有什么用處呢?它的用處就是可以讀取項(xiàng)目中的applicationContext.xml文件,自動(dòng)建立數(shù)據(jù)源、Dao對(duì)象,并把Dao對(duì)象注入到測(cè)試用例中,所以,測(cè)試類的代碼非常簡(jiǎn)潔,如下:

    package ?cn.puretext.unit.service;

    import ?java.util.List;

    import ?org.junit.Test;
    import ?org.springframework.beans.factory.annotation.Autowired;
    import ?org.springside.modules.orm.Page;
    import ?org.springside.modules.test.junit4.SpringTxTestCase;

    import ?cn.puretext.dao.ArticleDao;
    import ?cn.puretext.entity.web.Article;

    public ? class ?DaoTest? extends ?SpringTxTestCase?{
    ????@Autowired
    ????
    private ?ArticleDao?articleDao;

    ????
    public ?ArticleDao?getArticleDao()?{
    ????????
    return ?articleDao;
    ????}

    ????
    public ? void ?setArticleDao(ArticleDao?articleDao)?{
    ????????
    this .articleDao? = ?articleDao;
    ????}

    ????@Test
    ????
    public ? void ?addArticle()?{
    ????????Article?article?
    = ? new ?Article();
    ????????article.setSubject(
    " article?test " );
    ????????article.setContent(
    " article?test " );
    ????????articleDao.save(article);
    ????}
    }

    ?

    因?yàn)樵搯卧獪y(cè)試工作在事務(wù)環(huán)境下,所以運(yùn)行單元測(cè)試不會(huì)改變數(shù)據(jù)庫(kù)中的數(shù)據(jù)。白衣提供的這兩個(gè)類事實(shí)上只是在Spring 2.5的測(cè)試框架上做了一點(diǎn)點(diǎn)改進(jìn)。關(guān)于Spring 2.5測(cè)試框架的詳細(xì)介紹,大家可以到“IBM DeveloperWorks 中國(guó)”上去看這一篇文章:
    http://www.ibm.com/developerworks/cn/java/j-lo-spring25-test/

    但是白衣自己的做法卻完全不同,在白衣寫(xiě)的單元測(cè)試中,他偏偏用的是EasyMock,關(guān)于EasyMock的使用方法,大家可以到“IMB DeveloperWorks 中國(guó)”上去看這一篇文章:
    http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/

    再讓大家看一下截圖,我特地把測(cè)試類的代碼、JUnit著名的綠條和Hibernate輸出的SQL語(yǔ)句放到了一起,如下:

    代碼比較簡(jiǎn)單,只是為了證明上面寫(xiě)的Entity和Dao能夠正常運(yùn)行。在下面的內(nèi)容里,隨著我們的探討,測(cè)試代碼的內(nèi)容會(huì)逐漸增加。

    上文的內(nèi)容演示了SpringSide 3中Hibernate的使用過(guò)程和單元測(cè)試,也提到了SpringSide 3提供的CRUD封裝,這些都很簡(jiǎn)單。在SpringSide 3對(duì)Hibernate的封裝中,還有一個(gè)重點(diǎn),那就是分頁(yè)查詢。

    分頁(yè)查詢有HibernateDao類實(shí)現(xiàn),要配合Page類來(lái)使用。Page類一般用來(lái)設(shè)置查詢條件,并返回查詢結(jié)果,舉例說(shuō)明,如果對(duì)Articles表中的數(shù)據(jù)進(jìn)行分頁(yè)顯示,每一頁(yè)10條記錄,那么查詢第二頁(yè)應(yīng)該怎么辦呢?代碼如下:

    @Test
    ?
    public ? void ?pageQuery()?{
    ??Page
    < Article > ?page? = ? new ?Page < Article > ();
    ??page.setPageSize(
    10 );
    ??page.setPageNo(
    2 );
    ??page?
    = ?articleDao.getAll(page);
    ??List
    < Article > ?articles? = ?page.getResult();
    ?}


    以上代碼在單元測(cè)試中進(jìn)行,這個(gè)過(guò)程很容易理解,就是先創(chuàng)建一個(gè)Page對(duì)象,然后設(shè)置該頁(yè)的大小和序號(hào),就可以直接查找該頁(yè)的數(shù)據(jù)了。同時(shí),Page類還有很多輔助方法,如獲取總的記錄條數(shù),獲取頁(yè)的總數(shù),獲取是否有下一頁(yè)等等。

    Page只是一個(gè)輔助類,真正的查詢操作是在HibernateDao類中完成的,具體代碼如下:

    /**
    ??*?按Criteria分頁(yè)查詢.
    ??*?
    ??*?
    @param ?page?分頁(yè)參數(shù).
    ??*?
    @param ?criterions?數(shù)量可變的Criterion.
    ??*?
    ??*?
    @return ?分頁(yè)查詢結(jié)果.附帶結(jié)果列表及所有查詢時(shí)的參數(shù).
    ??
    */
    ?@SuppressWarnings(
    " unchecked " )
    ?
    public ?Page < T > ?find( final ?Page < T > ?page,? final ?Criterion?criterions)?{
    ??Assert.notNull(page,?
    " page不能為空 " );

    ??Criteria?c?
    = ?createCriteria(criterions);

    ??
    if ?(page.isAutoCount())?{
    ???
    int ?totalCount? = ?countCriteriaResult(c);
    ???page.setTotalCount(totalCount);
    ??}

    ??setPageParameter(c,?page);
    ??List?result?
    = ?c.list();
    ??page.setResult(result);
    ??
    return ?page;
    ?}


    可以看到,白衣的實(shí)現(xiàn)用的是Hibernate中的條件查詢,從上面的代碼可以看出,該過(guò)程是先創(chuàng)建Criteria對(duì)象,然后查詢記錄的總數(shù),并將記錄的總數(shù)填入到Page對(duì)象中,然后再調(diào)用setPageParameter方法將Page對(duì)象中的信息填入到Criteria對(duì)象中,最后調(diào)用Criteria對(duì)象的list()方法來(lái)獲取結(jié)果。

    下面跟蹤到setPageParameter方法中,其代碼如下:

    ? protected ?Criteria?setPageParameter( final ?Criteria?c,? final ?Page < T > ?page)?{
    ??
    // hibernate的firstResult的序號(hào)從0開(kāi)始
    ??c.setFirstResult(page.getFirst()? - ? 1 );
    ??c.setMaxResults(page.getPageSize());
    ??
    /* 以下代碼省略 */
    }


    可以看到,該方法中只是簡(jiǎn)單地調(diào)用了Criteria對(duì)象的setFirstResult和setMaxResults方法,這都是Hibernate的原生API,沒(méi)有什么需要特殊說(shuō)明的。我比較關(guān)心的是分頁(yè)查詢所生成的SQL語(yǔ)句及其正確性。

    講到這里,我得提一下我的技術(shù)背景:在使用MySQL之前,我有很長(zhǎng)一段時(shí)間使用的是MS SQL Server 2000。為什么要提這個(gè)問(wèn)題呢?那是因?yàn)檎驹赟QL Server 2000的角度,處理分頁(yè)問(wèn)題是比較困難的。在SQL Server 2000中,如果要獲取指定條數(shù)的記錄,只能使用top關(guān)鍵字,也就是說(shuō)要獲取10條數(shù)據(jù),就應(yīng)該使用select * top 10 from articles,那么怎么定位到第二頁(yè)呢?就必須知道第二頁(yè)的第一條數(shù)據(jù)的ID是多少,然后用這樣的語(yǔ)句select * top 10 from articles where id >= ?,那怎么知道第二頁(yè)的第一條記錄的ID是多少呢?免不了又要多一次查詢?nèi)鐂elect id top 20 from articles order by id desc。

    所以在SQL Server 2000中,要實(shí)現(xiàn)分頁(yè)查詢比較困難,不是思考起來(lái)困難,而是提高效率困難,必須得避免多次查詢。解決的辦法當(dāng)然有,要么使用存儲(chǔ)過(guò)程,要么在前面的select語(yǔ)句中加入子查詢。但是不管采取哪種辦法,SQL語(yǔ)句寫(xiě)起來(lái)都不簡(jiǎn)單。

    在MySQL中,該問(wèn)題就簡(jiǎn)單多了,MySQL不提供top,但提供limit,更重要的是limit接受兩個(gè)參數(shù),而不是像top只接受一個(gè)參數(shù)。limit后面的參數(shù)可以是{[offset,] row_count | row_count OFFSET offset},其中的offset就代表了第2頁(yè)的第一條數(shù)據(jù)所在的位置,大家請(qǐng)注意,這里說(shuō)的是位置,而不是SQL Server 2000中的ID,這兩者是有區(qū)別的,因?yàn)镮D可能不連續(xù),而位置肯定是連續(xù)的,所以位置是可以通過(guò)簡(jiǎn)單的數(shù)學(xué)計(jì)算來(lái)獲得的,這樣,MySQL就只需要生成一個(gè)簡(jiǎn)單的SQL語(yǔ)句select * from articles limit 10,10。

    下面是Hibernate自己生成的SQL語(yǔ)句:
    ??? select
    ??????? this_.id as id4_0_,
    ??????? this_.content as content4_0_,
    ??????? this_.subject as subject4_0_
    ??? from
    ??????? articles this_ limit ?,
    ??????? ?

    為了和SQL Server 2000對(duì)比,我把配置文件中的Dialect改為org.hibernate.dialect.SQLServerDialect,得到的SQL語(yǔ)句如下:
    ??? select
    ??????? top 20 this_.id as id4_0_,
    ??????? this_.content as content4_0_,
    ??????? this_.subject as subject4_0_
    ??? from
    ??????? articles this_
    2009-07-09 22:22:53,950 [main] WARN? [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 1064, SQLState: 42000
    2009-07-09 22:22:53,969 [main] ERROR [org.hibernate.util.JDBCExceptionReporter] - You have an error in your SQL syntax;

    因?yàn)槲覜](méi)有把數(shù)據(jù)庫(kù)遷移到SQL Server,所以該語(yǔ)句一運(yùn)行就出錯(cuò)了,不過(guò)從該語(yǔ)句中的top 20也可以看出,要么該語(yǔ)句的作用是為了得到第二頁(yè)的第一條記錄的ID,然后后面再跟一條SQL語(yǔ)句,只不過(guò)因?yàn)槌霈F(xiàn)錯(cuò)誤,所以后面的語(yǔ)句沒(méi)有顯示出來(lái),要么是直接取出20條記錄,并拋棄10條,只留下第二頁(yè)的數(shù)據(jù)。總之,和我之前預(yù)想的一樣,性能得不到保證。

    通過(guò)搜索引擎我還查出,Oracle也不支持limit語(yǔ)句,所以說(shuō),我們不能完全相信Hibernate,必要的時(shí)候,還是得靠自己寫(xiě)存儲(chǔ)過(guò)程。

    Fetch策略也是影響性能的一個(gè)方面,F(xiàn)etch策略主要是針對(duì)Entity中的集合數(shù)據(jù),正如白衣所說(shuō),很多人多只知道使用默認(rèn)的Lazy策略,我就是這很多人中的一個(gè),以前我還因?yàn)長(zhǎng)azy策略出現(xiàn)過(guò)問(wèn)題,什么問(wèn)題呢,那就是我先獲取一個(gè)Entity的數(shù)據(jù),然后把在Entity保存到HttpSession中,然后在使用該對(duì)象中的集合數(shù)據(jù)時(shí),就報(bào)錯(cuò)了,為什么呢,因?yàn)檫@個(gè)時(shí)候Hibernate的Session早就關(guān)閉了,所以出錯(cuò)。

    關(guān)于Fetch策略的選擇,SpringSide的文檔和Hibernate的文檔上面都寫(xiě)得很清楚,我就不羅嗦了,至于在代碼中怎么設(shè)置Fetch策略,代碼的注釋很清楚,一看就會(huì)。

    最后談一談二級(jí)緩存,Session中的緩存是一級(jí)緩存,ehcache提供二級(jí)緩存,關(guān)于二級(jí)緩存的配置,主要涉及到兩個(gè)地方,一個(gè)是xml配置文件,另一個(gè)是Entity類中的注解,xml配置文件中配置的是ehcache的屬性,而Entity中的注解設(shè)置了隔離級(jí)別,具體內(nèi)容請(qǐng)參閱SpringSide 3 的文檔。


    評(píng)論

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層  回復(fù)  更多評(píng)論   

    2009-07-10 13:36 by h521999
    通俗易懂,圖文并茂,非常不錯(cuò)

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層[未登錄](méi)  回復(fù)  更多評(píng)論   

    2009-07-10 22:50 by 過(guò)客
    使用Oracle,hibernate會(huì)通過(guò)rownum來(lái)分頁(yè)的。

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層  回復(fù)  更多評(píng)論   

    2009-07-11 12:13 by 虎嘯龍吟
    很不錯(cuò)。受教了。
    希望在springSide上有更多的文章。

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層  回復(fù)  更多評(píng)論   

    2009-07-12 17:03 by dd2086
    好文吶 關(guān)注你的文章

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層  回復(fù)  更多評(píng)論   

    2010-04-09 16:57 by 游客
    ,,太經(jīng)典了,,,太感謝了,,

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層  回復(fù)  更多評(píng)論   

    2010-06-18 11:25 by lacewigs
    Great site

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層  回復(fù)  更多評(píng)論   

    2012-01-29 13:44 by qinjiannet
    經(jīng)典,受教了。

    # re: SpringSide 3 中的數(shù)據(jù)庫(kù)訪問(wèn)層[未登錄](méi)  回復(fù)  更多評(píng)論   

    2012-05-14 16:08 by 過(guò)客
    感謝大俠
    主站蜘蛛池模板: 免费观看四虎精品成人| 亚洲Aⅴ无码专区在线观看q| 成年丰满熟妇午夜免费视频| 午夜两性色视频免费网站| 亚洲精品第一国产综合境外资源| 狠狠亚洲婷婷综合色香五月排名| 久久亚洲精品成人AV| 亚洲国产成人久久一区二区三区 | 亚洲成年人电影网站| 国产精品亚洲а∨天堂2021| 日韩精品无码免费专区午夜不卡| 99久久久国产精品免费无卡顿| 日韩免费观看一级毛片看看| 亚洲s色大片在线观看| 亚洲精品国产精品| 永久免费看bbb| 午夜不卡AV免费| 在线a毛片免费视频观看| 久久久久亚洲精品成人网小说| 日韩人妻一区二区三区免费| 四虎影在线永久免费观看| 亚洲日韩乱码中文无码蜜桃 | 波多野结衣久久高清免费| 男人的天堂av亚洲一区2区| 24小时日本韩国高清免费| 亚洲男人第一无码aⅴ网站| 丁香花在线观看免费观看图片| 永久免费毛片手机版在线看| jizz在线免费播放| 免费v片在线观看无遮挡| 99久久国产亚洲综合精品| 久久国产乱子伦精品免费不卡| 亚洲第一黄色网址| 亚洲国产精品网站在线播放| 久久夜色精品国产亚洲av| 免费看黄福利app导航看一下黄色录像| 最新国产AV无码专区亚洲| 久久成人国产精品免费软件| 亚洲黄色片在线观看| 99久久99久久精品免费观看 | 3344永久在线观看视频免费首页|