hibernate面试题


Hibernate使用Java 反射机制 而不是字节码增强程序来实现透明性

如果JDBC代码写的完美,优化做好,那么JDBC效率是最高的。但是,实际开发中非常不现实,对程序员要求太高。
一般情况下,hibernate内部进行了JDBC的优化处理,以及增加缓存机制,大大提高了运行效率。

SessionFactory使用要点如下:
1、负责创建Session对象,可以通过Configuration对象创建SessionFactory对象
2、SessionFactory 对象中保存了当前的数据库配置信息和所有映射关系以及预定义的SQL语句。
3、SessionFactory还负责维护Hibernate的二级缓存。
4、SessionFactory对象的创建会有较大的开销,而且SessionFactory对象采取了线程安全的设计方式,因此在实际中SessionFactory对象可以尽量的共享,在大多数情况下,一个应用中针对一个数据库可以共享一个SessionFactory实例
SessionFactory只能有一个(单例模式或者变为static属性)


Session是持久化操作的基础。Session的设计是非线程安全的,因此,一个Session对象只可以由一个线程使用。

使用Hibernate进行操作时(增、删、改)必须显示的调用Transaction(默认:autoCommit=false)
Transaction transaction = session.beginTransaction();
transaction.commit();

处于持久化状态的对象,当发生值得改变时,hibernate能检测到,立刻修改数据库的数据。如果在save之后,执行sql语句,效率较低。建议对持久对象值进行修改在save之前最好

//若对应id=1的记录不存在,返回null;
//get方法先在session缓存(一级缓存)中查找,若无,再在sessionfacotory(二级缓存)中查找,若无,再去数据库中查找,还无,返回null.
 User u = (User) s.get(User.class, 1);
  
//若对应id=1的记录不存在,则抛出异常。
//load一般用于我们可以保证这个记录一定存在的情况。该方法有懒加载!  
 User u = (User) s.load(User.class, 1);

一对多:
一般使用双向关联
cascade属性:级联操作。操作一个对象时将该对象相关属性对象也进行同样操作。
      all:进行任何操作都级联。
      save-update:保存和更新操作时
      delete:删除操作时级联
      all-delete-orpnan:当被关联对象失去宿主时,将其级联删除。
      none(默认) :不级联

inverse属性:由哪一方维护外键的值。默认为false, 双方都可以维护关联关系; 为true, 则表示由“多方”维护;
建议:
 为true, 由多方维护提高效率。调用必须调用“多方”(一般由多方维护)。
 为false,写程序方便,运行效率低下。
Inverse=false,关联关系可以由双方维护!
但是我们一般将关联关系交给”多方”来维护。上面两次实验,第一次交给了多方维护,执行了3条SQL语句。第二交给了一方维护,执行了3条insert语句和2条update语句(假如增加200个雇员那么就会有200个update语句)。如果我们交给一方来维护,显然会有多余的SQL语句执行,从而降低了效率。因此,我们一般建议关联关系由多方维护!那么这时候,我们可以强制通过inverse=true,指定由多方维护!


一对一关系我们一般采用唯一外键关联!

多对多:
多对多的实体关系模型也是很常见的,比如学生和课程的关系。一个学生可以选修多门课程,一个课程可以被多名学生选修。在关系型数据库中对于多对多关联关系的处理一般采用中间表的形式,将多对多的关系转化成两个一对多的关系。


性能优化:

注意session.clear()的运用,尤其在不断分页循环的时候,会产生另外一种形式的内存泄露  (  //面试题:Java有内存泄漏吗?语法级别没有 但是可由java引起,例如:连接池不关闭,或io读取后不关闭)

 
面试题:
1、open session每次都是新的,需要手动close
     getCurrentsession从上下文找,如果有,用旧的,如果没有,建新的
如果你正在查询,使用的openSession而没有手动关闭,多次之后会导致连接池溢出
2、get  load的区别:
load返回的是代理对象,等到真正用到对象的内容时才发出sql语句
get则返回的是实体对象,直接从数据库加载,不会延迟
get请求的对象数据库中没有时返回null,load则抛异常。

总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。

session.clear();:无论是load还是get,都会首先査找缓存(一级缓存),如果没有,才会去数据库査找,调用clear()方法可以强制清除session缓存
3、transient:内存中有一个新new的对象JavaBean,缓存(即session)中没有指向new对象的Map,跟数据库也没有关系(数据库没对应记录的存在)
     persistent:save()完后为persistent状态,内存中有JavaBean(生成了Id),以map形式存入到session中 ,数据库中有对应记录
     detached:session.close()后(但session.close()方法不会显示调用,在session.commit()时系统自动close),缓存中清空,跟session无关,数据库有对应记录,但是数据库和内存中的对象没有连接到一起,所以叫做detached托管的。
4、双向关联关系的两铁律:
 凡是双向关联,必设mappedBy
 在程序中也要A.set(B)以及B.set(A)
5、继承映射:
单表继承(整个类继承结构一个表)
 优点:只有一张表,操作方便,效率高。
 缺点:冗余数据多,增加子类要修改表结构,数据多时表非常大。
joined-subclass(父类映射成表,一个类一张表,互相关联,不独立)
 优点:冗余字段少,表结构合理(关系模型设计上优良)
 缺点:多态查询慢(一般不要用,最好直接指定类型。比如查询man,则会把man的子类表也查一遍)、查询需要外连接
union-subclass (父类不映射成表,每个子类一张表,互不关联,独立)
 优点:表都独立利于移植,查询不用外连接速度快
 缺点:数据库包含重复字段过多,包含了父类中的字段
 注意:父类和子类id不能重复,用自动递增不行,可用increment/hilo/uuid等。
如何选用?(一般使用joined-subclass)
 如果子类属性不多,总数据量不大,选用单表继承
 如果子类属性较多,可用joined-subclass.
 union-subclass用的很少
6、hibernate的树状结构:前面有介绍

7、1+N问题:
在一个对象中关联了另一个对象,同时FetchType为Eager,比如说最典型的ManyToOne(当然OneToMany也有这问题,当在Many这头设Eager),默认Many方是FetchType为Eager,当取Many里的对象时,会单独再发一条SQL语句将他关联的对象也取出来,即本来只要发一条SQL,结果发了1+N条, 解决方案:fetch=FetchType.LAZY,还可以用Join fetch(在1方,查找得到了n个对象, 那么又需要将n个对象关联的集合取出,于是本来的一条sql查询变成了n+1条。)

二级缓存
在对象更新,删除,添加相对于查询要少得多时, 二级缓存的应用将不怕n+1问题,因为即使第一次查询很慢,之后直接缓存命中也是很快的,刚好又利用了n+1。
8、三种缓存:
一级缓存( Session缓存)
一级缓存的管理 : 应用程序调用Session的save()、update()、saveOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate() 时,如果在Session缓存中还不存在相应的对象,Hibernate  就会把该对象加入到第一级缓存中。  可以通过close/clear/evict清空缓存
一级缓存的作用 : 因为Session的生命期往往很短,存在于Session内部的第一级最快缓存的生命期当然也很短,所以第一级缓存的命中率是很低的。其对系统性能的改善也是很有限的。Session内部缓  存的主要作用是保持Session内部数据状态同步。

二级缓存(SessionFactory缓存):需手动开启
开启方式:
       <property name="hibernate.cache.use_second_level_cache">true</property>
       <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
如何使用: 类定义前面:@cache,指该类的对象都会放入二级缓存。@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)  //放入二级缓存中也可以被修改。一般用它。
什么内容时候放入二级缓存:  经常被访问、改动不频繁、数量有限。

