`
liufei.fir
  • 浏览: 685192 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

HibernateTemplate 类 使用

阅读更多
目的:使用HibernateTemplate执行execute(new HibernateCallback())方法,从HibernateCallback中得到session,在此session中做多个操作,并希望这些操作位于同一个事务中。

    如果你这样写(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");// 在数据库中,name字段不允许为null
                session.save(stu1);
                session.flush();//实际上,如果不是程序员"手痒"来调用这个flush(),HibernateTemplate中session的事务处理还是很方便的

                Student stu2 = new Student();
                session.save(stu2);// 没有设置name字段,预期会报出例外
                session.flush();
                return null;
            }
        });

    }


    你期望spring在执行完execute回调后,在关闭session的时候提交事务,想法是很好的,但spring并不会这么做。让我们来看看在 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;
    }


    这个方法中的result是一个org.hibernate.transaction.JDBCTransaction实例,而方法中的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 );
    }


    它返回了一个JDBCTransaction,没什么特别的。

    在代码2中,执行了result.begin(),其实也就是JDBCTransaction实例的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);//把自动提交设为了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时,要在事务结束的时候,写上一句:tx.commit(),这个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();//重点代码,它的作用是提交事务,并把connection的autocommit属性恢复为true
            log.debug("committed JDBC Connection");
            committed = true;
            if (callback) {
                jdbcContext.afterTransactionCompletion(true, this);
            }
            notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_COMMITTED);
        } catch (SQLException e) {
            log.error("JDBC commit failed", e);
            commitFailed = true;
            if (callback) {
                jdbcContext.afterTransactionCompletion(false, this);
            }
            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();//这段的作用是恢复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,并手动管理它的session,并手动开启事务关闭事务的话,完全可以保证你的事务(好像完全是废话)。

但是,如果你用的是HibernateTemplate,如同源代码1一样,则不要指望spring在关闭session的时候为你提交事务(罪魁祸首就是在代码1中调用了session.flush())。因为在使用代码1 时,spring中得到session的方式如下:Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) : sessionFactory。openSession());简单地说它就是得到了一个session,而没有对connection的 autocommit()作任何操作,spring管理范围内的session所持有的connection是autocommit=true 的,spring借助这个属性,在它关闭session时,提交数据库事务。,因此如果你在源代码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());//打印一下事务提交方式
                // 保存stu1
                Student stu1 = new Student();
                stu1.setName("aaaa");// 在数据库中,name字段不允许为null
                session.save(stu1);
                session.flush();

                Student stu2 = new Student();
                session.save(stu2);// 没有设置name字段,预期会报出例外
                session.flush();
                return null;
            }
        });

    }


    运行后,它打出的结果是true,也就是说,虽然保存stu2时会报出例外,但如果commit属性为true,则每一个到达数据库的sql语句会立即被提交。换句话说,在调用完session.save(stu1)后,调用session.flush(),会发送sql语句到数据库,再根据commit 属性为true,则保存stu1的操作已经被持久到数据库了,尽管后面的一条insert语句出了问题。

    因此,如果你想在HibernateCallback中使用session的事务,需要如下写:

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");//在数据库中,name字段不允许为null
                session.save(stu1);
                session.flush();
              
                Student stu2 = new Student();
                session.save(stu2);//没有设置name字段,预期会报出例外
                   session.flush();
                session.connection().commit();
                //至于session的关闭就不用我们操心了
                return null;
            }
        });

    }


    运行上述代码,没问题了。至此,可能有些读者早就对代码1不满意了:为什么每次save()以后要调用flush()?这是有原因的。下面我们来看看把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");// 在数据库中,name字段不允许为null
                session.save(stu1);
                // session.flush();

                Student stu2 = new Student();
                session.save(stu2);// 没有设置name字段,预期会报出例外
                // session.flush();
                session.connection().commit();
                return null;
            }
        });

    }


    运行上述代码,后台报数据库的not null错误,这个是合理的,打开数据库,没有发现新增记录,这个也是合理的。你可能会说:由于事务失败,数据库当然不可能会有任何新增记录。好吧,我们再把代码改一下,去除not null的错误,以确保它能正常运行。代码如下:

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");// 在数据库中,name字段不允许为null
                session.save(stu1);
                // session.flush();

                Student stu2 = new Student();
                stu2.setName("asdfasdf");//好了,这个字段设过值,不会再报not null错误了
                session.save(stu2);
                // session.flush();
                session.connection().commit();
                return null;
            }
        });

    }


    至此再运行上述代码,出现了一个奇怪的问题:虽然控制台把insert语句打出来了,但是:数据库没有出现任何新的记录。

    究其原因,有二:

    一、 session.connection()。commit()确实导致数据库事务提交了,但是此刻session并没有向数据库发送任何语句。

    二、 在spring后继的flushIfNecessary()和closeSessionOrRegisterDeferredClose()方法中,第一个方法向数据库发送sql语句,第二个方法关闭session,同时关闭connection,然后问题在于:connection已经在程序中被手动设置为auttocommit=false了,因此在关闭数据库时,也不会提交事务。

    解决这个问题很容易,在程序中手动调用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");//在数据库中,name字段不允许为null
                session.save(stu1);
              
                Student stu2 = new Student();
                session.save(stu2);//没有设置name字段,预期会报出例外
              
                session.flush();//向数据库发送sql
                session.connection().commit();
                return null;
            }
        });

    }


    运行上述代码,打开数据库查看,没有新增任何记录。在代码中新加一行stu2.setName("aaa");再次运行代码,发现数据库表中多了两条记录。事务操作成功。

    至此,虽然操作成功,但事情还没有结束。这是因为spring在调用doInHibernate()的后继的步骤中,还要进行 flushIfNecessary()操作,这个操作其实最后调用的还是session.flush()。因为在程序中已经手动调用过 session.flush(),所以由spring调用的session.flush()并不会对数据库发送sql(因为脏数据比对的原因)。虽然不会对结果有什么影响,但是多调了一次flush(),还是会对性能多少有些影响。能不能控制让spring不调用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");//在数据库中,name字段不允许为null
                session.save(stu1);
              
                Student stu2 = new Student();
                stu2.setName("sdf");
                session.save(stu2);//没有设置name字段,预期会报出例外
              
                session.flush();
                session.connection().commit();
                return null;
            }
        });

    }


    通过设置HibernateTemplate的flushMode=FLUSH_NEVER来通知spring不进行session.flush()的调用,则spring的flushIfNecessary()将不进行任何操作,它的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()终于修改完毕。但事实上,这样的操作无疑是比较麻烦的,因此如果在spring中想利用session进行事务操作时,最好还是用TransactionTemplate(编程式事务)或是声明式事务比较方便一些。

    本例通过这么一个虽然简单但又绕来绕去的例子,主要是说明hibernate事务的一些内在特性,以及HibernateTemplate中如何处理 session和事务的开关,让读者对HibernateTemplate的源代码处理细节有一些了解,希望能给读者有抛砖引玉的作用。
分享到:
评论

相关推荐

    HibernateTemplate类的使用

    ### HibernateTemplate类的使用详解 #### 一、引言 在Java开发中,持久层框架Hibernate因其优秀的ORM(Object-Relational Mapping)特性被广泛应用。为了更便捷地使用Hibernate,Spring框架提供了一系列工具类来简化...

    HibernateTemplate详细描述以及使用范围

    ### HibernateTemplate 的详细介绍与使用范围 #### 一、概述 在Spring框架中,`HibernateTemplate`作为ORM(Object Relational Mapping)技术中的一个重要组件,它提供了简化Hibernate操作的方法集,使得开发人员...

    HibernateTemplate源代码

    `HibernateTemplate`类本身并没有直接继承自任何特定类,但它是Spring框架中众多DAO支持类之一。这些类通常用于处理数据访问异常以及资源管理等任务。`HibernateTemplate`主要依赖于`SessionFactory`来创建和管理...

    HibernateTemplate的方法使用

    **HibernateTemplate** 是 Spring 框架中用于简化 Hibernate 使用的一个工具类。它通过 AOP(面向切面编程)的思想,封装了大量的 CRUD(创建、读取、更新、删除)操作,使得开发者能够更加专注于业务逻辑的实现,而...

    HibernateTemplate 的常规用法.doc

    在实际应用中,DAO(Data Access Object)类通常会使用HibernateTemplate。例如,以下是一个简单的`PersonDAOImpl`类的示例: ```java public class PersonDAOImpl implements PersonDAO { private static Log log...

    hibernateTemplate

    下面是一个使用`HibernateTemplate`的DAO实现类的示例代码,该类实现了`PersonDAO`接口,包含了基本的增删改查操作。 ```java public class PersonDAOHibernate implements PersonDAO { // 使用log4j记录日志 ...

    hibernateTemplate的常用方法

    `HibernateTemplate`是Spring框架中用于操作Hibernate的一个便捷工具类,它极大地简化了Hibernate在实际应用中的集成与开发工作。通过`HibernateTemplate`,开发者可以非常方便地进行数据的增删改查等基本操作,而...

    HibernateTemplate的用法总结

    HibernateTemplate是Spring框架提供的一种操作Hibernate的方式,它简化了Hibernate的使用过程,使开发者无需关注Session的管理与事务控制,极大地提高了开发效率。本文将对HibernateTemplate的主要用法进行详细介绍...

    HibernateTemplate分组统计

    1. **HibernateTemplate**:是Spring框架提供的用于简化Hibernate使用的模板类。它提供了大量操作数据库的方法,包括查询、更新等,并且内置了事务管理机制。 2. **分组统计**:指通过SQL语句对查询结果按照一个或多...

    hibernateTemplate批量删除数据

    本文将深入探讨如何利用Hibernate中的`HibernateTemplate`类进行批量数据删除操作,这不仅能够提高数据处理效率,还能优化数据库性能。 ### HibernateTemplate与批量删除 在Hibernate框架中,`HibernateTemplate`...

    SSH2增删改查使用HibernateTemplate

    在这个框架中,HibernateTemplate是Spring提供的一个工具类,它简化了Hibernate的使用,使得开发者可以更方便地进行数据库操作,如增删改查。接下来,我们将深入探讨如何在SSH2中使用HibernateTemplate进行这些基本...

    一个模拟Spring将SessionFactory注入到HibernateTemplate的小例子

    3. **使用HibernateTemplate**:在我们的业务逻辑类中,我们可以通过@Autowired注解或者使用getBean方法从Spring容器中获取HibernateTemplate实例,然后调用它的方法进行数据库操作。例如: ```java @Service ...

    HibernateTemplate汇总

    HibernateTemplate是 Hibernate框架中的一种模板类,提供了许多实用的方法来操作数据库。下面将对HibernateTemplate中的一些常用方法进行详细介绍: applyNamedParameterToQuery方法 该方法用于将命名参数应用于...

    基于HIbernateTemplate的代码自动生成

    2. DAO接口的实现类,使用`HibernateTemplate`进行实际的数据库操作。 Service层的生成则涉及: 1. Service接口,声明业务逻辑方法,可能包括复杂的业务组合操作。 2. Service接口的实现类,调用DAO层的方法完成...

    Spring_Spring_Hibernate_HibernateTemplate

    HibernateTemplate是Spring为简化Hibernate操作而设计的一个类,它包装了Hibernate的Session对象,提供了事务管理、异常转换等功能。使用HibernateTemplate,开发者无需手动管理Session,避免了可能出现的资源泄露...

    对hibernate的封装 HibernateTemplate

    为了解决这些问题,Spring框架提供了一个称为HibernateTemplate的类,它是对Hibernate API的一个封装,旨在提高开发效率和代码的可维护性。 **一、HibernateTemplate介绍** HibernateTemplate是Spring框架的一个...

    HibernateTemplate及generator用法.doc

    为了简化Hibernate与Spring的整合,Spring提供了`HibernateTemplate`类,它是一个封装了Hibernate Session的工具类,使得在处理数据库操作时更加方便。`HibernateTemplate`在事务管理、异常转换等方面提供了很多便利...

    hibernateDaoSurpport和hibernateTemplate

    `HibernateTemplate`是Spring提供的用于简化Hibernate使用的类。它提供了许多便捷的方法,使得开发者无需关心Hibernate Session的管理,而可以直接进行数据访问操作。以下是几个常见的方法: - **void delete...

    HibernateTemplate及generator用法.docx

    为了简化Hibernate与Spring的集成,Spring提供了HibernateTemplate类,它是Spring对Hibernate的一种封装,使得在DAO层操作数据库变得更加简便和安全。 首先,我们来看如何在DAO中使用HibernateTemplate。通常,DAO...

    HibernateTemplate.saveOrUpdate时出现\xE7\x84十六进制之类的字符串

    在使用HibernateTemplate的saveOrUpdate方法时,如果遇到类似`\xE7\x84`这样的十六进制字符串,通常这是由于字符编码不匹配导致的汉字乱码问题。这个问题主要涉及到数据库的字符集设置、应用程序的编码配置以及数据...

Global site tag (gtag.js) - Google Analytics