Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
Hibernate有五个重要的接口组成,分别是:Session session接口负责Hibernate与数据库的原始操作,即通过SQL语句来完成的一种交互,session是非线程安全的。
SessionFactory:负责初始化Hibernate,它作为数据源的代理,用来创建Session,如果多个数据源的话则需要创建多个SessionFactory。
Configration:这个接口主要负责初始化Hibernate,通过读取映射文件和配置文件初始化SessionFactory。
Transaction:这个接口的主要任务是负责对事物的处理。
Query和Crietira:是查询接口,提供HQL和SQL的查询服务。
Hibernate中几个源码包的介绍:net.sf.hibernate.*;主要的接口类异常处理类;net.sf.hibernate.cache;主要的缓存类;net.sf.hibernate.cfg; 初始化文件
net.sf.hibernate.collection;Hibernate的各种集合类,自己来实现延迟加载的功能;
net.sf.hibernate.connection.* 几个数据库连接池的Provider
net.sf.hibernate.dialect.* 支持多种数据库特性,每个Dialect实现类代表一种数据库,描述了该数据库支持的数据类型和其它特点,例如是否有AutoIncrement,是否有Sequence,是否有分页sql等等
net.sf.hibernate. eg.* Hibernate文档中用到的例子;
net.sf.hibernate.engine.* 这个包的类作用比较散
net.sf.hibernate.expression.* HQL支持的表达式
net.sf.hibernate.hq.* HQL实现
net.sf.hibernate. id.* ID生成器
net.sf.hibernate.impl.* 最核心的包,一些重要接口的实现类,如果Session,SessionFactory,Query等
net.sf.hibernate.jca.* JCA支持,把Session包装为支持JCA的接口实现类
net.sf.hibernate.jmx.* 我不懂JMX,只知道JMX是用来编写App Server的管理程序的,大概是JMX部分接口的实现,使得App Server可以通过JMX接口管理Hibernate
net.sf.hibernate.loader.* 也是很核心的包,主要是生成sql语句的
net.sf.hibernate.lob.* Blob和Clob支持
net.sf.hibernate.mapping.* hbm文件的属性实现
net.sf.hibernate.metadata.* PO的Meta实现
net.sf.hibernate.odmg.* ODMG是一个ORM标准,这个包是ODMG标准的实现类
net.sf.hibernate.persister.* 核心包,实现持久对象和表之间的映射
net.sf.hibernate.proxy.* Proxy和Lazy Loading支持
net.sf.hibernate. ps.* 该包是PreparedStatment Cache
net.sf.hibernate.sql.* 生成JDBC sql语句的包
net.sf.hibernate.test.* 测试类,你可以用junit来测试Hibernate
net.sf.hibernate.tool.hbm2ddl.* 用hbm配置文件生成DDL
net.sf.hibernate.transaction.* Hibernate Transaction实现类
net.sf.hibernate.type.* Hibernate中定义的持久对象的属性的数据类型
net.sf.hibernate.util.* 一些工具类,作用比较散
net.sf.hibernate.xml.* XML数据绑定
Hibernate缓存技术介绍
Hibernate的缓存是分为两级,第一级别的缓存是Session级别的缓存,属于事物范围内的缓存。第二级别的缓存是属于进程范围内或者集群的缓存,这一个级别的缓存可以动态的加载卸载。
第一级别的缓存,session级别的缓存,每个事物都在一个进程或者集群范围内,每个事物被不同进程调用的时候都会有自己的一个缓存,不会并发,所以这一个级别的缓存是不需要考虑并发的。启用一级缓存后,程序通过Session接口进行数据库操作的同时,一级缓存会把对应数据库中的对象存放到缓存中,当缓存中数量达到一定的值时候就需要清空一些缓存,清空缓存有很多策略,比如设置一个最大的缓存对象数量,如果超过此数量则清空缓存。
当应用程序调用Session的save()、update()、saveOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。
第二级别的缓存,不同进程共享的数据或者是集群不同服务器处理后的共享数据,什么样的数据适合用于第二级别的缓存呢,就是那些经常被读但是一般不会更改的数据,比如些常量数据,就可以放到二级缓存,管理二级缓存有两个方面比较主要,一是选择缓存适配器,二定义好缓存过期策略。
二级缓存的应用举例:比如我查询 select * from user where user_name like '%James%'
把所有对象根据user_id放入到二级中去,如果应用程序查找有关对象,它会现在一级缓存中查询,如果查询不到,它会在2级缓存中进行查询,查询出来的结果如果存在就从缓存中直接读取,如果不存在就把相应的对象放入缓存当中去。但是二级缓存有个问题,对于条件查询没有用处,因为每次查询的条件一般都不同,我们可以用QueryCache,
什么样的数据适合存放到第二级缓存中? 1 很少被修改的数据 2 不是很重要的数据,允许出现偶尔并发的数据 3 不会被并发访问的数据 4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。
不适合放到二级缓存的数据,并发的数据,财务数据,与其他程序的共享数据。
一般采用的Hibernate的二级缓存插件为org.hibernate.cache.EhCacheProvider,因为它提供了JVM级别的缓存设置。
Hibernate的延迟加载功能
延迟加载顾名思义就是指在LOAD的时候不进行对象的加载,即不往内存中写入对象,而是在程序真正调用该对象的时候才加载对象到内存中去。延迟加载技术也有个缺陷,在DAO模式中,如果采用延迟加载策略,则必须保证Sesssion会话一直保持开着的状态,这样才能保证延迟加载成功,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor,通过两个类我们可以设置在应用程序请求与数据库连接的时候打开SESSION并在完成逻辑视图的时候关闭Session。这两种分别配置WEB.XML或者Spring的拦截器即可。
Hibernate的持久化过程
Hibernate的数据分为三种状态,瞬时状态就是放入内存的数据,一般也就是NEW出来的对象实例,保存在内存中如果程序退出就会被JVM回收掉,可以通过Hibernate的SAVAE或者saveorupadte的方法来把他们存储到数据库中,持久化。
持久化状态,持久化状态是该对象已经在数据库中有一条记录与之对应,那么我们管这种状态叫做持久态,如果调用delete方法此时变为瞬时状态,如果调用clear或者evict方法的话则进入托管状态。
托管状态是这样的,虽然内存中的数据已经在数据库有相应的记录但是如果对其修改不会更改数据库中相应的数据状态。
脱管对象具有如下特点:
1. 本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收;
2. 比瞬时对象多了一个数据库记录标识值。
Hibernate的使用步骤
1.读取并解析配置文件
2.读取并解析映射信息,创建SessionFactory
3.打开Sesssion
4.创建事务Transation
5.持久化操作
6.提交事务
7.关闭Session
8.关闭SesstionFactory
Hibernate的主键生成机制
1) assigned
主键由外部程序负责生成,无需Hibernate参与。
2) hilo
通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。
3) seqhilo
与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle。
4) increment
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。
5) identity
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。
6) sequence
采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。
7) native
由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
8) uuid.hex
由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
9) uuid.string
与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。
10) foreign
使用外部表的字段作为主键。一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。
这10中生成OID标识符的方法,increment 比较常用,把标识符生成的权力交给Hibernate处理.但是当同时多个Hibernate应用操作同一个数据库,甚至同一张表的时候.就推荐使用identity 依赖底层数据库实现,但是数据库必须支持自动增长,当然针对不同的数据库选择不同的方法.如果你不能确定你使用的数据库具体支持什么的情况下.可以选择用native 让Hibernate来帮选择identity,sequence,或hilo.
另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。
不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制