Hibernate常见面试题

1、在Hibernate中进行多表查询,每个表中各取几个字段,也就是说查询出来的结果集并没有一个实体类与之对应,如何解 决这个问题?

(1)使用传统方式:

String hql="select u.userName, u.telephone, u.address, g.goodsName from Users u, Goods g where u.userId=g.userId";
根据这个查询语句,调用query.list()方法得到一个List值,这个List中的每一个值都是Object[]类型的,里面包含了查询出来的所有值
(2)增加一个映射类

增加一个映射类UsersVoGoods.Java,添加需要查询的信息相关的所有属性,本例中添加userName, telephone, address, goodsName。并为这几个属性添加setter和getter方法,增加构造函数,参数与这四个属性对应,那么可以用hql查询方式:

String hql = "select new com.test.UsersVoGoods(u.userName, u.teltphone, u.address, g.goodsName) from Users u, Goods g where u.userId=g.userId";

2、hibernate中的get/load方法的区别

           Get/load方法它们都是根据id去查询对象。

Users user = (Users)session.load(Users.class, userId);   
Users user = (Users)session.get(Users.class, userId); 

(1)get直接得到了一个持久化类型对象,它就是立即查询操作
          load它得到的是持久化类型的代理类型对象(子类对象)。它采用了一种延迟策略来查询数据。

(2)get方法在查询时,如果不存在返回null
        load方法在查询时,如果不存在,会产生异常:ObjectNotFoundException。只有在访问除id之外的其他属性的时候,才发出sql

(3)扩展:

Load方法可返回实体的代理类实例,而get方法永远直接返回实体类。

  load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓 存,直接调用SQL完成数据读取。
  Session在加载实体对象时,将经过的过程:
  首先,Hibernate中维持了两级缓存。第一级缓存由Session实例维护,其中保持了Session当前所有关联实体的数据,也称为内 部缓存。而第二级缓存则存在于SessionFactory层次,由当前所有由本SessionFactory构造的Session实例共享。出于性能考 虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。首先在第一级缓存中,通过实体类型和id进行查找,如果第一 级缓存查找命中,且数据状态合法,则直接返回。
  之后,Session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。 “NonExists”记录了当前Session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当于一个查询黑名单列表)。如此一来,如果 Session中一个无效的查询条件重复出现,即可迅速作出判断,从而获得最佳的性能表现。
  对于load方法而言,如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。如在缓存中未发现有效数据,则发起数据库查询操作(SelectSQL),如经过查询未发现对应记录,则将此次查询的信息在 “NonExists”中加以记录,并返回null。根据映射配置和Select SQL得到的ResultSet,创建对应的数据对象。将其数据对象纳入当前Session实体管理容器(一级缓存)。执行Interceptor.onLoad方法(如果有对应的Interceptor)。将数据对象纳入二级缓存。如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。

3、Hibernate有哪几种查询数据的方式

1) 对象导航检索方式(导航对象图检索方式)根据已经加载的对象导航到其他对象,主要针对关联集合对象的查询。(针对多表)

2) OID检索方式:根据对象的OID来检索对象。(单表ById)

3) HQL检索方式:使用面向对象的HQL(Hibernate Query Language)查询语言来检索对象,Hibernate底层会自动HQL转换为SQL。Hibernate自己创建的一套以面向对象的方式操作数据库的查询语句,语法很类似SQL

4) Native SQL检索方式:本地(原生)SQL检索,使用本地数据库的SQL查询语句来检索对象。

5) QBC检索方式:使用完全面向对象的QBC(Query By Criteria)的API来检索对象,该API底层封装了查询语句。完全不需要懂HQL或者SQL,完全的面向对象的操作方式

注:前两种属于快捷检索方式,比较简单且常用,当这两种检索方式不能满足需要的时候,就需要使用后面几种检索方式,来自定义检索,如复杂检索条件等等。后面三种是可代替的

4、Hibernate工作原理及为什么要用?

工作原理:

Hibernate常见面试题_第1张图片

作用:

1)JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。

2) Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作

3)hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。

4)hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。

5、Hibernate采用延迟加载的方式

