hibernate中session的获取使用以及其他注意事项

hibernate中session的获取使用以及其他注意事项

前言:工作时,在同时使用Hibernate的getSession()、getHibernateTemplate()获取Session后进行数据查询时不是出现了"session is close"异常就是出现其他异常问题,痛定思痛,决定收集并整理相关资料,方便今后的使用。

一、session的获取

在hibernate中的Session对象通过SessionFactory来管理,可以通过使用openSession ()、getSession()和getCurrentSession()方法来获取session。

Spring和Hibernate的集成的一个要点就是对事务的支持,openSession、getCurrentSession都是编程式事务(手动设置事务的提交、回滚)中重要的对象,HibernateDaoSupport则提供了更方便的声明式事务支持。(引自:lmdcszh的 由openSession、getCurrentSession和HibernateDaoSupport浅谈Spring对事物的支持

  注:在 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 执行时,如果当前 Session 为空, currentSession 会调用 SessionFactory 的 openSession 。

  所以 getCurrentSession() 对于 Java EE 来说是更好的获取 Session 的方法。

  二、openSession ()、getSession()和getCurrentSession()方法的区别

  以下内容引自jpbirdy的文章“Hibernate中的openSession(),getSession() 和 getCurrentSession() 的区别”。讲的非常好,我就不一一细述,文章内容如下:



开发中,使用MyEclipse自动生成的Hibernate DAO类中在对session的获取中,都使用的getSession(MyEclipse8.5之前的版本都是如此)

首先要说明一下这3个方法:

1、openSession 和 getCurrentSession这两个方法是 Hibernate中的sessionFactory中的方法。要获取session只能通过这两个方法获取。

2、getSession这个方法是使用MyEclipse的Hibernate工具时自动生成的方法。进入方法里查看:

public static Session getSession() throws HibernateException {
  Session session = (Session) threadLocal.get();//注1
  if (session == null || !session.isOpen()) {
    if (sessionFactory == null) {
      rebuildSessionFactory();//注2
    }
    session = (sessionFactory != null) ? sessionFactory.openSession(): null;
    threadLocal.set(session);
  }
  return session;
}

  很明显,Myeclipse使用的是openSession。所以上述的3个方法,其实也就是openSession和getCurrentSession的区别。

  这里简单说明一下openSession 和 getCurrentSession这两个方法的区别。

  很容易看出来,openSession每次都是创建一个新的session,而getCurrentSession会查看,如果当前上下文中已经有Session,则直接使用原有的,否则会创建一个新的session。

  当然这样说并不是很形象。很多人会理解为,后面一个只是前面的略微优化而已,每次都打开新的也不会出错。当然这样理解并没有完全错误,其实这里的Session使用上没有很大的区别,区别是在于使用openSession时,如果涉及到对数据库的数据的修改(增、删、改),在事务提交之后,session必须手动关闭(session.close()),否则会出错,一般是数据已经保存进数据库,但使用Hibernate再次查询数据时并没有显示新的值。而getCurrentSession在事务提交后会自动关闭当前session。所以,一般推荐使用getCurrentSession,这样需要我们关心的内容就更少,而且更不容易出错。   当然,MyEclipse也不会放着高级的东西不用的,仔细查看上面的代码,发现在openSession之前,session会先从threadLocal中获取,这里的threadLocal就可以类比为getCurrentSession中配置的thread,所以,可以认为MyEclipse是在openSession之前已经实现了从线程中获取上下文的Session,所以直接使用MyEclipse生成的getSession的功能类似getCurrentSession,但是测试后发现,getSession在提交事务之后,并没有自动关闭,仍然需要手动关闭。   

  为什么MyEclipse不使用getCurrentSession呢?
  查看生成的DAO类可以发现,MyEclipse生成的是没有和事务相关的操作的,当然在进行save、update、delete操作时必须手动添加事务开始和事务提交,所以这里在提交事务之后必须手动关闭session。而进行数据库查找操作时,openSession是不需要提交事务的。getCurrentSession则必须要打开一个新的事务,否则会报createQuery is not valid without active transaction错误。

  从简化操作的角度,直接使用MyEclipse的方法更为简单,但要记住增删改操作必须手动添加事务,并在操作结束后关闭session。



注释:

  注1-ThreadLocal

  其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。

  线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。

  常用API:

  T get( )      --返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。

  void set(T value)  --将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。

  (引自:深入研究java.lang.ThreadLocal类

  注2-rebuildSessionFactory 重新加载hibernate,在HibernateUtil中的具体实现如下:

public static void rebuildSessionFactory() {
    log.debug("Using current Configuration to rebuild SessionFactory");
    rebuildSessionFactory(configuration);
}
public static void rebuildSessionFactory(Configuration cfg) {
    log.debug("Rebuilding the SessionFactory from given Configuration");
    if (sessionFactory != null && !sessionFactory.isClosed())
        sessionFactory.close();
    if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
      log.debug("Managing SessionFactory in JNDI");
      cfg.buildSessionFactory();
    } else {
        log.debug("Holding SessionFactory in static variable");
        sessionFactory = cfg.buildSessionFactory();
    }
    configuration = cfg;
}        

 HibernateUtil 源码:请点击!

总结:

  1、openSession和getCurrentSession的根本区别在于有没有绑定当前线程,所以,使用方法有差异:

* openSession 没有绑定当前线程,所以,使用完后必须关闭。重新建立一个新的session。

* currentSession 和当前线程绑定,在事务结束后会自动关闭。使用当前的session。

  2、在一个应用程序中,如果DAO 层使用Spring 的hibernate 模板,通过Spring 来控制session 的生命周期,则首选getCurrentSession ()。

  以下内容引自lmdcszh由openSession、getCurrentSession和HibernateDaoSupport浅谈Spring对事物的支持,想看原文的可直接点击此链接。

 三、事务的边界和传播

  通常情况下事务的边界需要设置在业务逻辑处理层中,但是若在一个业务中涉及到多个业务逻辑层之间的方法,且这些方法需要在同一个事务中运行,那这就涉及到了事务的传播性。

如果使用openSession,就要在dao层的方法中传递session,而这种做法是很糟糕的,首先增加了参数的个数,另外,方法是否需要事务,完全是可以当做一种独立的服务抽离出的。

因为currentSession是线程级别的,所以,只要业务逻辑方法在同一个线程中,就不会担心上面的问题。这也是currentSession的一个优越处之一。

  使用currentSession:

  1.在配置文件中将线程配置成Thread级别的。

<property name="hibernate.current_session_context_class">thread</property>

  2.调用sessionFactory的getCurrentSession方法:

 1 publicvoid addUser(User user) {
 2   Session session = null;
 3   try {
 4     session =this.getSessionFactory().getCurrentSession();
 5     session.beginTransaction();
 6     session.save(user);
 7     //记录操作日志,此处略
 8     session.getTransaction().commit();
 9   }catch(Exception e) {
10     e.printStackTrace();
11     session.getTransaction().rollback();
12   }
13 }

  使用openSession:

 1 public void addUser(User user) {
 2   Session session = null;
 3   try{
 4     session= this.getSession();
 5     session.beginTransaction();
 6     // 若干操作…………
 7     session.getTransaction().commit();
 8   }catch(Exceptione) {
 9     e.printStackTrace();
10     session.getTransaction().rollback();
11   }finally{
12     HibernateUtils.closeSession(session);
13   }

  使用HibernateDaoSupport声明式事务:即使用this.getHibernateTemplate()方法时可调用的方法,如save、update等。

  Spring与Hibernate的集成使用最多的是HibernateDaoSupport,它对session的获取以及事务做了进一步的封装,只需要关注dao的实现,而不用担心某个地方的事务是否关闭。

  四、关于异常与事务回滚

Spring在遇到运行期异常(继承了RuntimeException)的时候才会回滚,如果是Exception(如用户输入密码错误)抛出就好,事务会继续往下进行。

Spring对异常的处理的灵活性还是比较高的,可以配置遇到某个Exception进行回滚,某个RuntimeException不回滚,但是对于EJB就没有这么灵活了,EJB相当于是固定的套餐。

  不会回滚:

1 public void addUser(User user)  throws Exception {
2   this.getHibernateTemplate().save(user);
3   //若干操作……
4   throw new Exception();
5 } 

  回滚:

1 public void addUser(User user) {
2     this.getHibernateTemplate().save(user);
3     //若干操作……
4     throw new RuntimeException();
5  } 

  五、事务传播特性

  为了保证调用的业务逻辑方法都使用同一个事务,通常都使用REQUIRED这个级别,它表示:如果上一个方法中有事务,就直接使用,如果没有,就创建一个事务,这样,一旦事务创建了后,后续调用的方法就不会再创建。

  其他的事务传播特性见下表:

  六、Spring事务的隔离级别

  1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。

另外四个与JDBC的隔离级别相对应。

  2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。

这种隔离级别会产生脏读,不可重复读和幻像读。

  3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据

  4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。

它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

  5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。

  除了防止脏读,不可重复读外,还避免了幻像读。

  事务隔离级别主要应用在对大数据的处理方面,与锁的机制是密不可分的,这里不赘述。

时间: 2024-10-13 00:09:30

hibernate中session的获取使用以及其他注意事项的相关文章

Hibernate中Session的get和load

hibernate中Session接口提供的get()和load()方法都是用来获取一个实体对象,在使用方式和查询性能上有一些区别.测试版本:hibernate 4.2.0. get Session接口提供了4个重载的get方法,分别通过“持久类+主键”和“全类名+主键”以及“锁选项”来获取实体对象. public Object get(Class clazz, Serializable id); public Object get(Class clazz, Serializable id, L

Hibernate中Session与本地线程绑定

------------------siwuxie095 Hibernate 中 Session 与本地线程绑定 1.Session 类似于 JDBC 的连接 Connection 2.Session 对象是单线程对象,只能自己使用,不能共用 将 Session 与本地线程绑定,保证 Session 对象绝对是一个单线程对象 3.Hibernate 帮助我们实现了 Session 与本地线程绑定(底层是 ThreadLocal) 4.获取与本地线程绑定的 Session (1)在 Hiberna

[转]Hibernate中Session的get和load

hibernate中Session接口提供的get()和load()方法都是用来获取一个实体对象,在使用方式和查询性能上有一些区别.测试版本:hibernate 4.2.0. get Session接口提供了4个重载的get方法,分别通过“持久类+主键”和“全类名+主键”以及“锁选项”来获取实体对象. public Object get(Class clazz, Serializable id); public Object get(Class clazz, Serializable id, L

hibernate中session线程安全的实现

在hibernate中session是使用ThreadLocal实现线程安全的. ThreadLocal并不是一个Thread,而是一个线程副本,ThreadLocal为每个使用该变量的线程提供一个变量副本,线程修改自己的变量副本不会影响其他线程的变量副本 ThreadLocal有四个方法: set():设置当前线程的局部变量的值 get():获取当前线程的局部变量的值 remove():将当前线程局部变量的值删除,此方法不必显示的调用,因为局部变量自有垃圾回收器去清理 initialValue

HttpSession与Hibernate中Session的区别

HttpSession与Hibernate中Session的区别 一.javax.servlet.http.HttpSession是一个抽象接口 它的产生:J2EE的Web程序在运行的时候,会给每一个新的访问者建立一个HttpSession,这个Session是用户身份的唯一表示.注意,是容器(Tomcat,Resin)自动创建的. 用途:存放这个用户的一些经常被用到的信息,例如:用户名,权限.例如在购物车程序里,存放用户买的商品. 销毁:一定时间(跟容器有关)内,用户无任何动作,session

hibernate中session

hibernate中的session是一级缓存,可以理解为进程级的缓存.在进程运行期间一直存在. session可以理解为一个可以操作数据库的对象 具体如何操作数据库? session中有方法, 如果只是单独的hibernate程序的话. 获得session之后,打开事务,操作数据库,提交(回滚事务),关闭session.就这样完成了操作DB ? Hibernate中session的使用: 在Hibernate中session主要是用来操作数据库? Hibernate在操作数据库前需要得到Ses

Hibernate中Session的关闭处理(无法获取连接池)

java.lang.IllegalStateException: Pool not open 在使用Spring进行系统开发的时候,数据库连接一般都是配置在Spring的配置文件中,并且由Spring来管理的.在利用Spring + Hibernate进行开发时也是如此.下面是一个简单的Spring + Hibernate Dao的例子: 程序代码public class DaoReal extends HibernateDaoSupport implements Dao { public Li

Hibernate中Session之get和load方法的真正区别

最近在学习SHH框架中的hibernate,对Session的get和load方法,有点混不清楚,不知道区别在哪,或者对它们的区别感触不深.所以百度了一下,结果问题来了.百度的结果和实际测试的结果出入很大.主要是对get方法的说法跟实际运行的结果不一致. 先说一下观点吧: get不支持lazy,load支持lazy: 数据不存在时,get返回null,load则抛出ObjectNotFoundException异常. load方法可以返回实体的代理类实例,而get方法直接读取数据库,所以直接返回

Hibernate中Session.get()方法和load()方法的详细比较(转)

一.get方法和load方法的简易理解 (1)get()方法直接返回实体类,如果查不到数据则返回null.load()会返回一个实体代理对象(当前这个对象可以自动转化为实体对象),但当代理对象被调用时,如果没有数据不存在,就会抛出个org.hibernate.ObjectNotFoundException异常 (2)load先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中查询,这就是我们常说的 load