get/load会使用二级缓存。
iterate也会使用二级缓存。
list默认会往二级缓存中存放数据,即通过list查出的结果会放入二级缓存。但是list本身查询时不会使用二级缓存。


查询缓存:
     查询缓存只对query.list()起作用
     查询缓存依赖于二级缓存,因此一定要打开二级缓存。
     查询缓存实现机制:以查询语句为key,查到的对象的id为value
     查询缓存的配置和使用:
     开启二级缓存:
           <property name="hibernate.cache.use_query_cache">true</property> //默认是fasle
      在程序中必须手动启用查询缓存,如:query.setCacheable(true);

缓存算法问题:缓存满了后,将内存中哪个对象清掉。
LRU:Least  Recently   Used  最近最少被使用的。每个缓存对象都记录一个最后使用时间。
LFU:Least  Frequently  Used  最近使用频率最少。
FIFO:First  In  First Out

9、事务基本概念

ACID即是atomicity(原子性),consistency(一致性),isolation(隔离性)和durability(持久性)的首字母的缩写

      原子性:表示一个事务内的所有操作是一个整体,要 么全部成功,要么全失败;

      一致性:表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态;

      隔离性:事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。

      持久性:事务完成之后,它对于系统的影响是永久性的。

事务隔离级别从低到高:

      读取未提交(Read Uncommitted)

      读取已提交(Read Committed)

      可重复读(Repeatable Read)

      序列化(serializable)


read-commited(读取已提交的数据 项目中一般都使用这个)不会出现dirty read,因为只有另一个事务提交才会读出来结果,但仍然会出现 non-repeatable read 和 phantom-read
 使用read-commited机制可用悲观锁 乐观锁来解决non-repeatable read 和 phantom-read问题
我们一般采用读取已提交,配合各种并发访问控制策略来达到并发事务控制的目的。
10、乐观锁与悲观锁

乐观锁Optimistic Locking:
顾名思义就是保持一种乐观的态度,我们认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重新再来一次。
它的基本思想就是每次提交一个事务更新时,我们想看看要修改的东西从上次读取以后有没有被其它事务修改过,如果修改过,那么更新就会失败。
常用实现方法:
在我们的实体中增加一个版本控制字段,每次事务更新后就将版本(Version)字段:版本字段的值加1.
在实体类中增加@Version, private  int  version;getset 即可。

悲观锁Pessimistic Locking:
基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁。
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)

乐观锁和悲观锁的比较:
乐观锁:
        优势:并发性好,性能较高。
        缺点:用户体验不好,录入了半天,提交时被告知已经修改!

悲观锁:
        优势:会锁住记录,一个用户修改完成前,其他用户不能操作该记录。
        缺点:并发性不好,性能不高。
对于悲观锁是针对并发的可能性比较大,而一般在我们的应用中用乐观锁足以。

Hibernate实践总结
1、数据量巨大,性能要求高,hibernate由于在ORM映射中对系统资源消耗也比较高,所以不适合。
2、Hibernate适合:逻辑复杂,数据量不大。
3、sessionFactory的创建非常消耗资源,整个应用一般只要一个。
4、将所有的集合属性配置设置为懒加载。 Hibernate2.x默认是false, hibernate3.x默认是true
5、在定义关联关系时,集合首选Set,如果集合中的实体存在重复,则选择List,数组性能最差。
6、在一对多的双向关联中,一般将集合(多)的inverse设置为true,让集合的对方维护关联关系。
7、HQL子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分。
8、对大数据量查询时,慎用list() 返回查询结果
9、在性能瓶颈的地方使用JDBC。
10、使用双向关联。在大型应用中,几乎所有的关联必须在查询中可以双向导航。

 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值