Session接口
Session接口对于Hibernate 开发人员来说是一个最重要的接口。然而在Hibernate中,实例化的Session是一个轻量级的类,创建和销毁它都不会占用很多资源。这在实际项目中确实很重要,因为在客户程序中,可能会不断地创建以及销毁Session对象,如果Session的开销太大,会给系统带来不良影响。但值得注意的是Session对象是并不一定是非线程安全的,因此在你的设计中,最好是一个线程只创建一个Session对象。
在Hibernate的设计者的头脑中,他们将session看作介于数据连接与事务管理一种中间接口。我们可以将session想象成一个持久对象的缓冲区,Hibernate能检测到这些持久对象的改变,并及时刷新数据库。我们有时也称Session是一个持久层管理器,因为它包含这一些持久层相关的操作,诸如存储持久对象至数据库,以及从数据库从获得它们。请注意,Hibernate 的session不同于JSP应用中的HttpSession。当我们使用session这个术语时,我们指的是Hibernate中的session,而我们以后会将HttpSesion对象称为用户session。
在Hibernate中session主要是用来操作数据库?
不用框架,纯java代码操作数据库用的是jdbc,代码量太多
用hibernate则是简化了原先的jdbc操作数据库时——增删改查,需要写大量sql语句的弊端
因此,hibernate就用session的一些方法来代替原先的增删改查操作:例如
session.save();//保存
session.get();//获取
session.delete()//删除
session.update();//修改等等
Hibernate
在操作数据库前需要得到Session的实例,这个类似于jdbc中的Connection。
获得Session的方法如下:
Session session = sessionFactory.openSession();
在
Hibernate
3中,取消了find()方法,必须通过Query或Criteria来进行查询。
对于session这个接口的学习可以说是最痛苦也是最复杂的,因为它所涉及的方面太多了,一些隐藏的机制也很多,谁让它是Central API呢。
对于它的几个最基本的方法如save()、delete()、flush()等的学习都花了我一定的时间。在深入了解这些这些方法前,了解session的缓存机制以及
Hibernate
中Java对象的状态对我们是很有帮助的。
一.Session的缓存
Java是纯面向对象的语言,因此不可能像C语言那样直接操纵内存,例如声明一段可用的内存空间。在Java里面,缓存通常是指Java对象的属性占用的内存空间,通常是一些集合类型的属性。在session接口的实现类SessionImpl中定义了一系列的Java集合,这些Java集合就构成了Session的缓存。
使用缓存的一个很明显的好处就是可以减少数据库访问的频率,提高应用程序的性能,因为从内存中读取数据显然要比从数据库中查询快多了。根据我个人的理解,Session的缓存实际上起到了一个“过渡仓库”作用。就像魔兽中的英雄一样,身上都会背有一个包,用来存放常用的物品如补血药水、补魔药水、回城卷等等。如果想用回城卷而身上没有回程卷的话就要跑到商店去shopping了,这样就会浪费大量的时间了,除非你此刻就在商店旁边;如果想用的回城卷的时候身上就有的话,英雄就可以直接用而不必大老远的跑到商店去了。我们的Session的缓存可以说就相当于英雄身上的背包,我的应用程序就是英雄,而数据库就是商店咯。
当然这个比喻不是很准确了,比方说在
Hibernate
应用中我们可以向数据库插入一条新的记录,而在魔兽中你是不可能给商店增加存货量的,只是为了便于理解,才作了这么一个对比。
二.
Hibernate
中Java对象的状态
在一个
Hibernate
应用中,Java对象可以处于以下三个状态之一:
1.临时状态(Transient)。处于这个状态的对象还被没有纳入
Hibernate
的缓存管理体系,跟任何session都不关联,在数据库中也没有对应的记录。
2.持久化状态(Persistent)。处于这个状态的对象位于Session的缓存中,并且和数据库中的一条数据记录相对应。
3.游离状态(Detached)。处于这个状态的对象不再位于Session的缓存中,它与临时对象的最大区别在于,游离对象在数据库中还可能存在一条与它对应的记录。
游离状态的实例可以通过调用save()、persist()或者saveOrUpdate()方法进行持久化。持久化实例可以通过调用 delete()变成游离状态。通过get()或load()方法得到的实例都是持久化状态的。游离状态的实例可以通过调用 update()、0saveOrUpdate()、lock()或者replicate()进行持久化。游离或者自由状态下的实例可以通过调用merge()方法成为一个新的持久化实例。
上述3个状态之间是可以相互转化的,而且我们所说的状态都是针对某一个session实例而言的,比方说,对象A对于session1而言是处于持久化状态的,因为它处于session1的缓存中,但是对于session2而言对象A并不在它的缓存中,因此它是处于游离状态的。
对于这几个状态的理解花费了我一定的时间,因为总是有一些稀奇古怪的念头在我脑海中产生。比如说,对于临时状态的定义,如果我新建一个对象,然后人为的让它属性的值和数据库中的一条记录对应,包括id的取值都一样。此时它能否说是处于游离状态呢?因为它和一条记录想对应呀。实际上这些情况都是由于一些不和规范的操作而产生的。在
Hibernate
应用中,无论Java对象处于临时状态、持久化状态还是游离状态,应用程序都不应该修改它的OID。OID的值应该由
Hibernate
来维护和负责,实际上
Hibernate
在同步缓存中的对象与数据库中的记录时,都是通过OID来进行关联和映射的,如果应用程序人为的修改了对象的OID,就会导致一些莫名其妙的错误,而且这样也不利于数据的同步。
SessionFactory 接口
这里用到了一个设计模式――工厂模式,用户程序从工厂类SessionFactory中取得Session的实例。 Java JEE应用一般只有一个SessionFactory,
SessionFactory并不是轻量级的!实际上它的设计者的意图是让它能在整个应用中共享,其创建和销毁需要耗费很大的资源。典型地来说,一个项目通常只需要一个SessionFactory就够了,但是当你的项目要操作多个数据库时,那你必须为每个数据库指定一个SessionFactory。 并且SessionFactory是线程安全,多个并发线程可以同时访问一个 SessionFactory 并从中获取Session实例。比如服务于客户请求的各线程都通过这个工厂来获得Hibernate的Session实例,这 也是为什么SessionFactory接口的实现必须是线程安全的原因。还有,SessionFactory的内部状态包含着同对象关系影射有关的所有元数据,它是 不可变的,一旦创建好后就不能对其进行修改了。
SessionFactory在Hibernate中实际起到了一个缓冲区的作用,它缓冲了Hibernate自动生成的SQL语句和一些其它的映射数据,还缓冲了一些将来有可能重复利用的数据。
SessionFactory接口提供了获取session类实例的方法。
一般有两种方法创建session实例:
1、getCurrentSession方法:
采用该方法创建的session实例会绑定到当前线程当中。且session实例会在提交或回滚时自动关闭。
2、openSession方法:
采用该方法会创建新的的session实例。使用完后需进行手动关闭。
为了能创建一个SessionFactory对象 应该在Hibernate初始化的时候创建一个Configuration类的实例 并将已经写好的映射文件交给他处理 这样Configuration对象就可以创建一个SessionFactory对象 当SessionFactory对象创建成功后 Configuration对象就没用用了 就可以简单的抛弃他
示例代码:
- Configuration cfg = new Configuration();
- cfg.addResource("com/demo/hibernate/beans/User.hbm.xml");
- cfg.setProperty(System.getProperties());
- SessionFactory sessionFactory = cfg.buildSessionFactory();
再说下,SessionFactory用到了一个设计模式 工厂模式 用户程序从工程类SessionFactory取得Session实例 设计者的意图就是让它能在整个应用中共享 典型的来说 一个项目通常只需要一个SessionFactory就够了 因此我们就设计了HibernateSessionFactory.java这个辅助类 定义了一个静态的Configuration和SessionFactory对象
- private static final Configuration cfg = new Configuration();
- private static org.hibernate.SessionFactory sessionFactory;
这两个对象对整个应用来说只有一个实例存在 因此为用户的访问定义一个本地线程变量:
- private static final ThreadLocal threadLocal = new ThreadLocal();
该线程变量是静态的 对每一个访问该线程的用户产生一个实例 这样在要取得Session对象时 首先从当前用户的线程中取得Session对象 如果还没有创建 则从SessionFactory中创建一个Session 此时会判断SessionFactory对象是否已经创建 该对象对这个应用来说 只有一个 因此 只有第一次访问该变量的用户才会创建该对象
HibernateSessionFactory.java 取得Session对象的过程如下表示
- public static Session currentSession() throws HibernateException {
- Session session = (Session) threadLocal.get();
- if (session == null) {
- if (sessionFactory == null) {
- try {
- cfg.configure(CONFIG_FILE_LOCATION);
- sessionFactory = cfg.buildSessionFactory();
- }
- catch (Exception e) {
- System.err.println("%%%% Error Creating SessionFactory %%%%");
- e.printStackTrace();
- }
- }
- session = sessionFactory.openSession();
- threadLocal.set(session);
- }
- return session;
- }
首先判断threadLocal中是否存在Session对象 如果不存在 则创建Session对象 在创建Session对象时 首先要判断系统是否已经加载Configuration 如果没有sessionFactory 则需要先创建该对象 创建完成的Session对象 需要保存在threadLocal中以供本次访问线程的下一次调用
在关闭Session对象是 只需要从当前线程中取得Session对象 关闭该对象 并置空本地线程变量即可
- public static void closeSession() throws HibernateException {
- Session session = (Session) threadLocal.get();
- threadLocal.set(null);
- if (session != null) {
- session.close();
- }
- }
最后说一下用hibernate的三个必须要了解的三个方面(具体代码我就不写了,你去hibernate框架提供的例子里面找,hibernate有中文文档提供给你的)
1。主配置文件:hibernate.cfg.xml:
用来 配置数据库连接;
映射文件的配置;
其他一些配置;
2。映射文件:XXX.hbm.xml //XXX代表你要关联的类的名称是是什么,你就写什么
向数据中存储数据需要用对象来存储封装,那数据库怎样将对象的属性跟数据库中表的属性对应,就需要你写映射文件;(这是学习重点,量比较多)
3。api:也就是你上面问的session的用法等等
这部分只要了解一些简单操作就行,往往得到session后代码就一句话,这块比较简单,重点还是映射文件那块,只要配置文件整出来,这块不成问题。