1)当调用Session上的load()方法加载一个实体时会采用延迟加载。
2) 当Session加载某个实体时,会对这个实体中的集合属性值采用延迟加载。(one-to-many)
3) 当Session加载某个实体时,会对这个实体所单端关联(one-to-one, many-to-one)的另一个实体对象采用延迟加载。
第二种和第三种的区别是:第二种情况下取消延时加载的方法是在单方即有set属性的一方的映射文件的set标签后设置懒加载的属性lazy="false";第三种情况则是在多方即有many-to-one的一方的映射文件中的many-to-one标签后设置lazy="false"。

能够懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理对象)的属性(getId和getClass除外)hibernate会初始化这些代理,或用Hibernate.initialize(proxy)来初始化代理对象;当相关联的session关闭后,再访问懒加载的对象将出现异常。

6、Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)

           类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的 many-to-one、one-to-many、many-to-many

7、说下Hibernate的缓存机制

1)一级缓存

session级别的缓存,它是属于事务范围的缓存,生命周期是session的生命周期, (一个线程 绑定一个Session, 对应一份一级缓存, 一级缓存无法实现多用户之间数据共享,它是hibernate的内置缓存,由hibernate管理,一般情况下无需进行干预.

2)二级缓存

sessionFactory 级别的缓存,它属于进程级别的缓或者集群范围的缓存(一个项目 只会对应一个SessionFactory对象, sessionFactory缓存数据 实现多用户之间共享(解耦合思想)。因此Hibernate二级缓存是进程范围,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。

什么样的数据适合存放到第二级缓存中?  
1) 很少被修改的数据   
2) 不是很重要的数据,允许出现偶尔并发的数据   
3) 不会被并发访问的数据   
4) 常量数据   
不适合存放到第二级缓存的数据?   
1) 经常被修改的数据   
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发   
3) 与其他应用共享的数据。

8、 如何优化Hibernate?

1)懒加载

①懒加载的类型:类的懒加载、集合懒加载、单端关联的懒加载(多对一),其中可以利用session.load()方法产生代理对象,在session.load()方法执行的时候并不发出sql语句,在得到其一般属性的时候发出sql语句,只针对一般属性有效而对标示属性无效。默认情况是懒加载。

在遍历的时候才发出sql语句——集合的懒加载,需要在配置中lazy=”true”懒加载,lazy=”false”不用懒加载,lazy=”extra”更进一步的懒加载(count,min,max,sum)。

②懒加载主要解决的问题:解决hibernate在什么时候发出sql语句的问题。

2)抓取策略

解决的问题:发出什么样的sql语句

join 左外连接    如果把需求分析翻译成sql语句存在子查询,这个时候用该策略不起作用。
select 默认       先查询一的一端,再查询多的一端
subselect         子查询(可以解决n+1问题) 如果需要翻译成sql语句存在子查询,这个时候用该策略效率最高
总结:懒加载和抓取策略结合,研究对象时Set集合:

fetch lazy sql 什么时候发出sql语句 说明

join false 存在子查询 当查询classes时把classes 这时候join没用  和student全部查询出来

join true 存在子查询 当遍历student时发出student join没用

join true 不是子查询 在session.get(classes)时全部查询出来 lazy没用

subselect true/false 存在子查询 发出两条sql语句 如果lazy为true,

在遍历集合;

如果lazy为false  一开始就发出

select true/false 发出n+1条语句 同上

注意:抓取策略只是提供给Hibernate的一种优化方式,只要是抓取策略能做到的都能用hql语句来做。

3)缓存策略

4)HQL语句优化

1.1使用参数绑定
使用绑定参数的原因是让数据库一次解析SQL,对后续的重复请求可以使用用生成好的执行计划,这样做节省CPU时间和内存。
避免SQL注入
1.2尽量少使用NOT
如果where子句中包含not关键字,那么执行时该字段的索引失效。
1.3尽量使用where来替换having
Having在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销
1.4减少对表的查询
在含有子查询的HQL中,尽量减少对表的查询,降低开销
1.5使用表的别名
当在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。







   





你可能感兴趣的:(常见面试题)