因为项目要用到Hibernate,好长时间没有直接使用过了,看了一些资料,总结了一些知识点记在这里。主要来自官方文档(3.6.10),也有一些来自网络文章。
get和load的最大区别是,如果在缓存中没有找到相应的对象,get将会直接访问数据库并返回一个完全初始化好的对象,而这个过程有可能会涉及到多个数据库调用;而load方法在缓存中没有发现对象的情况下,只会返回一个代理对象,只有在对象getId()之外的其它方法被调用时才会真正去访问数据库,这样就能在某些情况下大幅度提高性能。
save()、saveOrUpdate()和persist()都是用于将对象保存到数据库中的方法,但其中有些细微的差别。例如,save()只能INSERT记录,但是saveOrUpdate()可以进行 记录的INSERT和UPDATE。还有,save()的返回值是一个Serializable对象,而persist()方法返回值为void。
SessionFactory就是一个用于创建Hibernate的Session对象的工厂。SessionFactory通常是在应用启动时创建好的,应用程序中的代码用它来获得Session对象。作为一个单个的数据存储,它也是 线程安全的,所以多个线程可同时使用同一个SessionFactory。Java JEE应用一般只有一个SessionFactory,服务于客户请求的各线程都通过这个工厂来获得Hibernate的Session实例,这也是为什么SessionFactory接口的实现必须是线程安全的原因。还有,SessionFactory的内部状态包含着同对象关系影射有关的所有元数据,它是 不可变的,一旦创建好后就不能对其进行修改了。
Session代表着Hibernate所做的一小部分工作,它负责维护者同数据库的链接而且不是线程安全的,也就是说,Hibernage中的Session不能在多个线程间进行共享。虽然Session会以主动滞后的方式获得数据库连接,但是Session最好还是在用完之后立即将其关闭。
Hibernate 支持两个第三方的开源 JDBC 连接池:c3p0 和 proxool。
Hibernate 的SessionFactory —
SessionFactory sessions = cfg.buildSessionFactory();
一个关联于特定数据库全局的工厂(factory),这个工厂将被应用程序的所有线程共享。如果你要使用多个数据库,就要用多个的 <session-factory>,通常把它们放在多个配置文件中
SessionFactory 可以创建并打开新的 Session。一个 Session 代表一个单线程的单元操作,org.hibernate.SessionFactory 则是个线程安全的全局对象,只需要被实例化一次。
org.hibernate.Session 在第一次被使用的时候,即第一次调用 getCurrentSession() 的时候,其生命周期就开始。然后它被 Hibernate 绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate 会自动把 org.hibernate.Session 从当前线程剥离,并且关闭它。假若你再次调用 getCurrentSession(),你会得到一个新的 org.hibernate.Session,并且开始一个新的工作单元。
所有的持久化类(persistent classes)都要求有无参的构造器,因为 Hibernate 必须使用 Java 反射机制来为你创建对象。
当处理映射文件时,Hibernate 用反射(reflection)来决定这个映射类型。这需要时间和资源,所以如果你注重启动性能,你应该考虑显性地定义所用的类型。
Hibernate 支持各种各样的集合映射,<set> 使用的最为普遍。对于多对多关联(或叫 n:m 实体关系), 需要一个关联表(association table)。表里面的每一行代表从 person 到 event 的一个关联。表名是由 set 元素的 table 属性配置的。关联里面的标识符字段名,对于 person 的一端,是由 <key> 元素定义,而 event 一端的字段名是由 <many-to-many> 元素的 column 属性定义。
在查询类别对应的表格时,需注意到继承的问题,Hibernate会自动判定继承关系,如果查询的类别是某类别的父类别,则会返回与父类别、子类别对应的所有表格数据,例如如果查询java.lang.Object,由于Object在Java中是所有类别的父类别,所以下面这个查询会返回数据库中所有表格的数据:
Query query = session.createQuery("from java.lang.Object");
关于缓存
Hibernate缓存分为两级:一级缓存和二级缓存。查询时实现过程为:一级缓存-->二级缓存-->数据库,查询速度依次降低。
一级缓存为Hibernate默认带,不能卸载存在于session中。二级缓存由sessionFactory控制的进程级缓存,由全局共享。必须使用相应方法才能从缓存中获取数据,如Query.iterate()、load、get方法等。注意:session.find方法直接从库中获取数据,不读缓存。
hibernate会自行维护二级缓存中的数据,以保证缓存中的数据和数据库中的真实数据的一致性。删除、更新、增加数据的时候,同时更新缓存。
只要是调用hibernate API执行数据库相关的工作。hibernate都会为你自动保证 缓存数据的有效性!!
但是,如果你使用了JDBC绕过hibernate直接执行对数据库的操作。此时,Hibernate不会/也不可能自行感知到数据库被进行的变化改动,也就不能再保证缓存中数据的有效性!!
如果数据表数据量特别巨大,此时不适合于二级缓存。原因是缓存的数据量过大可能会引起内存资源紧张,反而降低性能。
如果数据表数据量特别巨大,但是经常使用的往往只是较新的那部分数据。此时,也可为其配置二级缓存。但是必须单独配置其持久化类的缓存策略,比如最大缓存数、缓存过期时间等,将这些参数降低至一个合理的范围(太高会引起内存资源紧张,太低了缓存的意义不大)。
财务数据等是非常重要的数据,绝对不允许出现或使用无效的数据,所以此时为了安全起见最好不要使用二级缓存。因为此时 “正确性”的重要性远远大于 “高性能”的重要性。
抓取策略(Fetching strategies)
当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate 使用 抓取策略(fetching strategy) 获取关联对象。抓取策略可以在 O/R 映射的元数据中声明,也可以在特定的 HQL 或条件查询(Criteria Query)中重载声明。
Hibernate3 定义了如下几种抓取策略:
连接抓取(Join fetching):Hibernate 通过在 SELECT 语句使用 OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。
查询抓取(Select fetching):另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。除非你显式的指定 lazy="false" 禁止 延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条 select 语句。
子查询抓取(Subselect fetching):另外发送一条 SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定 lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条 select 语句。
批量抓取(Batch fetching):对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate 使用单条 SELECT 语句获取一批对象实例或集合。
Hibernate 会区分下列各种情况:
Immediate fetching,立即抓取:当宿主被加载时,关联、集合或属性被立即抓取。
Lazy collection fetching,延迟集合抓取:直到应用程序对集合进行了一次操作时,集合才被抓取(对集合而言这是默认行为)。
"Extra-lazy" collection fetching,"Extra-lazy" 集合抓取:对集合类中的每个元素而言,都是直到需要时才去访问数据库。除非绝对必要,Hibernate 不会试图去把整个集合都抓取到内存里来(适用于非常大的集合)。
Proxy fetching,代理抓取:对返回单值的关联而言,当其某个方法被调用,而非对其关键字进行 get 操作时才抓取。
"No-proxy" fetching,非代理抓取:对返回单值的关联而言,当实例变量被访问的时候进行抓取。与上面的代理抓取相比,这种方法没有那么“延迟”得厉害(就算只访问标识符,也会导致关联抓取)但是更加透明,因为对应用程序来说,不再看到 proxy。这种方法需要在编译期间进行字节码增强操作,因此很少需要用到。
Lazy attribute fetching,属性延迟加载:对属性或返回单值的关联而言,当其实例变量被访问的时候进行抓取。需要编译期字节码强化,因此这一方法很少是必要的。
这里有两个正交的概念:关联何时被抓取,以及被如何抓取(会采用什么样的 SQL 语句)。注意不要混淆它们。我们使用抓取来改善性能。我们使用延迟来定义一些契约,对某特定类的某个脱管的实例,知道有哪些数据是可以使用的。
21.1.1. 操作延迟加载的关联
默认情况下,Hibernate 3 对集合使用延迟 select 抓取,对返回单值的关联使用延迟代理抓取。对几乎是所有的应用而言,其绝大多数的关联,这种策略都是有效的。
假若你设置了 hibernate.default_batch_fetch_size,Hibernate 会对延迟加载采取批量抓取优化措施(这种优化也可能会在更细化的级别打开)。
然而,你必须了解延迟抓取带来的一个问题。在一个打开的 Hibernate session 上下文之外调用延迟集合会导致一次意外。比如:
s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts"); // Error!
在 Session 关闭后,permessions 集合将是未实例化的、不再可用,因此无法正常载入其状态。 Hibernate 对脱管对象不支持延迟实例化。这里的修改方法是将 permissions 读取数据的代码移到事务提交之前。
除此之外,通过对关联映射指定 lazy="false",我们也可以使用非延迟的集合或关联。但是,对绝大部分集合来说,更推荐使用延迟方式抓取数据。如果在你的对象模型中定义了太多的非延迟关联,Hibernate 最终几乎需要在每个事务中载入整个数据库到内存中。
但是,另一方面,在一些特殊的事务中,我们也经常需要使用到连接抓取(它本身上就是非延迟的),以代替查询抓取。 下面我们将会很快明白如何具体的定制 Hibernate 中的抓取策略。在 Hibernate3 中,具体选择哪种抓取策略的机制是和选择 单值关联或集合关联相一致的。
调整抓取策略(Tuning fetch strategies)
查询抓取(默认的)在 N+1 查询的情况下是极其脆弱的,因此我们可能会要求在映射文档中定义使用连接抓取:
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set
<many-to-one name="mother" class="Cat" fetch="join"/>
在映射文档中定义的抓取策略将会对以下列表条目产生影响:
通过 get() 或 load() 方法取得数据。
只有在关联之间进行导航时,才会隐式的取得数据。
条件查询
使用了 subselect 抓取的 HQL 查询
不管你使用哪种抓取策略,定义为非延迟的类图会被保证一定装载入内存。注意这可能意味着在一条 HQL 查询后紧跟着一系列的查询。
通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中, 使用 HQL 的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。在条件查询 API 中,应该调用 setFetchMode(FetchMode.JOIN)语句。
也许你喜欢仅仅通过条件查询,就可以改变 get() 或 load() 语句中的数据抓取策略。例如:
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
这就是其他 ORM 解决方案的“抓取计划(fetch plan)”在 Hibernate 中的等价物。
截然不同的一种避免 N+1 次查询的方法是,使用二级缓存。
分享到:
相关推荐
Hibernate框架知识点的梳理;对HQL检索、检索策略、以及如何配置详细的进行描述
本文档适用于J2EE初学者,主要是针对hibernate部分的知识点进行总结,有助于初学者迅速入门
Hibernate知识点总结,自己学习hibernate所坐的笔记,希望能帮助更多的朋友
hibernate主要知识点归纳,学习hibernate时自己总结的,还不错的东西,给大家分享下
hibernate知识点的总结,适合初学者
总结了hibernate的所有知识,帮助很大的。
该资源总结了java中常见的三大开源框架的知识点,便于初学者学习。
自己总结的一些hibernate的知识点
java面试知识点总结--Hibernate、ibates、strucs.pdf
本文档总结了有关hibernate面试的知识点
老师精心总结的关于面试过程中有可能遇到的hibernate的面试题 关于hibernate的总结,用于个人学习和面试之用
总结的Hibernate全部知识点,方便复习和使用!希望对大家有帮助
SSM知识点总结,包括Hibernate以及Mybatis的区别整理。供大家借鉴
Hibernate的一些学习知识点总结PPT等资源,适合初学者
Hibernate框架的详细总结,里面有该框架的知识点和每个注意事项
Hibernate知识总结(从入门到精通),是我自己一点一点看视屏总结出来的,对于很多比较细小的知识点都总结到位,很适合刚刚入门的新手朋友,也适合各位进行温故知新~
一、应用程序的分层结构 二、hibernate数据持久化组件 三、持久化类 四、把持久化类映射到表 五、持久化对象(Persistent Object) 六、Hibernate中实体关系的处理 七、Hibernate知识点总结 八、Hibernate控制的事务
hibernate 中对象id的生成方式 对象---关系映射文件 hibernate中对象的状态及数据类型 基数类型的对象关系映射:(重点,难点) SQL语句 配置文件 组件关系映射: 继承关系映射 值类型的集合影射: 一些小知识点
本课程的讲解者李勇老师的技术功底非常深厚,课程内容组织得非常合理和巧妙,知识点的讲解也很细腻和透彻,总结了许多独有的经验,许多有经验的开发人员听完李勇老师的Hibernate课程后,都有一种恍然大悟的感慨。
话题一:hibernate的基本使用 话题二: hibernate 中对象id的生成方式: 话题三:hibernate中对象的状态及数据类型 话题四:基数类型的对象关系映射:(重点,难点) 话题五:组件关系映射:(一...话题八:一些小知识点