hibernate面试笔记

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

若是JDBC代码写的完美,优化作好,那么JDBC效率是最高的。可是,实际开发中很是不现实,对程序员要求过高。
通常状况下,hibernate内部进行了JDBC的优化处理,以及增长缓存机制,大大提升了运行效率。程序员

SessionFactory使用要点以下:
一、负责建立Session对象,能够经过Configuration对象建立SessionFactory对象
二、SessionFactory 对象中保存了当前的数据库配置信息和全部映射关系以及预约义的SQL语句。
三、SessionFactory还负责维护Hibernate的二级缓存。
四、SessionFactory对象的建立会有较大的开销,并且SessionFactory对象采起了线程安全的设计方式,所以在实际中SessionFactory对象能够尽可能的共享,在大多数状况下,一个应用中针对一个数据库能够共享一个SessionFactory实例
SessionFactory只能有一个(单例模式或者变为static属性)web


Session是持久化操做的基础。Session的设计是非线程安全的,所以,一个Session对象只能够由一个线程使用。面试

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

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

//若对应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);sql

一对多:
通常使用双向关联
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读取后不关闭)


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

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

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

七、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。
八、三种缓存:
一级缓存( 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

九、事务基本概念

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问题
咱们通常采用读取已提交,配合各类并发访问控制策略来达到并发事务控制的目的。
十、乐观锁与悲观锁

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

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

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

悲观锁:
优点:会锁住记录,一个用户修改完成前,其余用户不能操做该记录。
缺点:并发性很差,性能不高。
对于悲观锁是针对并发的可能性比较大,而通常在咱们的应用中用乐观锁足以。

Hibernate实践总结
一、数据量巨大,性能要求高,hibernate因为在ORM映射中对系统资源消耗也比较高,因此不适合。
二、Hibernate适合:逻辑复杂,数据量不大。
三、sessionFactory的建立很是消耗资源,整个应用通常只要一个。
四、将全部的集合属性配置设置为懒加载。 Hibernate2.x默认是false, hibernate3.x默认是true
五、在定义关联关系时,集合首选Set,若是集合中的实体存在重复,则选择List,数组性能最差。
六、在一对多的双向关联中,通常将集合(多)的inverse设置为true,让集合的对方维护关联关系。
七、HQL子句自己大小写无关,可是其中出现的类名和属性名必须注意大小写区分。
八、对大数据量查询时,慎用list() 返回查询结果
九、在性能瓶颈的地方使用JDBC。
十、使用双向关联。在大型应用中,几乎全部的关联必须在查询中能够双向导航。
Hibernate工做原理及为何要用?
原理:
1.读取并解析配置文件
2.读取并解析映射信息,建立SessionFactory
3.打开Sesssion
4.建立事务Transation
5.持久化操做
6.提交事务
7.关闭Session
8.关闭SesstionFactory

为何要用:
1. 对JDBC访问数据库的代码作了封装,大大简化了数据访问层繁琐的重复性代码。
2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工做
3. hibernate使用Java反射机制,而不是字节码加强程序来实现透明性。
4. hibernate的性能很是好,由于它是个轻量级框架。映射的灵活性很出色。它支持各类关系数据库,从一对一到多对多的各类复杂关系。

2. Hibernate是如何延迟加载?
1. Hibernate2延迟加载实现:a)实体对象 b)集合(Collection)
2. Hibernate3 提供了属性的延迟加载功能;当Hibernate在查询数据的时候,数据并无存在与内存中,当程序真正对数据的操做时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提升了服务器的性能。
3.Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
类与类之间的关系主要体如今表与表之间的关系进行操做,它们都市对对象进行操做,咱们程序中把全部的表与类都映射在一块儿,它们经过配置文件中的many-to-one、one-to-many、many-to-many、

4. 说下Hibernate的缓存机制
1. 内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存
2. 二级缓存:
a) 应用及缓存
b) 分布式缓存
条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非 关键数据
c) 第三方缓存的实现

5. Hibernate的查询方式
Sql、Criteria,object comptosition
Hql:
一、 属性查询
二、 参数查询、命名参数查询
三、 关联查询
四、 分页查询
五、 统计函数

6. 如何优化Hibernate?
1.使用双向一对多关联,不使用单向一对多
2.灵活使用单向一对多关联
3.不用一对一,用多对一取代
4.配置对象缓存,不使用集合缓存
5.一对多集合使用Bag,多对多集合使用Set
6. 继承类使用显式多态
7. 表字段要少,表关联不要怕多,有二级缓存撑腰

7. Struts工做机制?为何要使用Struts?
工做机制:
Struts的工做流程:
在web应用启动时就会加载初始化ActionServlet,ActionServlet从
struts-config.xml文件中读取配置信息,把它们存放到各类配置对象
当ActionServlet接收到一个客户请求时,将执行以下流程.
-(1)检索和用户请求匹配的ActionMapping实例,若是不存在,就返回请求路径无效信息;
-(2)若是ActionForm实例不存在,就建立一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中;
-(3)根据配置信息决定是否须要表单验证.若是须要验证,就调用ActionForm的validate()方法;
-(4)若是ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActuibErrors对象, 就表示表单验证成功;
-(5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪一个Action,若是相应的 Action实例不存在,就先建立这个实例,而后调用Action的execute()方法;
-(6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给 ActionForward对象指向的JSP组件;
-(7)ActionForward对象指向JSP组件生成动态网页,返回给客户;

为何要用:
JSP、Servlet、JavaBean技术的出现给咱们构建强大的企业应用系统提供了可能。但用这些技术构建的系统很是的繁乱,因此在此之上,咱们须要一个规则、一个把这些技术组织起来的规则,这就是框架,Struts便应运而生。
基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件

8. Struts的validate框架是如何验证的?
在struts配置文件中配置具体的错误提示,再在FormBean中的validate()方法具体调用。

9. 说下Struts的设计模式
MVC 模式: web应用程序启动时就会加载并初始化ActionServler。用户提交表单时,一个配置好的ActionForm对象被建立,并被填入表单相应的数据,ActionServler根据Struts-config.xml文件配置好的设置决定是否须要表单验证,若是须要就调用ActionForm的 Validate()验证后选择将请求发送到哪一个Action,若是Action不存在,ActionServlet会先建立这个对象,而后调用 Action的execute()方法。Execute()从ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。

10. spring工做机制及为何要用?
1.spring mvc请全部的请求都提交给DispatcherServlet,它会委托应用系统的其余模块负责负责对请求进行真正的处理工做。
2.DispatcherServlet查询一个或多个HandlerMapping,找处处理请求的Controller.
3.DispatcherServlet请请求提交到目标Controller
4.Controller进行业务逻辑处理后,会返回一个ModelAndView
5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象
6.视图对象负责渲染返回给客户端。

为何用:{AOP 让开发人员能够建立非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP 后,公共服务 (比 如日志、持久性、事务等)就能够分解成方面并应用到域对象上,同时不会增长域对象的对象模型的复杂性。IOC 容许建立一个能够构造对象的应用环境,而后向这些对象传递它们的协做对象。正如单词 倒置 所代表的,IOC 就像反 过来的 JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straight construction),每个对象都是用其协做对象构造的。所以是由容器管理协做对象(collaborator)。Spring即便一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协做对象)。而后能够很容易地在须要时替换具备相似接口的协做对象。}