Hibernate
1.Hibernate简介?
Hibernate是一个开源的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用面向对象编程思想来操纵数据库。
2.Hibernate工作原理?及为什么要用?
Hibernate是一个JDO工具。它的工作原理是通过文件把值对象和数据库表之间建立起一个映射关系,这样,我们只需要通过操作这些值对象和Hibernate提供的一些基本类,就可以达到使用数据库的目的。
1)对JDBC操作提供封装,方便操作简化数据库访问的代码,使用面向对象的方式操作DAO;
2)简化DAO层的代码量ORM从关系型DB到面向对象(java)的转变 ;
3)在java代码中充斥着大量的sql语句不便于维护,但是ORM映射可以减少此类代码,便于维护 ;
3.Hibernate的优点和缺点?
⑴Hibernate的优点:
① Hibernate使用Java反射机制,而不是字节码增强程序来实现透明性;
② Hibernate的性能非常好,因为它是一个轻量级框架。映射的灵活性很出色;
③ 它支持多种关系数据库,从一对一到多对多的各种复杂关系;
(2)Hibernate的缺点:
它限制您所使用的对象模型,如一个持久性类不能映射到多个表,其独有的界面和可怜的市场份额也让人不安。
4.Hibernate的缓存机制?
Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。
Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。
SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
事务范围的缓存是持久化层的第一级缓存;进程范围和集群范围的缓存是持久化层的第二级缓存。Hibernate还为查询结果提供了一个查询缓存,它依赖于二级缓存。
持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,还可以到进程范围或集群范围的缓存内查询,如果还是没有查到,那么只有到数据库中查询。事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。
什么样的数据适合放到二级缓存中?
1)很少被修改的数据;
2)不是很重要的数据;
3)不会被并发访问的数据;
4)参考数据。
不适合放到二级缓存中的数据?
1)经常被修改的数据;
2)绝对重要的数据,例如:财务数据;
3)共享数据。
其它:
· 处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象 必须提供数据过期策略;
· 只要应用程序通过Session接口执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行指操作。 用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就可以考虑使用第二级缓存。只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。
我的理解:
Hibernate中的一级缓存是出于Hibernate自身的需要而进行的一次数据缓存,可以在Hibernate中的一般操作中起到数据缓存的作用。而通常所说的缓存技术,实际上对应的就是Hibernate中的二级缓存,只不过在Hibernate中,已经有了Session级别的一级缓存,所以称之为二级缓存。
缓存的管理:
一级缓存的管理
Session为应用程序提供了两个管理缓存的方法:
Evict(Object obj); 从缓存中清除参数指定的持久化对象;
Clear(); 清空缓存中所有持久化对象。
二级缓存的管理:
Hibernate的二级缓存策略的一般过程如下:
1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
2) 把获得的所有数据对象根据ID放入到第二级缓存中。
3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
4) 删除、更新、增加数据的时候,同时更新缓存。
此外,Hibernate的二级缓存策略,只是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache(查询缓存)。
常用的缓存插件:
Hibernate的二级缓存是一个插件,下面是几种常用的缓存插件:
· EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
· OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。
· SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。
· JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。
5.Hibernate是如何实现延迟加载的?
Hibernate对象关系映射提供延迟的与非延迟的对象初始化。
非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化的阶段被读出来了。
一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。
这项技术的一个缺陷是延迟加载技术要求一个Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃DAO模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的可扩展性。
幸运的是,Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。对那些不熟悉Spring与Hibernate集成使用的人,我不会在这里讨论过多的细节,但是我建议你去了解Hibernate与Spring集成的数据访问。以一个Web应用为例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中,而Filter在Spring之前运行并被配置在web.xml中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开Hibernate会话。一旦已绑定到线程,这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了,Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。
6.Hibernate有几种查询方式?
· Session的get()和load()查询;
· HQL查询;
· Query查询;
· Criteria查询;
· 本地SQL语句查询(Native SQl)。
7.Hibernate的性能优化?
⑴在运行的情况下使用最新版本的Hibernate发行版,如hibernate3中经过优化的批量处理机制,代理机制、属性的延迟加载支持等;
⑵指定合理的缓存策略,通过系统压力测试得到最佳的缓存性能;
⑶采用合理的Session管理机制,避免无谓的数据库开销和临时对象的反复创建;
⑷尽量使用延迟加载特性,以避免系统资源的无谓消耗;
⑸设定合理的批处理参数(batch_size);
⑹如果可能,使用UUID作为主键生成器;
⑺如果可能,使用基于Version的乐观锁策略替代悲观锁;
⑻开发过程中,打开Hibernate的SQL日志文件(hibernate.show_sql),通过观察Hibernate生成的SQL语句进一步了解其实现原理,从而指定更好的实现策略;
⑼数据库本身的优化也起着至关重要的作用,合理的索引、缓存和数据分区策略都会对持久层性能带来客观提升。
8.Hibernate中的update()和saveOrUpdate()的区别?
Hibernate中的对象状态有3种,分别为:
· 未被持久化的值对象(Value Object,VO);
· 已经被持久化的持久化对象(Persistent Object,PO);
· 曾经被持久化过,但现在已经和Session分离,以VO的身份在运行。
首先,我们来说说何时用update();
· 如果你的PO不需要跨Session,即打开一个Session之后,对其进行操作,之后关闭,再也不会用到了,此时不需要用update()方法,此时Hibernate会自动检测到PO已经被修改过,会向数据库发送一条Update语句;如下代码:
Foo foo=sess.load(Foo.class,id);;
foo.setXXX(xxx);
sess.commit();
· 如果你的PO需要跨Session操作,在第一个Session关闭后,你还想把已经于第一个Session分离的PO当做VO来用,并将修改更新到数据库中,那么此时就需要Update()方法;
也就是说,当数据是持久化对象时,不需要使用update方法,当数据不是持久化状态的时候,必须使用update方法,告诉数据库要更新数据;
然后,我们谈谈saveOrUpdate();
如果业务层传过来的是一个已经持久化过的PO对象,那么Hibernate就会根据这个方法更新该对象,如果传递过来的是一个VO对象,那么Hibernate就会save这个对象。
9.Hibernate中的load()和get()的区别?
最重要的区别:如果未能发现符合条件的记录,Hibernate get方法返回null,而load方法会抛出一个ObjectNotFoundException。
1. 对于Hibernate get方法,Hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null。
2. Hibernate load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论:
(1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为 实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一 个ObjectNotFoundException。
(2)若为false,就跟Hibernate get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。