在这一节中,我主要介绍下面的内容:
- Session介绍
- Save和Update
- 实体对象的识别
- Hibernate一级缓存
- Hibernate 二级缓存
- Hibernate 事务处理
- 使用复合主键
1.1 Session介绍
Session是hibernate运作的中心,对象的生命周期、事务的管理、数据库的存取都与Session息息相关。在Hibernate中使用持久化对象PO(Persistent Object)完成持久化操作,对PO的操作必须在Session的管理下才能同步到数据库。
从这个图中,我们可以看到流程的第一步就是Configuration对象,Configuration对象的作用就是用于配置和启动Hibernate。Hibernate应用通过Configuration实例来指定对象/关系映射文件的位置或者动态配置hibernate的属性,然后创建一个SessionFactory实例。
注意:Configuration实例是一个启动期间对象,一旦SessionFactory创建完成它就被丢弃了
第二步,就是创建一个SessionFactory实例,Configuration负责创建SessionFactory实例,是通过Configuration的buildSessionFactory方法创建的,buildSessionFactory方法把Configuration对象所包含的所有配置信息都复制到SessionFactory对象的缓存中当中。同时,SessionFactory对象创建后就不再和Configuration对象关联了,即Configuration完成使命,被丢弃了。
一个SessionFactory对象就代表着一个数据库存储源,通常一个应用程序只需要创建一个SessionFactory实例即可。由此看出其具备如下特点:
- 线程安全:整个应用共用一个SessionFactory实例。
- 重量级: 在SessionFactory中存放了Hibernate配置信息以及映射元素据信息,这些都需要很大的缓存。
第三步,就是创建Session,Session接口负责执行被持久化队形的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句)。但需要注意的是Session对象是非线程安全的。
Session的生命周期绑定在一个物理的事务上面,Session的主要功能是提供对映射的实体类实例的创建、读取和删除操作。实例可能以下面三种状态存在:
1.自由状态:不曾进行持久化,未与任何Session相关联。
2.持久化状态:仅与一个Session相关联。
3.游离状态:已经进行过持久化了,但当前未与任何Session相关联。
当然,这几种形态中也能进行相互之间的转换:游离状态的实例可以通过调用save()、 epersist()或者saveOrUpdate()方法进行持久化。持久化实例可以通过调用delete()变成游离状态。通过get()或load()方法得到的实例都是持久化状态的。游离状态的实例可以通过调用update()、saveOrUpdate()、lock()或者replicate()进行持久化。游离或者自由状态下的实例可以通过调用merge()方法成为一个新的持久化实例。
1.2 Save还是Update
在我们使用Hibernate的时候可能会有一些疑问?对象的三种状态的变化,Hibernate根据什么来判断对象是否被更改又是根据什么来进行判断的呢。。。如果被修改,是应该增加一条新纪录还是修改某个记录?
在使用saveorUpdae方法的时候,这需要在对象映射文件的主键中定义unsaved-value属性,如果不显示定义,则默认为unsaved-value=null
unsaved-value的属性值可以是下面的几点:
- null: 主键是对象类型,Hibernate判断操作对象的主键是否为null,来判断操作对象是否已经被持久化,如果是,调用save方法,生成insert语句,在数据库中增加一条记录;如果不是,设置主键则直接生成update的SQL语句,发送update,如果数据库中没有那条记录,则抛出异常。
- none:由于不论主键属性为任何值,都不可能为none,因此hibernate总是对被操作对象发送update。
- any:由于不论主键属性为任何值,都肯定为any,因此Hibernate总是对project对象发送save,hibernate生成主键。
1.3 实体对象的识别
Hibernate将数据库数据同Java实体对象关联在一起,使开发者以操作对象的方式实现数据库的访问。相对于数据库,比较两个记录是否相同,一般比较其主键值是否相同就可以了,那么Hibernate持久化的实体对象如何进行比较呢?
当我们需要进行实体对象的识别的时候,可以在持久化类对象中增加equals()、hasCode()两个方法。
public boolean equals(Object o){
if(this == o) return true;
if(id == null || !(o instanceof Medicine)) return false;
final Medicine medicine = (Medicine) o;
return this.id.equals(medicine.getId());
}
public int hasCode(){
return id == null?System.identityHashCode(this):id.hashCode();
}
这就是在进行比较的时候,我们需要在实体类中进行重写两个方法。
1.4 Hibernate一级缓存
首先,我们先来说一下缓存的定义。缓存(Cache)是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的副本,应用程序在运行时直接读写缓存中的数据,只有某些特定时刻按照缓存中的数据来同步更新数据存储源。
技术要点:
Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,当程序调用方法时,Hibernate会把该对象加入到一级缓存中,当Session关闭时,该Session所管理的一级缓存也会立即被清除。
注意:Hibernate的一级缓存是Session所内置的,不能被卸载,也不能进行任何的配置。
1.5 Hibernate二级缓存
二级缓存是SessionFactory级别的全局缓存,这在Hibernate应用中非常有益于提高数据库访问效率,但是值得注意的是只有用好了二级缓存才有帮助,不然会导致很多问题,例如:死锁,数据同步等
技术要点:
在使用二级缓存之前,还有一些准备工作。在配置Hibernate二级缓存我们使用了EhCache,需要将Hibernate开发包lib\optional\ehcache目录中的ehcache-..*.jar包类库复制到ClassPath或者是项目的lib目录下。
另外还需要EhCache的配置文件,可以在Hibernate开发包project\etc路径下找到,文件名为ehcache.xml,将文件复制到项目路径下。修改Hibernate配置文件,增加属性代码:
这里我们需要注意的是,指定二级缓存应用到的实体对象需要在指定实体类之后。
1.6 Hibernate事务处理
Hibernate是对JDBC的轻量级对象封装,Hibernate本身是不具备Transaction处理功能的,hibernate的Transaction实际上是底层的JDBC Transaction的封装,或者是JTA Transaction的封装。
1.7 使用复合主键
本节将会介绍复合主键对于Hibernate又是如何进行处理,一般情况下有两种方法进行处理。
可以再建立一个主键类,把表中会成为主键的数据进行持久化处理。这样的话,我们同样需要在主键类中进行重写equals()、hashCode()方法。