一、Hibernate概述
1.概述
(1)JDBC优缺点
* 优点:直接底层操作,提供了很简单、便捷的访问数据库的方法,跨平台性比较强.灵活性比较强,可以写很复杂的SQL语句.
* 缺点: 无法做到面向对象编程.
操作繁琐,重用性低.
批量操作时,频繁与数据库交互,效率极低.
(2)Hibernate解决了什么问题(优点)
* 它实现了面向对象的数据库编程.
* 相比JDBC来说,代码书写简单化了.
* Hibernate提出缓存机制,反问数据的效率得到极大提高.
* Hibernate使用java反射机制,而不是字节码增强程序类实现透明性.
(3)ORM框架-hibernate
* 是一个开源的持久层框架.
* ORM(Object/Relation Mapping)映射工具,建立面向对象的域模型和关系数据模型之间的映射.
* 连接java应用和数据库的中间件.
* 对JDBC进行封装,负责java对象的持久化.
* 在分层结构中处于持久化层,封装对数据库的访问细节,是业务逻辑层更专注于实现业务逻辑.
2.一个简单的Hibernate程序
(1)所需要的lib包
* antlr-....jar: 语言转换工具,hibernate利用它实现Hql到sql的转换.
* common-collections-...jar: collections Apache的工具集,增强java对集合的处理能力.
* dom4j-...jar: xml解析器
* javassist-...GA.jar: 代码生成工具,hibernate用它在运行时扩展java类.
* jta-...jar: 标准的java事务处理接口.
* mysql-connector-jara-..-bin.jar: mysql的连接驱动包.
* slf4j-api-...jar: hibernate使用的一个日志系统.
* log4j.jar
* slf4j-log4jf2.jar:同上.
* hibernate3.jar: 核心包.
(2)写配置文件:hibernate.cfg.xml
写持久化类:例如:Person.java
写持久化类的映射文件:Person.hbm.xml
(3)加载配置文件并获得SessionFactory
private static SessionFactory sessionFactory = null;
static{
Configuration configuration = new Configuration();
//默认加载classpath下的hibernate.cfg.xml,如果在别处请写明path
configuration.configure("com/liu/manytomany/hibernate.cfg.xml");
sessionFactory = configuration.buildSessionFactory();
}
3.Hibernate的运行流程
(1)应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息.
(2)用这些信息生成一个SessionFactory对象.
(3)然后从SessionFactory对象生成一个Session对象.
(4)并用Session对象生成Transaction对象.
* 可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()
等方法对PO进行加载,保存,更新,删除,等操作.
* 在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;
如果没有异常,Transaction对象将提交这些操作到数据库中.
标识符生成器: 描述
Increment: 由hibernate自动以递增的方式生成标识符,每次增量1
Hibernate会先读取 NEWS表中的主键的最大值, 而接下来向 NEWS 表中插入记录时,
就在 max(id) 的基础上递增, 增量为 1.
适用于只有单个 Hibernate 应用进程访问同一个数据库的场合.
Identity: 由底层数据库生成标识符,条件是数据库支持自动增长数据类型
支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase 等.
Sequence: Hibernate根据底层数据库序列生成标识符,条件是数据库支持序列
支持序列的数据库包括:DB2、Oracle等.
Native: 根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo
适合跨数据库平台开发.
Uuid.hex: Hibernate采用128位的UUID算法来生成标识符.
该算法能够在网络环境中生成唯一的字符串标识符,这种策略并不流行,因为字符串类型的
主键比整数类型的主键占用更多的数据库空间.
assigned: 适用于自然主键,由java程序负责生成标识符.不能把setID()声明为private.
使用于主键有一定含义等,一般不推荐使用.
5.持久化对象的状态(三种)
(1)持久化状态(也叫"托管",Persist)
* OID不为null
* 位于Session缓存中
* 持久化对象和数据库中的相关记录对应.
* Session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库.
* 在同一个Session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象.
* Session的许多方法都能出发java对象进入持久化状态:
Session的save()方法把临时对象转变为持久化对象.
Session的load()或get()方法返回的对象总是处于持久化状态.
Session的update()、saveOrUpdate()和lock()方法使游离对象转变为持久化状态.
当一个持久化对象关联一个临时对象,在允许级联保存的情况下,Session在清理缓存时会把这个临时对象也转变为持久化对象.
(2)临时状态(transient)
* 在使用代理主键的情况下,OID通常为null
* 不处于Session的缓存中
* 在数据库中没有对应的记录
* 以下情况,java对象进入临时状态:
当通过new语句刚创建了一个java对象,它处于临时状态,此时不知数据库中的任何记录对应.
(3)游离状态(也叫"脱管",Detached)
* OID不为null
* 不再处于Session的缓存中.
* 一般情况下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录.
* Session的一下方法可以使持久化对象转变成游离对象
调用Session的close()方法时,Session的缓存被清空,缓存中的所有持久化对象都变为游离对象,
如果在应用程序中没有引用变量引用这些游离对象,他们就会结束生命周期.
Session的evict()方法能够从缓存中删除一个持久化对象,使它变为游离状态,当Session的缓存中
保存了大量的持久化对象,会消耗许多内存空间,为了提高性能可以考虑调用evict()方法,从缓存中删除
一些持久化对象.
1.映射一对多关联关系
(1)单向关联
* 仅建立一对多的关联
* 仅建立多对一的关联
* Hibernate使用<many-to-one>元素来映射多对一的关联关系.
* 级联保存和更新:当hibernate持久化一个临时对象时,默认情况下不会自动持久化所关联的其它临时对象,会抛出
TransientObjectException.若设定<many-to-one>的cascade属性为save-update
的话,可以实现自动持久化所关联的对象.
(2)双向关联
* 双向1-n需要在1的一段可以访问n的一端,反之亦然.
* Hibernate使用set元素来映射一对多关联关系.
(3)inverse属性详解
* 来源
在hibernate中通过对inverse属性的值决定是由双向关联的哪一方来维护表的关系.默认为false
inverse=false的为主动方,为true的为被动方,主动方负责维护关联关系.
* 在没有设置inverse=true的情况下,父子两边都维护父子关系.
* 原则:
在1-n关系中,将n方设为主控方将有助于性能改善(例如:主席记住全国人民的名字不可能,反过来则很容易).
如果把1方设为主控方,会额外多出update语句.因此,一般把1的一方设置为true,来提高效率.
(4)使用inverse小结
* 在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,可以提高性能.
* 在建立两个对象的关联时,应该同时修改关联两端的对应属性:
customer.getOrders().add(order);
order.setCustomer(customer);
这样会使程序更加健壮,提高业务逻辑层的独立性.
同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性:
Customer.getOrders().remove(order);
Order.setCustomer(null);
(5)<set>元素的order-by属性
例如:设置order-by属性等于id,则会根据id进行排序.
3.深入Session
(1)session概述
Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存,
更新, 删除和加载Java 对象的方法.
(2)理解session的缓存
* 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存.
只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
* 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,
该对象仍然处于生命周期中。当试图load()对象时,会判断缓存中是否存在该对象,有则返回。没有在查询数据库.
(3)清理session的缓存
* Session具有一个缓存,位于缓存中的对象称为持久化对象,它和数据库中的相关记录对应.
Session能够在某些时间点,按照缓存中的对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程称为清理缓存(flush)
* 默认情况下Session在一下时间点清理缓存:
当应用程序调用Transaction的commit()方法时,该方法先清理缓存(session.flush()),
然后再向数据库提交事务(tx.commit()).
当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先清理缓存,
以保证查询结果能够反映持久化对象的最新状态.
(4)flush,commit,refresh,clear区别
* flush:进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步,执行一些sql语句,但不提交事务.
* commit: 先调用flush()方法,然后提交事务.
* refresh: 刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.
* clear: 清空缓存,等价于list.removeAll();
(5)Session执行批量操作
示例:
for(int i=0; i<10000; i++){
Session.save(object);
}
注意:这样会使一万个对象的缓存全部存在内存中,这样做加大了内存的压力,所以应该定期清理session的缓存,
也就是flush一下,这样内存才能保证足够的空间.
(6)session的产生方式
* sessionFactory.openSession
每次都会新创建一个session,只要新创建一个session,hibernate都会打开应用程序和数据库的连接
所以这种方式效率比较低
* sessionFactory.getCurrentSession
如果当前线程中没有session,先openSession,然后把session存放到当前线程中
从当前线程中得到session
crud操作必须有事务环境
不用手动去close掉
4.session.load与session.get方法
(1)根据需求选择两种方法
* 需求一:用户要取数据库的一张表的一个字段,这个字段很可能就是一个字符,很短.
* 需求二:用户要取数据库的一张表的一个字段,而这个值很可能是blob类型,也许存的是一个很大的视频.
* 上述两个需求,针对数据库中的大数据,不希望特别早的加载到内存中,当用到它的时候才加载.
(2)类的懒加载
* 在默认情况下,类就是执行懒加载
* 只有使用了load方法以后才能用懒加载
* 如果在相应的映射文件中,设置<class>的lazy="false"懒加载将失去效果
(3)集合的懒加载
针对一多对的情况或者多对多的情况
根据一方加载set集合,决定在什么时候给set集合填充数据
* true
在遍历集合中的每一个元素的时候发出sql语句
* false
在得到集合的时候,发出sql语句
* extra
students.size()这个时候用extra仅仅能够得到大小
(4)单端关联(<one-to-one> 或 <many-to-one>)
lazy可选属性: False,proxy, no-proxy
* 对于多对一的<many-to-one>:由于数量特别少,选什么都可以,差别不大
* 对于一对一的<one-to-one>:
proxy:当前对象的単值相关对象只有在调用它的主键外的其他属性的get方法时才加载它,相当于true.
no-proxy:当前对象的単值相关对象只有在调用它的属性时才加载,需要字节码增强.
5.Hibernate检索策略
(1)类级别的检索策略(class的lazy属性,3.x默认为true,2.x默认为false)
* 立即检索:lazy=false
* 延迟检索: lazy=true
* get():总是使用立即检索,没有数据则返回空.
* load(): lazy=true时,延迟检索,调用getXxx()(非getOID())时进行查询,没有数据则抛异常.
lazy=false立即检索,没有数据则抛异常.
(2)关联级别的检索策略
在映射文件中,用<set>元素来配置一对多关联关系,<set>元素有lazy(默认为true)和fetch(默认为select)属性.
* fetch取值join,lazy属性将被忽略,此时策略均为:迫切左外联接检索
* fetch取值select,lazy取值false,此时策略为:立即检索
* fetch取值select,lazy取值true,此时策略为:延迟检索
* fetch取值select,lazy取值extra,此时策略为:延迟检索
* fetch取值subselect,根据lazy取值也分三种情况.
(3)各种检索策略优缺点
* 立即检索
优点:对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象.
缺点:select语句多
可能会加载应用程序不需要访问的对象,浪费许多内存空间
优先考虑使用场合:类级别
应用程序需要立即访问的对象
使用了二级缓存
* 延迟检索
优点:由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。
因此能提高检索性能,并节省内存空间
缺点:应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化.
优先考虑使用场合:一对多或者多对多关联.
应用程序不需要立即访问或者根本不会访问的对象.
* 迫切左外连接检索
优点:对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。
使用了外连接,select语句少.
缺点:可能会加载应用程序不需要访问的对象,浪费内存.
复杂的数据库表连接也会影响检索性能.
优先考虑使用场合:多对一.
需要立即访问的对象.
数据库有良好的表连接性能.
6.一级缓存(session级别的缓存)
(1)一级缓存的生命周期:
一级缓存在session中存放,只要打开session,一级缓存就存在了,当session关闭时,一级缓存就不存在了.
(2)利用session的get,update,save方法可以把数据存放到一级缓存中.
(3)利用session的get方法可以获取一级缓存中的数据.
(4)只要是一个持久化状态的数据就在一级缓存中,利用session.refresh方法,可以同步数据库的数据到一级缓存中,只能同步一个对象.
(5)session.evcit方法可以从一级缓存中清除某一个对象,并把这个对象从持久化状态转为脱管状态(游离)
(6)session.clear()方法可以清空一级缓存的所有对象.
(7)session中存放私有数据,通过新建session和从当前线程中获取session保证数据的安全性.
7.二级缓存(sessionFactory级别的缓存,是外置缓存)
(1)二级缓存是一个共享缓存,存放共享数据.
(2)二级缓存在sessionFactory中,因为sessionFactory本身是线程安全的,所以二级缓存的数据线程也是安全的.
(3)二级缓存的声明周期和sessionFactory一样.
(4)通过session的get,load,update方法都可以把对象放入到二级缓存中.
(5)通过session的get,load方法可以提取二级缓存的数据.
(6)启用二级缓存的条件:
* 很少被修改
* 很多系统模块都要用到
* 不是私有数据,是共享数据.
(7)配置二级缓存
二级缓存供应商有许多,这里我们采用EhCache,此java缓存框架是Hibernate中默认的CacheProvider.
* 首先拷贝ehcache-...jar到当前工程的lib目录下.
* hibernate.cfg.xml配置文件中开启二级缓存
<property name="cache.use_second_level_cache">true</property>
* hibernate.cfg.xml中指定缓存的供应商
<property name="cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
* 指定使用二级缓存的类
方法一:在持久化类的映射文件中配置(*.hbm.xml)
<class name="com.liu.Customer" table="customer" lazy="false">
<!-- 配置类级别的二级缓存 -->
<cache usage="read-write"/>
....
<set name="orders" table="orders" inverse="true">
<!-- 配置集合级别的二级缓存-->
<cache usage="read-write"/>
</class>
方法二:在hibernate.cfg.xml文件中配置(建议使用)
<!-- 指定使用二级缓存的类放在mapping下面 -->
<!-- 配置类级别的二级缓存 -->
<class-cache class="com.liu.Customer" usage="read-write"/>
<class-cache class="com.liu.Order" usage="read-write"/>
<!-- 配置集合级别的二级缓存 -->
<collection-cache collection="com.liu.Customer.orders" usage="read-write"/>
把二级缓存的数据放在磁盘上: 在classpath下存放一个文件ehcache.xml
8.常用方法
(1)session.get 或session.load
* 把一个对象变成持久化状态的对象.
* 把该对象存到session的一级缓存中.
* 把该对象存到二级缓存中.
* session.clear清空一级和二级缓存.
(2)session.update
* 也会把对象存到二级缓存
(3)session.save
* 该方法只把对象放入到一级缓存中,并没有放入到二级缓存中.
* 在执行session.flush的时候,把数据保存到二级缓存中.
(4)查询缓存
* 查询缓存是建立在二级缓存基础之上的.
* 查询缓存不是默认开启的,需要在hibernate.cfg.xml中设置
<property name="cache.use_second_level_cache">true</property>
* 在代码中开启:query.setCacheable(true);
(5)无论list,load还是iterate,只要读出一个对象,都会填充缓存。
但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,
如果在缓存里面有,就从缓存取,没有的话就去数据库load
1.概述
(1)JDBC优缺点
* 优点:直接底层操作,提供了很简单、便捷的访问数据库的方法,跨平台性比较强.灵活性比较强,可以写很复杂的SQL语句.
* 缺点: 无法做到面向对象编程.
操作繁琐,重用性低.
批量操作时,频繁与数据库交互,效率极低.
(2)Hibernate解决了什么问题(优点)
* 它实现了面向对象的数据库编程.
* 相比JDBC来说,代码书写简单化了.
* Hibernate提出缓存机制,反问数据的效率得到极大提高.
* Hibernate使用java反射机制,而不是字节码增强程序类实现透明性.
(3)ORM框架-hibernate
* 是一个开源的持久层框架.
* ORM(Object/Relation Mapping)映射工具,建立面向对象的域模型和关系数据模型之间的映射.
* 连接java应用和数据库的中间件.
* 对JDBC进行封装,负责java对象的持久化.
* 在分层结构中处于持久化层,封装对数据库的访问细节,是业务逻辑层更专注于实现业务逻辑.
2.一个简单的Hibernate程序
(1)所需要的lib包
* antlr-....jar: 语言转换工具,hibernate利用它实现Hql到sql的转换.
* common-collections-...jar: collections Apache的工具集,增强java对集合的处理能力.
* dom4j-...jar: xml解析器
* javassist-...GA.jar: 代码生成工具,hibernate用它在运行时扩展java类.
* jta-...jar: 标准的java事务处理接口.
* mysql-connector-jara-..-bin.jar: mysql的连接驱动包.
* slf4j-api-...jar: hibernate使用的一个日志系统.
* log4j.jar
* slf4j-log4jf2.jar:同上.
* hibernate3.jar: 核心包.
(2)写配置文件:hibernate.cfg.xml
写持久化类:例如:Person.java
写持久化类的映射文件:Person.hbm.xml
(3)加载配置文件并获得SessionFactory
private static SessionFactory sessionFactory = null;
static{
Configuration configuration = new Configuration();
//默认加载classpath下的hibernate.cfg.xml,如果在别处请写明path
configuration.configure("com/liu/manytomany/hibernate.cfg.xml");
sessionFactory = configuration.buildSessionFactory();
}
3.Hibernate的运行流程
(1)应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息.
(2)用这些信息生成一个SessionFactory对象.
(3)然后从SessionFactory对象生成一个Session对象.
(4)并用Session对象生成Transaction对象.
* 可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()
等方法对PO进行加载,保存,更新,删除,等操作.
* 在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;
如果没有异常,Transaction对象将提交这些操作到数据库中.
(5)运行流程图如下:
标识符生成器: 描述
Increment: 由hibernate自动以递增的方式生成标识符,每次增量1
Hibernate会先读取 NEWS表中的主键的最大值, 而接下来向 NEWS 表中插入记录时,
就在 max(id) 的基础上递增, 增量为 1.
适用于只有单个 Hibernate 应用进程访问同一个数据库的场合.
Identity: 由底层数据库生成标识符,条件是数据库支持自动增长数据类型
支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase 等.
Sequence: Hibernate根据底层数据库序列生成标识符,条件是数据库支持序列
支持序列的数据库包括:DB2、Oracle等.
Native: 根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo
适合跨数据库平台开发.
Uuid.hex: Hibernate采用128位的UUID算法来生成标识符.
该算法能够在网络环境中生成唯一的字符串标识符,这种策略并不流行,因为字符串类型的
主键比整数类型的主键占用更多的数据库空间.
assigned: 适用于自然主键,由java程序负责生成标识符.不能把setID()声明为private.
使用于主键有一定含义等,一般不推荐使用.
5.持久化对象的状态(三种)
(1)持久化状态(也叫"托管",Persist)
* OID不为null
* 位于Session缓存中
* 持久化对象和数据库中的相关记录对应.
* Session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库.
* 在同一个Session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象.
* Session的许多方法都能出发java对象进入持久化状态:
Session的save()方法把临时对象转变为持久化对象.
Session的load()或get()方法返回的对象总是处于持久化状态.
Session的update()、saveOrUpdate()和lock()方法使游离对象转变为持久化状态.
当一个持久化对象关联一个临时对象,在允许级联保存的情况下,Session在清理缓存时会把这个临时对象也转变为持久化对象.
(2)临时状态(transient)
* 在使用代理主键的情况下,OID通常为null
* 不处于Session的缓存中
* 在数据库中没有对应的记录
* 以下情况,java对象进入临时状态:
当通过new语句刚创建了一个java对象,它处于临时状态,此时不知数据库中的任何记录对应.
(3)游离状态(也叫"脱管",Detached)
* OID不为null
* 不再处于Session的缓存中.
* 一般情况下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录.
* Session的一下方法可以使持久化对象转变成游离对象
调用Session的close()方法时,Session的缓存被清空,缓存中的所有持久化对象都变为游离对象,
如果在应用程序中没有引用变量引用这些游离对象,他们就会结束生命周期.
Session的evict()方法能够从缓存中删除一个持久化对象,使它变为游离状态,当Session的缓存中
保存了大量的持久化对象,会消耗许多内存空间,为了提高性能可以考虑调用evict()方法,从缓存中删除
一些持久化对象.
(4)Hibernate对象状态转化图
1.映射一对多关联关系
(1)单向关联
* 仅建立一对多的关联
* 仅建立多对一的关联
* Hibernate使用<many-to-one>元素来映射多对一的关联关系.
* 级联保存和更新:当hibernate持久化一个临时对象时,默认情况下不会自动持久化所关联的其它临时对象,会抛出
TransientObjectException.若设定<many-to-one>的cascade属性为save-update
的话,可以实现自动持久化所关联的对象.
(2)双向关联
* 双向1-n需要在1的一段可以访问n的一端,反之亦然.
* Hibernate使用set元素来映射一对多关联关系.
(3)inverse属性详解
* 来源
在hibernate中通过对inverse属性的值决定是由双向关联的哪一方来维护表的关系.默认为false
inverse=false的为主动方,为true的为被动方,主动方负责维护关联关系.
* 在没有设置inverse=true的情况下,父子两边都维护父子关系.
* 原则:
在1-n关系中,将n方设为主控方将有助于性能改善(例如:主席记住全国人民的名字不可能,反过来则很容易).
如果把1方设为主控方,会额外多出update语句.因此,一般把1的一方设置为true,来提高效率.
(4)使用inverse小结
* 在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,可以提高性能.
* 在建立两个对象的关联时,应该同时修改关联两端的对应属性:
customer.getOrders().add(order);
order.setCustomer(customer);
这样会使程序更加健壮,提高业务逻辑层的独立性.
同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性:
Customer.getOrders().remove(order);
Order.setCustomer(null);
(5)<set>元素的order-by属性
例如:设置order-by属性等于id,则会根据id进行排序.
(6)cascade属性一览
3.深入Session
(1)session概述
Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存,
更新, 删除和加载Java 对象的方法.
(2)理解session的缓存
* 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存.
只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
* 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,
该对象仍然处于生命周期中。当试图load()对象时,会判断缓存中是否存在该对象,有则返回。没有在查询数据库.
(3)清理session的缓存
* Session具有一个缓存,位于缓存中的对象称为持久化对象,它和数据库中的相关记录对应.
Session能够在某些时间点,按照缓存中的对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程称为清理缓存(flush)
* 默认情况下Session在一下时间点清理缓存:
当应用程序调用Transaction的commit()方法时,该方法先清理缓存(session.flush()),
然后再向数据库提交事务(tx.commit()).
当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先清理缓存,
以保证查询结果能够反映持久化对象的最新状态.
(4)flush,commit,refresh,clear区别
* flush:进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步,执行一些sql语句,但不提交事务.
* commit: 先调用flush()方法,然后提交事务.
* refresh: 刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.
* clear: 清空缓存,等价于list.removeAll();
(5)Session执行批量操作
示例:
for(int i=0; i<10000; i++){
Session.save(object);
}
注意:这样会使一万个对象的缓存全部存在内存中,这样做加大了内存的压力,所以应该定期清理session的缓存,
也就是flush一下,这样内存才能保证足够的空间.
(6)session的产生方式
* sessionFactory.openSession
每次都会新创建一个session,只要新创建一个session,hibernate都会打开应用程序和数据库的连接
所以这种方式效率比较低
* sessionFactory.getCurrentSession
如果当前线程中没有session,先openSession,然后把session存放到当前线程中
从当前线程中得到session
crud操作必须有事务环境
不用手动去close掉
4.session.load与session.get方法
(1)根据需求选择两种方法
* 需求一:用户要取数据库的一张表的一个字段,这个字段很可能就是一个字符,很短.
* 需求二:用户要取数据库的一张表的一个字段,而这个值很可能是blob类型,也许存的是一个很大的视频.
* 上述两个需求,针对数据库中的大数据,不希望特别早的加载到内存中,当用到它的时候才加载.
(2)类的懒加载
* 在默认情况下,类就是执行懒加载
* 只有使用了load方法以后才能用懒加载
* 如果在相应的映射文件中,设置<class>的lazy="false"懒加载将失去效果
(3)集合的懒加载
针对一多对的情况或者多对多的情况
根据一方加载set集合,决定在什么时候给set集合填充数据
* true
在遍历集合中的每一个元素的时候发出sql语句
* false
在得到集合的时候,发出sql语句
* extra
students.size()这个时候用extra仅仅能够得到大小
(4)单端关联(<one-to-one> 或 <many-to-one>)
lazy可选属性: False,proxy, no-proxy
* 对于多对一的<many-to-one>:由于数量特别少,选什么都可以,差别不大
* 对于一对一的<one-to-one>:
proxy:当前对象的単值相关对象只有在调用它的主键外的其他属性的get方法时才加载它,相当于true.
no-proxy:当前对象的単值相关对象只有在调用它的属性时才加载,需要字节码增强.
5.Hibernate检索策略
(1)类级别的检索策略(class的lazy属性,3.x默认为true,2.x默认为false)
* 立即检索:lazy=false
* 延迟检索: lazy=true
* get():总是使用立即检索,没有数据则返回空.
* load(): lazy=true时,延迟检索,调用getXxx()(非getOID())时进行查询,没有数据则抛异常.
lazy=false立即检索,没有数据则抛异常.
(2)关联级别的检索策略
在映射文件中,用<set>元素来配置一对多关联关系,<set>元素有lazy(默认为true)和fetch(默认为select)属性.
* fetch取值join,lazy属性将被忽略,此时策略均为:迫切左外联接检索
* fetch取值select,lazy取值false,此时策略为:立即检索
* fetch取值select,lazy取值true,此时策略为:延迟检索
* fetch取值select,lazy取值extra,此时策略为:延迟检索
* fetch取值subselect,根据lazy取值也分三种情况.
(3)各种检索策略优缺点
* 立即检索
优点:对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象.
缺点:select语句多
可能会加载应用程序不需要访问的对象,浪费许多内存空间
优先考虑使用场合:类级别
应用程序需要立即访问的对象
使用了二级缓存
* 延迟检索
优点:由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。
因此能提高检索性能,并节省内存空间
缺点:应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化.
优先考虑使用场合:一对多或者多对多关联.
应用程序不需要立即访问或者根本不会访问的对象.
* 迫切左外连接检索
优点:对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。
使用了外连接,select语句少.
缺点:可能会加载应用程序不需要访问的对象,浪费内存.
复杂的数据库表连接也会影响检索性能.
优先考虑使用场合:多对一.
需要立即访问的对象.
数据库有良好的表连接性能.
6.一级缓存(session级别的缓存)
(1)一级缓存的生命周期:
一级缓存在session中存放,只要打开session,一级缓存就存在了,当session关闭时,一级缓存就不存在了.
(2)利用session的get,update,save方法可以把数据存放到一级缓存中.
(3)利用session的get方法可以获取一级缓存中的数据.
(4)只要是一个持久化状态的数据就在一级缓存中,利用session.refresh方法,可以同步数据库的数据到一级缓存中,只能同步一个对象.
(5)session.evcit方法可以从一级缓存中清除某一个对象,并把这个对象从持久化状态转为脱管状态(游离)
(6)session.clear()方法可以清空一级缓存的所有对象.
(7)session中存放私有数据,通过新建session和从当前线程中获取session保证数据的安全性.
7.二级缓存(sessionFactory级别的缓存,是外置缓存)
(1)二级缓存是一个共享缓存,存放共享数据.
(2)二级缓存在sessionFactory中,因为sessionFactory本身是线程安全的,所以二级缓存的数据线程也是安全的.
(3)二级缓存的声明周期和sessionFactory一样.
(4)通过session的get,load,update方法都可以把对象放入到二级缓存中.
(5)通过session的get,load方法可以提取二级缓存的数据.
(6)启用二级缓存的条件:
* 很少被修改
* 很多系统模块都要用到
* 不是私有数据,是共享数据.
(7)配置二级缓存
二级缓存供应商有许多,这里我们采用EhCache,此java缓存框架是Hibernate中默认的CacheProvider.
* 首先拷贝ehcache-...jar到当前工程的lib目录下.
* hibernate.cfg.xml配置文件中开启二级缓存
<property name="cache.use_second_level_cache">true</property>
* hibernate.cfg.xml中指定缓存的供应商
<property name="cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
* 指定使用二级缓存的类
方法一:在持久化类的映射文件中配置(*.hbm.xml)
<class name="com.liu.Customer" table="customer" lazy="false">
<!-- 配置类级别的二级缓存 -->
<cache usage="read-write"/>
....
<set name="orders" table="orders" inverse="true">
<!-- 配置集合级别的二级缓存-->
<cache usage="read-write"/>
</class>
方法二:在hibernate.cfg.xml文件中配置(建议使用)
<!-- 指定使用二级缓存的类放在mapping下面 -->
<!-- 配置类级别的二级缓存 -->
<class-cache class="com.liu.Customer" usage="read-write"/>
<class-cache class="com.liu.Order" usage="read-write"/>
<!-- 配置集合级别的二级缓存 -->
<collection-cache collection="com.liu.Customer.orders" usage="read-write"/>
把二级缓存的数据放在磁盘上: 在classpath下存放一个文件ehcache.xml
8.常用方法
(1)session.get 或session.load
* 把一个对象变成持久化状态的对象.
* 把该对象存到session的一级缓存中.
* 把该对象存到二级缓存中.
* session.clear清空一级和二级缓存.
(2)session.update
* 也会把对象存到二级缓存
(3)session.save
* 该方法只把对象放入到一级缓存中,并没有放入到二级缓存中.
* 在执行session.flush的时候,把数据保存到二级缓存中.
(4)查询缓存
* 查询缓存是建立在二级缓存基础之上的.
* 查询缓存不是默认开启的,需要在hibernate.cfg.xml中设置
<property name="cache.use_second_level_cache">true</property>
* 在代码中开启:query.setCacheable(true);
(5)无论list,load还是iterate,只要读出一个对象,都会填充缓存。
但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,
如果在缓存里面有,就从缓存取,没有的话就去数据库load