Spring中HibernateTemplate类的使用

  实际情况中,对于编程事务的操作最好还是使用org.springframework.transaction.support.TransactionTemplate,因为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的源代码处理细节有一些了解,
希望能给读者有抛砖引玉的作用。

转载于:https://www.cnblogs.com/phoebus0501/archive/2011/01/04/1925757.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422965.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【OS学习笔记】三十三 保护模式九:分页机制对应的汇编代码之---用户程序代码

本片文章是以下两篇文章: 【OS学习笔记】三十 保护模式九:段页式内存管理机制概述【OS学习笔记】三十一 保护模式九:页目录、页表和页三者的关系详解 对应的用户程序汇编代码。 ;代码清单16-2;文件名:c16.asm;文件说明&#xff…

一款研发管理软件的对象标签介绍

如果你了解WIKI,WIKI中的所有信息链接以标签的方式网状关联。TOPO系统中实现了完整的TAG功能,区别于目前市面上的其他类似管理系统,TOPO系统中实现的标签功能更加彻底和全面,例如完全用户自定义TAG,系统中所有管理对象…

【OS学习笔记】三十四 保护模式十:中断和异常区别

上几篇文章学习了分页机制的一些原理: 【OS学习笔记】三十 保护模式九:段页式内存管理机制概述【OS学习笔记】三十一 保护模式九:页目录、页表和页三者的关系详解 今天继续学习保护模式下的关于中断与异常的概念。本文主要学习中断与异常的…

记录一次文件迁移

事情的经过是这样子的!数据库A表添加一条记录,**系统中B目录下就会多出5n个文件。随着系统运行3年多,B目录中的文件数已高达2M多,而这些文件恰恰又是用户高度频繁访问的。于是问题就来了,一方面是用户访问文件速度变慢…

【OS学习笔记】三十五 保护模式十:中断描述符表、中断门和陷阱门

上一篇文章学习了中断与异常的概念:【OS学习笔记】三十四 保护模式十:中断和异常区别 本片文章接着学习以下内容: 中断描述符表中断门陷阱门 1 中断描述符表 我们前面讲了无数次,在实模式下,是由位于低地址的1M内存…

【OS学习笔记】三十六 保护模式十:通过中断发起任务切换----中断任务

上一篇文章学习了:OS学习笔记】三十五 保护模式十:中断描述符表、中断门和陷阱门 本篇文章接着上一篇文章学习中断任务。 我们在前面文章中一直在说通过中断发起任务切换,本文就是将之前没有说明白的内容:通过中断发起任务切换讲…

GC是什么? 为什么要有GC?

答:GC是垃圾收集器。程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一: System.gc()Runtime.getRuntime().gc() 转载于:https://www.cnblogs.com/mianshi/archive/2011/01/05/192663…

【OS学习笔记】三十七 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----主引导扇区代码

本文是以下几篇文章对应的主引导扇区代码汇编代码: 【OS学习笔记】三十四 保护模式十:中断和异常区别【OS学习笔记】三十五 保护模式十:中断描述符表、中断门和陷阱门【OS学习笔记】三十六 保护模式十:通过中断发起任务切换----中…

过滤JSON中的特殊字符

过滤方法用的是Discuz中的过滤方法&#xff01; /// <summary>/// 加载事件/// </summary>/// <param name"sender"></param>/// <param name"e"></param>protected void Page_Load(object sender, EventArgs e){if(!…

【OS学习笔记】三十八 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----微型内核汇代码

本文是以下几篇文章对应的微型内核代码汇编代码&#xff1a; 【OS学习笔记】三十四 保护模式十&#xff1a;中断和异常区别【OS学习笔记】三十五 保护模式十&#xff1a;中断描述符表、中断门和陷阱门【OS学习笔记】三十六 保护模式十&#xff1a;通过中断发起任务切换----中断…

小议传统分层与新式分层,抑或与DDD分层

引言本文提到的分层只是软件架构上的分层。文中的传统分层指的是传统的三层结构&#xff1a;UI&#xff08;界面表现层&#xff09;,BLL&#xff08;业务逻辑层&#xff09;,DAL&#xff08;数据访问层&#xff09;。文中提出的观点也都是个人的一点认识&#xff0c;与任何组织…

【OS学习笔记】三十九 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----动态加载的用户程序/任务一代码

本文是以下几篇文章对应的动态加载的用户程序/任务一代码&#xff1a; 【OS学习笔记】三十四 保护模式十&#xff1a;中断和异常区别【OS学习笔记】三十五 保护模式十&#xff1a;中断描述符表、中断门和陷阱门【OS学习笔记】三十六 保护模式十&#xff1a;通过中断发起任务切…

WCF 第八章 安全 客户端认证

一个服务的客户端认证是通过向服务提供一系列信赖的声明。声明可以是任何形式&#xff0c;只要客户端和服务端理解这个格式并信赖它的来源就可以。 如果客户端和服务端共享一个秘密信息&#xff0c;比如一个用户名和密码&#xff0c;只要客户端通过一个合法认证发送数据&#x…

前端学习(205):animation动画库

动画库 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible" conte…

【OS学习笔记】四十 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----动态加载的用户程序/任务二代码

本文是以下几篇文章对应的微型动态加载的用户程序/任务二代码&#xff1a; 【OS学习笔记】三十四 保护模式十&#xff1a;中断和异常区别【OS学习笔记】三十五 保护模式十&#xff1a;中断描述符表、中断门和陷阱门【OS学习笔记】三十六 保护模式十&#xff1a;通过中断发起任…

JS调用后台方法大全

javascript函数中执行C#代码中的函数&#xff1a;方法一&#xff1a; 1、首先建立一个按钮&#xff0c;在后台将调用或处理的内容写入button_click中;    2、在前台写一个js函数&#xff0c;内容为document.getElementById("btn1").click();    3、在前台或后…

【OS修炼指南目录】----《X86汇编语言-从实模式到保护模式》读书笔记目录表

学习交流加&#xff08;可免费帮忙下载CSDN资源&#xff09;&#xff1a;个人微信&#xff1a; liu1126137994学习交流资源分享qq群1&#xff08;已满&#xff09;&#xff1a; 962535112学习交流资源分享qq群2&#xff1a; 780902027 本文是将个人的关于《X86汇编语言-从实模式…

C语言编译全过程剖析

内容摘要 C语言编译的整个过程是非常复杂的&#xff0c;里面涉及到的编译器知识、硬件知识、工具链知识都是非常多的&#xff0c;深入了解整个编译过程对工程师理解应用程序的编写是有很大帮助的&#xff0c;希望大家可以多了解一些&#xff0c;在遇到问题时多思考、多实践。 一…

【剑指offer - C++/Java】7、斐波那契数列

在线题目链接&#xff1a;斐波那契数列 文章目录1、题目描述2、题目分析3、代码3.1 递归方法3.11 Java代码3.12 C代码3.2 动态规划3.21 Java代码3.22 C代码3.3 循环方法3.31 Java代码3.32 C代码4、总结1、题目描述 大家都知道斐波那契数列&#xff0c;现在要求输入一个整数n&a…

母版事件中注册javascript脚本

母版事件中注册javascript脚本 ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ Title :㈠母版等事件中注册javascript脚本 Description: version : 1.0 Date :8:46 2008-3-26 Author : Tia…