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

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

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

    Hibernate進(jìn)行時(shí)

    有關(guān)Hibenrate及其相關(guān)工具的主頁
    隨筆 - 0, 文章 - 16, 評(píng)論 - 29, 引用 - 0
    數(shù)據(jù)加載中……

    HibernateTemplate中HibernateCallback的事務(wù)

    目的:使用HibernateTemplate執(zhí)行execute(new HibernateCallback())方法,從HibernateCallback中得到session,在此session中做多個(gè)操作,并希望這些操作位于同一個(gè)事務(wù)中。
          如果你這樣寫(1):
          
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().execute(
    new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    
    // 保存stu1
                    Student stu1 = new Student();
                    stu1.setName(
    "aaaa");// 在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    session.flush();//實(shí)際上,如果不是程序員"手癢"來調(diào)用這個(gè)flush(),HibernateTemplate中session的事務(wù)處理還是很方便的

                    Student stu2 
    = new Student();
                    session.save(stu2);
    // 沒有設(shè)置name字段,預(yù)期會(huì)報(bào)出例外
                    session.flush();
                    
    return null;
                }

            }
    );

        }
          你期望spring在執(zhí)行完execute回調(diào)后,在關(guān)閉session的時(shí)候提交事務(wù),想法是很好的,但spring并不會(huì)這么做.讓我們來看看在Hibernate的源代碼中,session.beginTransation()做了什么事。看如下代碼(2):
    public Transaction beginTransaction() throws HibernateException {
            errorIfClosed();
            
    if ( rootSession != null ) {
                
    // todo : should seriously consider not allowing a txn to begin from a child session
                
    //      can always route the request to the root session
                log.warn( "Transaction started on non-root session" );
            }

            Transaction result 
    = getTransaction();
            result.begin();
            
    return result;
        }
    這個(gè)方法中的result是一個(gè)org.hibernate.transaction.JDBCTransaction實(shí)例,而方法中的getTransaction()方法源代碼為(3):
    public Transaction getTransaction() throws HibernateException {
            
    if (hibernateTransaction==null{
                log.error(owner.getFactory().getSettings()
                        .getTransactionFactory().getClass());
                hibernateTransaction 
    = owner.getFactory().getSettings()
                        .getTransactionFactory()
                        .createTransaction( 
    this, owner );
            }

            
    return hibernateTransaction;
        }
    再次追蹤,owner.getFactory().getSettings() .getTransactionFactory()的createTransaction()方法源代碼如下(4):
    public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
        
    throws HibernateException {
            
    return new JDBCTransaction( jdbcContext, transactionContext );
        }
    它返回了一個(gè)JDBCTransaction,沒什么特別的。
    在代碼2中,執(zhí)行了result.begin(),其實(shí)也就是JDBCTransaction實(shí)例的begin()方法,來看看(5):

    public void begin() throws HibernateException {
            
    if (begun) {
                
    return;
            }

            
    if (commitFailed) {
                
    throw new TransactionException("cannot re-start transaction after failed commit");
            }

            log.debug(
    "begin");
            
    try {
                toggleAutoCommit 
    = jdbcContext.connection().getAutoCommit();
                
    if (log.isDebugEnabled()) {
                    log.debug(
    "current autocommit status: " + toggleAutoCommit);
                }

                
    if (toggleAutoCommit) {
                    log.debug(
    "disabling autocommit");
                    jdbcContext.connection().setAutoCommit(
    false);//把自動(dòng)提交設(shè)為了false
                }

            }
     catch (SQLException e) {
                log.error(
    "JDBC begin failed", e);
                
    throw new TransactionException("JDBC begin failed: ", e);
            }

            callback 
    = jdbcContext.registerCallbackIfNecessary();
            begun 
    = true;
            committed 
    = false;
            rolledBack 
    = false;

            
    if (timeout > 0{
                jdbcContext.getConnectionManager().getBatcher().setTransactionTimeout(timeout);
            }


            jdbcContext.afterTransactionBegin(
    this);
        }

    在直接使用Hibernate時(shí),要在事務(wù)結(jié)束的時(shí)候,寫上一句:tx.commit(),這個(gè)commit()的源碼為:
    public void commit() throws HibernateException {
            
    if (!begun) {
                
    throw new TransactionException("Transaction not successfully started");
            }


            log.debug(
    "commit");

            
    if (!transactionContext.isFlushModeNever() && callback) {
                transactionContext.managedFlush(); 
    // if an exception occurs during
                
    // flush, user must call
                
    // rollback()
            }


            notifyLocalSynchsBeforeTransactionCompletion();
            
    if (callback) {
                jdbcContext.beforeTransactionCompletion(
    this);
            }


            
    try {
                commitAndResetAutoCommit();//重點(diǎn)代碼,它的作用是提交事務(wù),并把connection的autocommit屬性恢復(fù)為true
                log.debug(
    "committed JDBC Connection");
                committed 
    = true;
                
    if (callback) {
                    jdbcContext.afterTransactionCompletion(
    truethis);
                }

                notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_COMMITTED);
            }
     catch (SQLException e) {
                log.error(
    "JDBC commit failed", e);
                commitFailed 
    = true;
                
    if (callback) {
                    jdbcContext.afterTransactionCompletion(
    falsethis);
                }

                notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
                
    throw new TransactionException("JDBC commit failed", e);
            }
     finally {
                closeIfRequired();
            }

        }

    上面代碼中,commitAndResetAutoCommit()方法的源碼如下:
    private void commitAndResetAutoCommit() throws SQLException {
            
    try {
                jdbcContext.connection().commit();//這段不用說也能理解了
            }
     finally {
                toggleAutoCommit();//這段的作用是恢復(fù)connection的autocommit屬性為true
            }

        }

    上述代碼的toggleAutoCommit()源代碼如下:
        private void toggleAutoCommit() {
            
    try {
                
    if (toggleAutoCommit) {
                    log.debug(
    "re-enabling autocommit");
                    jdbcContext.connection().setAutoCommit(
    true);//這行代碼的意義很明白了吧
                }

            }
     catch (Exception sqle) {
                log.error(
    "Could not toggle autocommit", sqle);
            }

        }
          因此,如果你是直接使用hibernate,并手動(dòng)管理它的session,并手動(dòng)開啟事務(wù)關(guān)閉事務(wù)的話,完全可以保證你的事務(wù)(好像完全是廢話).
          但是,如果你用的是HibernateTemplate,如同源代碼1一樣,則不要指望spring在關(guān)閉session的時(shí)候?yàn)槟闾峤皇聞?wù)(罪魁禍?zhǔn)拙褪窃诖a1中調(diào)用了session.flush())。因?yàn)樵谑褂么a1時(shí),spring中得到session的方式如下:
    Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) : sessionFactory
                    .openSession());
    簡(jiǎn)單地說它就是得到了一個(gè)session,而沒有對(duì)connection的autocommit()作任何操作,spring管理范圍內(nèi)的session所持有的connection是autocommit=true的,spring借助這個(gè)屬性,在它關(guān)閉session時(shí),提交數(shù)據(jù)庫事務(wù)。,因此如果你在源代碼1中加上一句話:
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().execute(
    new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    log.info(session.connection().getAutoCommit());
    //打印一下事務(wù)提交方式
                    
    // 保存stu1
                    Student stu1 = new Student();
                    stu1.setName(
    "aaaa");// 在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    session.flush();

                    Student stu2 
    = new Student();
                    session.save(stu2);
    // 沒有設(shè)置name字段,預(yù)期會(huì)報(bào)出例外
                    session.flush();
                    
    return null;
                }

            }
    );

        }
         運(yùn)行后,它打出的結(jié)果是true,也就是說,雖然保存stu2時(shí)會(huì)報(bào)出例外,但如果commit屬性為true,則每一個(gè)到達(dá)數(shù)據(jù)庫的sql語句會(huì)立即被提交。換句話說,在調(diào)用完session.save(stu1)后,調(diào)用session.flush(),會(huì)發(fā)送sql語句到數(shù)據(jù)庫,再根據(jù)commit屬性為true,則保存stu1的操作已經(jīng)被持久到數(shù)據(jù)庫了,盡管后面的一條insert語句出了問題。
         因此,如果你想在HibernateCallback中使用session的事務(wù),需要如下寫:
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().execute(
    new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    session.connection().setAutoCommit(
    false);
                    
    //保存stu1
                    Student stu1=new Student();
                    stu1.setName(
    "aaaa");//在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    session.flush();
                    
                    Student stu2 
    = new Student();
                    session.save(stu2);
    //沒有設(shè)置name字段,預(yù)期會(huì)報(bào)出例外
                       session.flush();
                    session.connection().commit();
                    
    //至于session的關(guān)閉就不用我們操心了
                    return null;
                }

            }
    );

        }
    運(yùn)行上述代碼,沒問題了。至此,可能有些讀者早就對(duì)代碼1不滿意了:為什么每次save()以后要調(diào)用flush()?這是有原因的。下面我們來看看把session.flush()去掉后會(huì)出什么問題。改掉后的代碼如下:
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().execute(
    new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    session.connection().setAutoCommit(
    false);
                    
    // 保存stu1
                    Student stu1 = new Student();
                    stu1.setName(
    "aaaa");// 在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    
    // session.flush();

                    Student stu2 
    = new Student();
                    session.save(stu2);
    // 沒有設(shè)置name字段,預(yù)期會(huì)報(bào)出例外
                    
    // session.flush();
                    session.connection().commit();
                    
    return null;
                }

            }
    );

        }
    運(yùn)行上述代碼,后臺(tái)報(bào)數(shù)據(jù)庫的not null錯(cuò)誤,這個(gè)是合理的,打開數(shù)據(jù)庫,沒有發(fā)現(xiàn)新增記錄,這個(gè)也是合理的。你可能會(huì)說:由于事務(wù)失敗,數(shù)據(jù)庫當(dāng)然不可能會(huì)有任何新增記錄。好吧,我們?cè)侔汛a改一下,去除not null的錯(cuò)誤,以確保它能正常運(yùn)行。代碼如下:
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().execute(
    new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    session.connection().setAutoCommit(
    false);
                    
    // 保存stu1
                    Student stu1 = new Student();
                    stu1.setName(
    "aaaa");// 在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    
    // session.flush();

                    Student stu2 
    = new Student();
                    stu2.setName(
    "asdfasdf");//好了,這個(gè)字段設(shè)過值,不會(huì)再報(bào)not null錯(cuò)誤了
                    session.save(stu2);
                    
    // session.flush();
                    session.connection().commit();
                    
    return null;
                }

            }
    );

        }

    至此再運(yùn)行上述代碼,出現(xiàn)了一個(gè)奇怪的問題:
    雖然控制臺(tái)把insert語句打出來了,但是:數(shù)據(jù)庫沒有出現(xiàn)任何新的記錄。
    究其原因,有二:
    一. session.connection().commit()確實(shí)導(dǎo)致數(shù)據(jù)庫事務(wù)提交了,但是此刻session并沒有向數(shù)據(jù)庫發(fā)送任何語句。
    二.在spring后繼的flushIfNecessary()和closeSessionOrRegisterDeferredClose()方法中,第一個(gè)方法向數(shù)據(jù)庫發(fā)送sql語句,第二個(gè)方法關(guān)閉session,同時(shí)關(guān)閉connection,然后問題在于:connection已經(jīng)在程序中被手動(dòng)設(shè)置為auttocommit=false了,因此在關(guān)閉數(shù)據(jù)庫時(shí),也不會(huì)提交事務(wù)。
    解決這個(gè)問題很容易,在程序中手動(dòng)調(diào)用session.flush()就可以了。如下代碼:
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().execute(
    new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    session.connection().setAutoCommit(
    false);
                    
                    
    //保存stu1
                    Student stu1=new Student();
                    stu1.setName(
    "aaaa");//在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    
                    Student stu2 
    = new Student();
                    session.save(stu2);
    //沒有設(shè)置name字段,預(yù)期會(huì)報(bào)出例外
                    
                    session.flush();//向數(shù)據(jù)庫發(fā)送sql
                    session.connection().commit();
                    
    return null;
                }

            }
    );

        }

    運(yùn)行上述代碼,打開數(shù)據(jù)庫查看,沒有新增任何記錄。在代碼中新加一行stu2.setName("aaa");再次運(yùn)行代碼,發(fā)現(xiàn)數(shù)據(jù)庫表中多了兩條記錄。事務(wù)操作成功。
    至此,雖然操作成功,但事情還沒有結(jié)束。這是因?yàn)閟pring在調(diào)用doInHibernate()的后繼的步驟中,還要進(jìn)行flushIfNecessary()操作,這個(gè)操作其實(shí)最后調(diào)用的還是session.flush()。因?yàn)樵诔绦蛑幸呀?jīng)手動(dòng)調(diào)用過session.flush(),所以由spring調(diào)用的session.flush()并不會(huì)對(duì)數(shù)據(jù)庫發(fā)送sql(因?yàn)榕K數(shù)據(jù)比對(duì)的原因)。雖然不會(huì)對(duì)結(jié)果有什么影響,但是多調(diào)了一次flush(),還是會(huì)對(duì)性能多少有些影響。能不能控制讓spring不調(diào)用session.flush()呢?可以的,只要加上一句代碼,如下所示:
    public static void main(String ss[]) {
            CtxUtil.getBaseManager().getHibernateTemplate().setFlushMode(
    0);//0也就是FLUSH_NEVER
            CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
                
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    session.connection().setAutoCommit(
    false);
                    
                    
    //保存stu1
                    Student stu1=new Student();
                    stu1.setName(
    "aaaa");//在數(shù)據(jù)庫中,name字段不允許為null
                    session.save(stu1);
                    
                    Student stu2 
    = new Student();
                    stu2.setName(
    "sdf");
                    session.save(stu2);
    //沒有設(shè)置name字段,預(yù)期會(huì)報(bào)出例外
                    
                    session.flush();
                    session.connection().commit();
                    
    return null;
                }

            }
    );

        }
    通過設(shè)置HibernateTemplate的flushMode=FLUSH_NEVER來通知spring不進(jìn)行session.flush()的調(diào)用,則spring的flushIfNecessary()將不進(jìn)行任何操作,它的flushIfNecessary()源代碼如下:
    protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
            
    if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
                logger.debug(
    "Eagerly flushing Hibernate session");
                session.flush();
            }

        }
    至此,代碼1中的main()終于修改完畢。但事實(shí)上,這樣的操作無疑是比較麻煩的,因此如果在spring中想利用session進(jìn)行事務(wù)操作時(shí),最好還是用TransactionTemplate(編程式事務(wù))或是聲明式事務(wù)比較方便一些。
    本例通過這么一個(gè)雖然簡(jiǎn)單但又繞來繞去的例子,主要是說明hibernate事務(wù)的一些內(nèi)在特性,以及HibernateTemplate中如何處理session和事務(wù)的開關(guān),讓讀者對(duì)HibernateTemplate的源代碼處理細(xì)節(jié)有一些了解,希望能給讀者有拋磚引玉的作用。

    posted on 2007-04-25 13:42 caixuetao 閱讀(25392) 評(píng)論(9)  編輯  收藏

    評(píng)論

    # re: HibernateTemplate中HibernateCallback的事務(wù)  回復(fù)  更多評(píng)論   

    好文啊,透徹!
    2007-04-26 15:24 | ztroma

    # re: HibernateTemplate中HibernateCallback的事務(wù)  回復(fù)  更多評(píng)論   

    最近在研究您的大作,學(xué)習(xí)hibernate,希望多多指導(dǎo),不懂之處我會(huì)給您發(fā)郵件,有空能回復(fù)郵件,萬分感謝jb198388@163.com
    2007-04-30 02:56 | sleepingboy

    # re: HibernateTemplate中HibernateCallback的事務(wù)  回復(fù)  更多評(píng)論   

    HibernateTemplate確實(shí)沒有設(shè)置session的事務(wù)屬性,因?yàn)樵趕pring的模型中,事務(wù)本來就不是HibernateTemplate來管理的。HibernateTemplate的作用是保證session能夠正確的打開和關(guān)閉,避免手工管理session帶來的問題,同時(shí)讓session自動(dòng)的參與事務(wù),轉(zhuǎn)換檢查異常。spring中的事務(wù)應(yīng)該使用TransactionTemplate,或者是是更加好的,類似EJB的聲明式事務(wù)。如果配置了聲明式事務(wù),HibernateTemplate就可以保證讓session自動(dòng)參與事務(wù),這一點(diǎn)從HibernateTemplate的源代碼中可以看出來。手工管理的session在多線程操作,聲明式事務(wù)的各種場(chǎng)景下,會(huì)造成代碼的混亂。不推薦手工管理session,也不推薦手工管理事務(wù)。最佳的實(shí)踐,是HibernateTemplate + 聲明式事務(wù)。樓主沒有使用任何spring的事務(wù)機(jī)制,所以HibernateTemplate確實(shí)就是 無事務(wù),這就是autoCommit==true的原因。樓主的例子在實(shí)踐中極為危險(xiǎn)。
    2007-07-07 13:44 | www

    # re: HibernateTemplate中HibernateCallback的事務(wù)  回復(fù)  更多評(píng)論   

    "spring管理范圍內(nèi)的session所持有的connection是autocommit=true的"

    這個(gè)能否提供依據(jù)?謝謝了
    2008-06-29 13:08 | thebye85

    # re: HibernateTemplate中HibernateCallback的事務(wù)  回復(fù)  更多評(píng)論   

    正在看蔡老師的Hibernate開發(fā)及其整合應(yīng)用大全,但是在圖書館借的書都沒源代碼,請(qǐng)問蔡老師可以給我一份嗎?我的郵箱是:hank31713@163.com,謝謝了
    2008-07-24 22:21 | 易海建

    # re: HibernateTemplate中HibernateCallback的事務(wù)[未登錄]  回復(fù)  更多評(píng)論   

    真是高人啊,解決了一個(gè)我的問題關(guān)于在回調(diào)函數(shù)中,session是否自動(dòng)關(guān)閉
    2009-09-23 17:36 | 菜鳥

    # re: HibernateTemplate中HibernateCallback的事務(wù)[未登錄]  回復(fù)  更多評(píng)論   

    public static void main(String ss[]) {
    CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
    session.connection().setAutoCommit(false);
    // 保存stu1
    Student stu1 = new Student();
    stu1.setName("aaaa");// 在數(shù)據(jù)庫中,name字段不允許為null
    session.save(stu1);
    // session.flush();

    Student stu2 = new Student();
    stu2.setName("asdfasdf");//好了,這個(gè)字段設(shè)過值,不會(huì)再報(bào)not null錯(cuò)誤了
    session.save(stu2);
    // session.flush();
    session.connection().commit();
    return null;
    }
    });

    }


    session.connection().setAutoCommit(false); 有這一句當(dāng)然不能提交,不要這一句呢?
    2011-09-11 23:48 | 過客

    # re: HibernateTemplate中HibernateCallback的事務(wù)[未登錄]  回復(fù)  更多評(píng)論   

    怎樣獲得session?
    2014-08-18 00:23 | aa

    # re: HibernateTemplate中HibernateCallback的事務(wù)  回復(fù)  更多評(píng)論   

    不錯(cuò) 看了三個(gè)禮拜,終于看懂了!~~深度解析
    2015-11-11 16:01 | 和大象掰腕子

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产精品国产自线拍免费软件| 久久精品国产亚洲AV电影网| MM131亚洲国产美女久久 | 中文字幕无码免费久久| 一级毛片a免费播放王色| 成人a毛片免费视频观看| 国产99视频精品免费视频76| 中文无码日韩欧免费视频| 在线观看免费视频一区| 久久精品人成免费| 久久精品国产免费观看三人同眠| 亚洲免费人成视频观看| 亚洲国产精品碰碰| 中文字幕不卡亚洲 | 久久精品国产亚洲AV网站| 亚洲av无码国产精品色午夜字幕| 亚洲欧好州第一的日产suv| 国产精品无码亚洲精品2021| 鲁大师在线影院免费观看| 中国在线观看免费高清完整版| 午夜精品在线免费观看| 国产精品免费小视频| 亚洲综合网美国十次| 亚洲一卡2卡3卡4卡乱码 在线| 亚洲国产午夜精品理论片在线播放| 高潮内射免费看片| 99免费在线视频| 免费日本黄色网址| 亚洲AV永久无码精品| 日韩在线视频免费| 午夜男人一级毛片免费| 久久精品国产亚洲精品2020| 亚洲最大视频网站| 老司机午夜免费视频| 一个人看www在线高清免费看| 亚洲VA中文字幕无码一二三区 | 影音先锋在线免费观看| 亚洲高清视频在线播放| 美女又黄又免费的视频| 久久久久国产精品免费免费搜索| 久久伊人亚洲AV无码网站|