1、什么是Hibernate的并发机制?怎么去处理并发问题?
Hibernate并发机制:
a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次, 然后就丢弃。
如果一个Session 实例允许共享的话,那些支持并发运行的,例如Http request,session beans将会导致出现资源争用。
如果在Http Session中有hibernate的Session的话,就可能会出现同步访问Http Session。只要用户足够快的点击浏览器的“刷新”, 就会导致两个并发运行的线程使用同一个Session。
b、多个事务并发访问同一块资源,可能会引发第一类丢失更新,脏读,幻读,不可重复读,第二类丢失更新一系列的问题。
解决方案:
a:设置事务隔离级别。
Serializable:串行化。隔离级别最高
Repeatable Read:可重复读
Read Committed:已提交数据读
Read Uncommitted:未提交数据读。隔离级别最差
b:设置锁:乐观锁和悲观锁。
1、乐观锁:
使用版本号或时间戳来检测更新丢失,在的映射中设置 optimistic-lock=”all”可以在没有版本或者时间戳属性映射的情况下实现 版本检查,此时Hibernate将比较一行记录的每个字段的状态
2、行级悲观锁:
Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。类LockMode 定义了Hibernate所需的不同的锁定级别:LockMode.UPGRADE,LockMode.UPGRADE_NOWAIT,LockMode.READ;
2、update和saveOrUpdate的区别?
update()和saveOrUpdate()是用来对跨Session的PO进行状态管理的。
update()方法操作的对象必须是持久化了的对象。也就是说,如果此对象在数据库中不存在的话,就不能使用update()方法。
saveOrUpdate()方法操作的对象既可以使持久化了的,也可以使没有持久化的对象。如果是持久化了的对象调用saveOrUpdate()则会 更新数据库中的对象;如果是未持久化的对象使用此方法,则save到数据库中。
3、hibernate的三种状态之间如何转换
当对象由瞬时状态(Transient)一save()时,就变成了持久化状态;
当我们在Session里存储对象的时候,实际是在Session的Map里存了一份, 也就是它的缓存里放了一份,然后,又到数据库里存了一份,在缓存里这一份叫持久对象(Persistent)。 Session 一 Close()了,它的缓存也都关闭了,整个Session也就失效了,这个时候,这个对象变成了游离状态(Detached),但数据库中还是存在的。
当游离状态(Detached)update()时,又变为了持久状态(Persistent)。
当持久状态(Persistent)delete()时,又变为了瞬时状态(Transient), 此时,数据库中没有与之对应的记录。
4、Hibernate中get和load有什么不同之处?
把get和load放到一起进行对比是Hibernate面试时最常问到的问题,这是因为只有正确理解get()和load()这二者后才有可能高效地使用Hibernate。get和load的最大区别是,如果在缓存中没有找到相应的对象,get将会直接访问数据库并返回一个完全初始化好的对象,而这个过程有可能会涉及到多个数据库调用;而load方法在缓存中没有发现对象的情况下,只会返回一个代理对象,只有在对象getId()之外的其它方法被调用时才会真正去访问数据库,这样就能在某些情况下大幅度提高性能。你也可以参考 Hibernate中get和load的不同之处, 此链接给出了更多的不同之处并对该问题进行了更细致的讨论。
(1)Hibernate的get方法,会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null。
(2)Hibernate的load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true):
1、若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException。
2、若为false,就跟Hibernate的get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException,所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时。
3、load 支持延迟加载,get不支持延迟加载。
5、Hibernate中save、persist和saveOrUpdate这三个方法的不同之处?
除了get和load,这又是另外一个经常出现的Hibernate面试问题。 所有这三个方法,也就是save()、saveOrUpdate()和persist()都是用于将对象保存到数据库中的方法,但其中有些细微的差别。例如,save()只能INSERT记录,但是saveOrUpdate()可以进行 记录的INSERT和UPDATE。还有,save()的返回值是一个Serializable对象,而persist()方法返回值为void。你还可以访问 save、persist以及saveOrUpdate,找到它们所有的不同之处。
6、Hibernate中的命名SQL查询指的是什么?
Hibernate的这个面试问题同Hibernate提供的查询功能相关。命名查询指的是用<sql-query>标签在影射文档中定义的SQL查询,可以通过使用Session.getNamedQuery()方法对它进行调用。命名查询使你可以使用你所指定的一个名字拿到某个特定的查询。 Hibernate中的命名查询可以使用注解来定义,也可以使用我前面提到的xml影射问句来定义。在Hibernate中,@NameQuery用来定义单个的命名查询,@NameQueries用来定义多个命名查询。
7、Hibernate中的SessionFactory有什么作用? SessionFactory是线程安全的吗?
这也是Hibernate框架的常见面试问题。顾名思义,SessionFactory就是一个用于创建Hibernate的Session对象的工厂。SessionFactory通常是在应用启动时创建好的,应用程序中的代码用它来获得Session对象。作为一个单个的数据存储,它也是 线程安全的,所以多个线程可同时使用同一个SessionFactory。Java JEE应用一般只有一个SessionFactory,服务于客户请求的各线程都通过这个工厂来获得Hibernate的Session实例,这也是为什么SessionFactory接口的实现必须是线程安全的原因。还有,SessionFactory的内部状态包含着同对象关系影射有关的所有元数据,它是 不可变的,一旦创建好后就不能对其进行修改了
。
8、Hibernate中的Session指的是什么? 可否将单个的Session在多个线程间进行共享?
前面的问题问完之后,通常就会接着再问这两个问题。问完SessionFactory的问题后就该轮到Session了。Session代表着Hibernate所做的一小部分工作,它负责维护者同数据库的链接而且 不是线程安全的,也就是说,Hibernage中的Session不能在多个线程间进行共享。虽然Session会以主动滞后的方式获得数据库连接,但是Session最好还是在用完之后立即将其关闭。
9、hibernate中sorted collection和ordered collection有什么不同?
T这个是你会碰到的所有Hibernate面试问题中比较容易的问题。sorted collection是通过使用 Java的Comparator在内存中进行排序的,ordered collection中的排序用的是数据库的order by子句。对于比较大的数据集,为了避免在内存中对它们进行排序而出现 Java中的OutOfMemoryError,最好使用ordered collection。
10、Hibernate中transient、persistent、detached对象三者之间有什么区别?
在Hibernate中,对象具有三种状态:transient、persistent和detached。同Hibernate的session有关联的对象是persistent对象。对这种对象进行的所有修改都会按照事先设定的刷新策略,反映到数据库之中,也即,可以在对象的任何一个属性发生改变时自动刷新,也可以通过调用Session.flush()方法显式地进行刷新。如果一个对象原来同Session有关联关系,但当下却没有关联关系了,这样的对象就是detached的对象。你可以通过调用任意一个session的update()或者saveOrUpdate()方法,重新将该detached对象同相应的seesion建立关联关系。Transient对象指的是新建的持久化类的实例,它还从未同Hibernate的任何Session有过关联关系。同样的,你可以调用persist()或者save()方法,将transient对象变成persistent对象。可要记住,这里所说的transient指的可不是 Java中的transient关键字,二者风马牛不相及。
11、Hibernate中Session的lock()方法有什么作用?
这是一个比较棘手的Hibernate面试问题,因为Session的lock()方法重建了关联关系却并没有同数据库进行同步和更新。因此,你在使用lock()方法时一定要多加小心。顺便说一下,在进行关联关系重建时,你可以随时使用Session的update()方法同数据库进行同步。有时这个问题也可以这么来问:Session的lock()方法和update()方法之间有什么区别?。这个小节中的关键点也可以拿来回答这个问题。
12、Hibernate中二级缓存指的是什么?
这是同Hibernate的缓存机制相关的第一个面试问题,不出意外后面还会有更多这方面的问题。二级缓存是在SessionFactory这个级别维护的缓存,它能够通过节省几番数据库调用往返来提高性能。还有一点值得注意,二级缓存是针对整个应用而不是某个特定的session的。
13、Hibernate中的查询缓存指的是什么?
这个问题有时是作为上个Hibernate面试问题的后继问题提出的。查询缓存实际上保存的是sql查询的结果,这样再进行相同的sql查询就可以之间从缓存中拿到结果了。为了改善性能,查询缓存可以同二级缓存一起来使用。Hibernate支持用多种不同的开源缓存方案,比如EhCache,来实现查询缓存。
14、为什么在Hibernate的实体类中要提供一个无参数的构造器这一点非常重要?
每个Hibernate实体类必须包含一个 无参数的构造器, 这是因为Hibernate框架要使用Reflection API,通过调用Class.newInstance()来创建这些实体类的实例。如果在实体类中找不到无参数的构造器,这个方法就会抛出一个InstantiationException异常。
15、可不可以将Hibernate的实体类定义为final类?
可以将Hibernate的实体类定义为final类,但这种做法并不好。因为Hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义成final类之后,因为 Java不允许对final类进行扩展,所以Hibernate就无法再使用代理了,如此一来就限制了使用可以提升性能的手段。不过,如果你的持久化类实现了一个接口而且在该接口中声明了所有定义于实体类中的所有public的方法轮到话,你就能够避免出现前面所说的不利后果。
16、用过hibernate注解吗 说出几个 @Entity:标记为持久类映射 @Table:指定表名 @Id @GeneratedValue @GenericGenerator:主键生成机制 @OneToMany @ManyToOne @JoinColumn:关联字段 其它注解: @Resource:标注在对象上,可以代替以前的set方法。 @Controller:表示action层。 @Service:表示service层。 @Repository:表示dao层。
17、Hibernate工作原理及为什么要用?
原理:
读取并解析配置文件
读取并解析映射信息,创建SessionFactory
打开Sesssion
创建事务Transation
持久化操作
提交事务
关闭Session
关闭SesstionFactory
为什么要用:
对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
18、Hibernate是如何延迟加载?
Hibernate2延迟加载实现:a)实体对象 b)集合(Collection)
Hibernate3 提供了属性的延迟加载功能
当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而
提高了服务器的性能。
19、Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都市对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、
one-to-many、many-to-many、
20、说下Hibernate的缓存机制
内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存
二级缓存:
a) 应用及缓存
b) 分布式缓存
条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非关键数据
c) 第三方缓存的实现
21、如何优化Hibernate?
使用双向一对多关联,不使用单向一对多
灵活使用单向一对多关联
不用一对一,用多对一取代
配置对象缓存,不使用集合缓存
一对多集合使用Bag,多对多集合使用Set
继承类使用显式多态
表字段要少,表关联不要怕多,有二级缓存撑腰
22、N+1问题。
所谓N+1问题,即hibernate在做关联查询的时候有几个关联对象,就会有多少个查询语句,这便是N,而+1是查询实体本生还需要一个查询语句,所以hibernate查询一个有关联的实体便会产生N+1条查询语句,这便是N+1.对于hibernate的N+1问题。而Hibernate中使用N+1策略时执行Criteria时,会将自动进行SQL构造,并且映射到实体Bean中,就只会返回一个结果,这样不仅提高了性能,而且在处理返回值的时候也变得比较轻松。
Hibernate中常会用到set,bag等集合表示1对多的关系, 在获取实体的时候就能根据关系将关联的对象或者对象集取出。
解决方法一个是延迟加载, 即lazy=true;
一个是预先抓取, 即fetch=join;
23、inverse的好处。(重点)
在关联关系中用inverse在控制由哪一端来控制关联关系。这样做有什么好处呢?举customer和order的例子来说。他们是一对多的关系,如果只单向关联,且由customer控制关联关系,则如果我想添加一个order,则先取customer, 然后getOrders得到所有的order集合,然后往集合里面多加入一个order,然后save(customer), 这样开销太大。 如果改双向关联且由order主控关系,则如果想为customer增加一个order, 则new一个order,然后给order设置customer,然后save(order)即可。
24、merge的含义:
http://cp3.iteye.com/blog/786019 这里写的好;
如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例
如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例
最后返回该持久实例
用户给出的这个对象没有被关联到session上,它依旧是脱管的
25、persist和save的区别
persist不保证立即执行,可能要等到flush;persist不更新缓存;
26、cascade
cascade,用来指示在主对象和它包含的集合对象的级联操作行为,即对住对象的更新怎么影响到子对象;
save-update: 级联保存(load以后如果子对象发生了更新,也会级联更新). 但它不会级联删除
delete: 级联删除, 但不具备级联保存和更新
all-delete-orphan: 在解除父子关系时,自动删除不属于父对象的子对象, 也支持级联删除和级联保存更新.
all: 级联删除, 级联更新,但解除父子关系时不会自动删除子对象.
delete-orphan:删除所有和当前对象解除关联关系的对象
27、session.commit 和flush区别
commit会先调用flash执行session清理,然后提交事物; flash执行session,但不一定提交事物(因为事物可能被委托给外围的aop代理来做);
28、session清理的顺序
insert -> update -> delete -> 对集合进行delete -〉对集合的insert;
29、检索策略
立即检索,lazy=false;延迟加载:lazy=true;预先抓取: fetch=“join”;
30、主键生成 策略有哪些?
identity,increment, sequence, assigned, uuid.hex, foreign; foreign是一对一主键关联的情况下使用。
31、Hibernate 的检索方式有哪些 ?
① 导航对象图检索
② OID检索
③ HQL检索
④ QBC检索
⑤ 本地SQL检索
32、在 Hibernate 中 Java 对象的状态有哪些?
①. 临时状态(transient):不处于 Session 的缓存中。OID 为null 或等于 id 的 unsaved-value 属性值
②. 持久化状态(persistent):加入到 Session 的缓存中。
③. 游离状态(detached):已经被持久化,但不再处于 Session 的缓存中。
33、Session的清理和清空有什么区别?
清理缓存调用的是 session.flush() 方法. 而清空调用的是 session.clear() 方法.
Session 清理缓存是指按照缓存中对象的状态的变化来同步更新数据库,但不清空缓存;清空是把Session 的缓存置空, 但不同步更新数据库;
34、hibernate 优缺点
①. 优点:
对 JDBC 访问数据库的代码做了封装,简化了数据访问层繁琐的重复性代码
映射的灵活性, 它支持各种关系数据库, 从一对一到多对多的各种复杂关系.
非侵入性、移植性会好
缓存机制: 提供一级缓存和二级缓存
②. 缺点:
无法对 SQL 进行优化
框架中使用 ORM原则, 导致配置过于复杂
执行效率和原生的JDBC 相比偏差: 特别是在批量数据处理的时候
不支持批量修改、删除
35、描述使用 Hibernate 进行大批量更新的经验.
直接通过 JDBC API 执行相关的 SQl 语句或调用相关的存储过程是最佳的方式
36、Hibernate 的OpenSessionView 问题
①. 用于解决懒加载异常, 主要功能就是把 Hibernate Session 和一个请求的线程绑定在一起, 直到页面完整输出, 这样就可以保证页面读取数据的时候 Session 一直是开启的状态, 如果去获取延迟加载对象也不会报错。
②. 问题: 如果在业务处理阶段大批量处理数据, 有可能导致一级缓存里的对象占用内存过多导致内存溢出, 另外一个是连接问题: Session 和数据库 Connection 是绑定在一起的, 如果业务处理缓慢也会导致数据库连接得不到及时的释放, 造成连接池连接不够. 所以在并发量较大的项目中不建议使用此种方式, 可以考虑使用迫切左外连接 (LEFT OUTER JOIN FETCH) 或手工对关联的对象进行初始化.
③. 配置 Filter 的时候要放在 Struts2 过滤器的前面, 因为它要页面完全显示完后再退出.
37、Hibernate 中getCurrentSession() 和 openSession() 的区别 ?
①. getCurrentSession() 它会先查看当前线程中是否绑定了 Session, 如果有则直接返回, 如果没有再创建. 而openSession() 则是直接 new 一个新的 Session 并返回。
②. 使用ThreadLocal 来实现线程 Session 的隔离。
③. getCurrentSession() 在事务提交的时候会自动关闭 Session, 而 openSession() 需要手动关闭.
38、如何调用原生 SQL ?
调用 Session 的doWork() 方法.
39、说说 Hibernate 的缓存:
Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存:
1). Hibernate一级缓存又称为“Session的缓存”,它是内置的,不能被卸载。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。在第一级缓存中,持久化类的每个实例都具有唯一的OID。
2).Hibernate二级缓存又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存删除、更新、增加数据的时候,同时更新缓存。
40、请你谈谈你对OR映射的理解?
1、or:将每个实体类都映射为数据库中的一个表,或将数据库中的表映射为一个实体类。
41、很多人说Hibernate不适合大项目,性能有问题,你是如何理解的?
因为Hibernate属于大型框架,里面对方法的封装比较多,让SQL控制权降低。这样的话,适用性就会降低,在不必要的操作上会浪费时间。但是只要将Hibernate里的配置进行优化,也能提高其性能。
42、Hibernate为什么一定要定义一个数据库标识?
可以让映射文件变得简洁,消除无用的噪音(比如TBL_前缀等)。Hibernate使用的默认策略是几乎什么都不做,所以使用标识的作用就是使数据操作更加简洁和方便.
43、为什么Hibernate建议你的实体类实现hashCode和equals方法?
因为Hibernate使用了一级和二级缓存,很多查询为了提高效率Hibernate都会先从缓存中进行查找,然后再从数据库进行查找。而HashCode是唯一的,所以这样避免数据操作出现数据混淆的可能,而equals的作用是对比Hibernate缓存中数据是否一致。
44、谈谈你对Hibernate实体类中的数据库标识与数据库主键之间关系的认识?
数据库标识是为了方便和简洁映射文件,而主键是为了使数据不会重复。
45、谈谈你对Hibernate关联映射与数据库外键之间关系的认识?
Hibernate在进行映射的时候会将数据库的关系也进行映射将数据库中的外键也使用标签的形式进行管理,这样在人为操作的时候就不需要手动的管理数据库关系了。
46、调用session.save()方法,hibernate一定会发出insert语句吗?谈谈你的理解
不会,具体执行步骤如下:
1、首先在Session内部缓存中进行查找,如果发现则直接返回。
2、执行实体类对应的Interceptor.isUnsaved方法(如果有的话),判断对象是否为未保存状态。
3、根据unsaved-value判断对象是否处于未保存状态。
4、如果对象未保存(Transient状态),则调用save方法保存对象。
5、如果对象未已保存(Detached状态),则调用update方法将对象与Session重新关联。
47、调用session.update()方法,hibernate一定会发出update语句吗?谈谈你的理解
不会,具体步骤同上。
48、请你聊一下以下名词、概念或用法:lazy、lazy=”extra”、inverse、fetch、fetch=”join”、fetch=”subselect”、batch-size
lazy懒加载,hibernate映射文件默认的lazy = true.lazy=“extra”extra属性是不大容易重视的,其实它和true差不多,但有个小的智能的地方是,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据
inverse控制翻转,主要是为了让谁去维护关系,一般是在主表中配置,将维护关系的只能交给主键。
fetch取值,fetch="join"主要是在查询的时候Hibernate会自动查询有关联的表。fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合,此时lazy会失效.(也可以通过设置lazy为false,实现加载关联表)
fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合
batch-size配置这个属性是让Hibernate在执行批量的数据库操作
49、配置了lazy=”true”一定会实现懒加载吗?
不一定,因为如果再配置中你也使用fetch属性的话此时lazy就会失效。(lazy设为true,同理)
50、请你谈谈Hibernate有哪些最佳实践?
对象-关系映射。 hibernate支持关系型数据库,并且它与sql紧密结合的程度较高,有自己的查询语言hql,它与sql的区别在于hql是面向对象的而不是针对数据模型。但是如果需要也可以在hibernate中使用 sql.并且hibernate还提供了灵活的映射机制,以及session级缓存,和sessionfactosy级缓存。只是session级缓存是非线程安全的,如果多线程访问可能引起数据存取的不一致,甚至系统宕机。
51、请解释为什么SessionFactory一般以单利方式使用。
SessionFactory是一个大型对象,而且线程安全。在一个程序中只需要一个
52、请解释说明hibernate配置文件中dialect属性的确切含义。
数据库方言:多种数据库拥有不同的语言语法,通过设置dialect指定所用的数据库,并生成对应语法和语言的sql语句。
53、请解释inverse属性的作用
inverse表“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to-many的集合定义中使用,inverse="true"表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false)。 one-to-many维护关联关系就是更新外键。many-to-many维护关联关系就是在中间表增减记录。
54、请解释Hibernate查询中出现的N+1问题,并提出解决方案。
Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略。这种检索策略存在两大不足:
(1) select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。
55、请简要的描述一下使用Hibernate二级高速缓存的经验
1.Hibernate3的二级缓存和session级别的缓存一样都只对实体对象做缓存,不对属性级别的查询做缓存;二级缓存的生命周期和sessionFactory的生命周期是一样的,sessionFactory可以管理二级缓存;
2.sessionFactory级别的缓存,需要手动配置;所有的session可以共享sessionFactory 级别的缓存;(一般把一些不经常变化的实体对象放到sessionFactory级别的缓存中,适合放不经常变化的实体对象。)
3.Hiberante3二级缓存的配置和使用方法如下:
必须把ehcache.jar包导入,然后到Hibernate3.2的etc文件下把ehcache.xml复制到工程src目录下(ehcache.xml里边的参数里边有详细英文说明);
说明:ehcache.jar是第三方法的缓存产品,hiberante只是把它做了集成,还有好多第三方hibernate集成的缓存产品,相关说明请查阅hiberante3开发手册;ehcache是不支持分布应用的,如果有分布式需求,请换成支持分布式的二级缓存产品,hiberate3开发手册都有相头说明。配置方法都类似);
4.Hibernate3的二级缓存默认是开起的,也可以指定开起。
56、Query的list和iterator方法的不同。
list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,如果在缓存里面有,就从缓存取,没有的话就去数据库load。
不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存
查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。
这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!
57、讲讲一下orm框架 以及各个orm框架的区别 o---Object对象。 r---关系数据库。 m---映射文件。 orm可以说是一项为了实现面向对象与关系数据库的不匹配而产生的一种框架,简单的说:orm是通过使用描述对象与关系数据库之间映射的元数据, 将Java程序中的对象通过自动持久化同步到关系数据库,本质上来说就是把数据从一种形式转换成另一种形式。 orm类型 映射关系 开发效率 数据库移植 hibernate: 全自动 实体类和数据库 自动生成sql 不同数据库类型的支持 mybatis: 半自动 实体类和SQL语句 需要编写具体的sql 标准SQL方便移植
58、hibernate 的原理及其步骤 1.读取配置(Configuration)*.cfg.xml。 2.使用Configuration创建SessionFactory(线程安全)。 3.使用SessionFactory创建Session(线程不安全,使用本地线程TheradLocal解决)。 SessionFactory提供了两种方式得到session。 1 openSession:得到的session是线程不安全的,可以通过本地线程 Threadlocal机制解决,把session放入Threadlocal中, 达到隔离线程的目的,得到的session需要手动关闭。 2 getCurrentSession可以解决要在事务范围内 session 线程不安全问题,因为它总是把session绑定到当前线程, 并且在事务提交的时候自动关闭session。 但需要配置:<property name="hibernate.current_session_context_class">thread</property><!--针对jdbc的--> 自动关闭session 4.利用Session启动事务(Transaction)。 5.利用Session进行业务操作。 6.提交事务(调用commit()方法)。 7.关闭Session(调用close()方法)。
59、说出Hibernate的核心类和接口: 答: Configuration:管理从配置文件(*.cfg.xml)中读取的信息。 SessionFactory:根据配置信息创建一个SessionFactory实例。 Session:持久化方法是通过Session来完成的。 Transaction: 事务管理。 Query:查询接口。 SQLQuerry:sql查询的查询接口。
60、 什么是hibernate主键生成机制 举例说明 答: 主键生成机制就是通过在*.hbm.xml文件中generator标签里进行配置,通过使用不同的主键机制而产生不同的主键性能。 如:assigned,主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用session.save()之前要指定主键值。 hilo 会多生成一个表,使用高低位算法生成主键。 increment:由hibernate管理主键,自动以递增的方式生成字段值,每次增量为1,该主键必须为Integer类型。 identity:由数据库生成标识符,identity是由数据库自己生成的,但这个主键必须设置为自动增长,前提条件是数据库支持自动增长字段类型, 有可能增长的幅度不一定为1。 uuid:数据类为String类型,由 Hibernate 通过128位uuid算法生成16进制数值(编码后以长度32的字符串表示)作为主键。 sequence: 采用数据库提供的 sequence(序列) 机制生成主键。如 Oralce 中的Sequence。 foreign :使用另外一个相关联的对象的标识符作为主键。 native: 由Hibernate根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式。
61、阐述一下hibernate的映射 在关联映射中一对多 和 多对多 是如何实现的 要注意什么问题? 映射分为四种: 1 基本类型的映射。 2 集合映射:加一个表,主键关联起来,1 list映射 有序可重复,2 set 无序不可重复,3 bag 无序可重复,4 map映射。 3 级联映射:1 一对一,2 一对多,3 多对多 分为单向映射(一方持有另一方的实体或集合),双向映射(彼此持有对方的实体或集合)。 1. 一对一:1 外键关联,把外键对应的字段加上唯一约束。 2 主键关联,双方共用一个主键,采用的主键生产机制是foreign。 2. 一对多:使用的是级联与反转。 1. 一般使用的是双向关联,在一方放多方的集合,在多方放一方的实体。 2. 反转:1. 如果不用反转 hibernate默认由一方维护关联关系的字段,做法是一个一个的对集合里面的数据进行插入, 关联字段为空,之后发一条一条的uodate语句去更新关联字段。 2. 如果用反转 表明将关联关系维护权交给另一方(多方)管理,由多方维护关联字段的话,不会产生update语句, 带来了性能上的提升。 3. 使用反转:在one-to-many或many-to-many的集合定义中使用,inverse=“true”表示该对象不维护关联关系, 该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false), one-to-many维护关联关系就是更新外键,many-to-many维护关联关系就是在中间表增减记录。 3. 级联:当主对象做操作时候其关联的对象也做与主对象类似的操作,可以减少代码,一般在一方使用, 因为如果在多方使用的话那就没什么意义了。 注意:级联时给关联对象设置id时需要注意,hibernate会对关联的对象进行修改,如果关联对象在数据库不存在的时候会报错。 使用级联删除时需要关联对象持久化,否则报外键约束错误。 3. 多对多:加一个中间表,many-to-many,实则是转成两个一对多,而中间表是放了双方的id作为联合主键,和约束。 使用双向多对多的时候,使他们同时指向一个表,不能会创建两个中间表出来,但只有一个中间表会保存到数据, 并且要指明关联字段维护权的问题,一个使用inverse="false" 一个使用inverse="true",否则会出现主键重复的问题。 4 继承映射:1 每个类一张表,2 每个子类一张表,父类没有表,3 所有类一张表 5 组件映射:它只是一个普通的java类,没有映射文件,但是它不是hibernate的实体,而是实体的一部分,将可重用的部分提取出来, 使它可以进行重用。
62、hibernate的缓存是什么 用来干什么的 什么时候用什么方法刷新缓存 各种缓存里放的是什么东西 使用缓存应该注意什么 1. 缓存是什么:缓存是介于物理数据源与应用程序之间,是数据库数据在内存中的存放临时copy的容器, 其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用的运行性能。 2. 使用缓存的条件。 1.读取大于修改。 2.数据量不能超过内存容量。 3.可以容忍出现无效数据。 3. 一级缓存,是session共享级别的,hibernate内置的缓存,里面存放的实体,因为它是hibernate内置的缓存不能控制缓存的数量, 所以要注意大批量操作数据时可能造成内存溢出,可以用clear方法清除缓存中的内容。 4. 二级缓存,SessionFactory级共享,是放在配置文件中,实现为可插拔,里面存放实体。 1 如果使用二级缓存就需要在配置文件中配置开发二级缓存,使用evict(属性类.class)方法清除二级缓存。 2 开启二级缓存的步骤:二级缓存的配置和使用。 * 开启二级缓存,修改hibernate.cfg.xml文件 在hibernate的配置文件中使用property属性开启。 <property name="hibernate.cache.use_second_level_cache">true</property> * 指定缓存产品提供商,修改hibernate.cfg.xml文件 指定缓存产品的提供商(EhCache(衣艾去k其)),也是在配置文件中用property属性指定。 <property ame="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> * 指定那些实体类使用二级缓存(两种方法) * 在映射文件中采用<cache>标签 <cache usage="read-only"/> usage="read-only"是“只读”缓存策略,这个<cache>标签只能放在<class>标签的内部,而且必须处在<id>标签的前面。 * 在hibernate.cfg.xml配置文件中,采用<class-cache>标签。 <class-cache class="com.lxit.hibernate.Classes" usage="read-only"/> 注意:这个<class-cache>标签必须放在<mapping>标签的后面。 5. 查询缓存,Query,Criteria(查询缓存)由于命中率较低,所以hibernate默认是关闭的。 1 在配置文件中使用property属性打开查询缓存开关。<property name="hibernate.cache.use_query_cache">true</property> 2 在程序中手动启用查询缓存query.setCacheable(true)或criteria.setCacheable(true),是先读取查询缓存,再把读出来的数据放入查询缓存中。 SessionFactory中提供了evictQueries()方法用来清除缓存中的内容。 3 如果查询的是实体放入的是id,否则就放入整个结果集。 4 查询缓存里面放的是: 1 查询的list里面放的是实体,会把实体放入的一级缓存与二级缓存里面,则查询缓存里面放的是id, 会根据id到一级缓存与二级缓存里面去找相对于的数据,找不到会发sql到数据库中找。 2 如果查询的list里面放的不是实体,只是查某些字段,查询缓存里面放的是查询的结果集。 6. 使用缓存应该注意什么: 1 使用缓存时应该用于的系统是查询大于修改,因为查询出来的数据会放入缓存中下次直接从缓存中拿出来,不用数据库发sql进行查询, 这样大大提升了系统的性能,而修改过多每次查询都是不同的数据,每次都要重新到数据库中拿值,这样就降低了项目的性能。 2 数据量不能超过内存容量,否则会报系统内存溢出错误。 3. 使用一级缓存的问题,当Session关闭后里面的数据就没有了,因为不能控制,所以要注意大批量操作数据时可能造成内存溢出。 4. 使用二级缓存的问题,要注意它的命中率,如果命中率太低对系统性能的消耗会很大。 7. 查看二级缓存的命中率: (1) 在hibernate的配置文件中要打开命中率开关,<property name="hibernate.generate_statistics">true</property>。 (2) 通过sessionFactory.getStatistics().getSecondLevelCacheHitCount();,查看命中的次数(是从二级缓存中查询出来的)。 通过sessionFactory.getStatistics().getSecondLevelCacheMissCount());,查看没有命中的次数。
63、什么是懒加载(延迟加载) 懒加载用什么技术实现 如何解决session关闭导致的懒加载问题 解决的方案有缺点吗 懒加载:1. 当具体确定用到某个数据时才会去加载数据库,跟我们以前讲的JVM的类装载器差不多,用谁加载谁。 2. 懒加载使用的是代理技术来完成的,代理类就是加载对象的子类(系统定义的),如果把加载对象设置成final修饰那么就不能使用懒加载。 3. 当Session关闭时会造成懒加载的问题,可以使用OpenSessionInView模式来完成懒加载问题,通过过滤器来实现, 在最开始访问过滤器时就把Session打开,直到执行完所有命令返回结果后关闭Session,这样就不会出现懒加载问题。 4. 同样这种方式也带来了问题,事务扩大了,导致资源等待时间过长,session范围变大,如果加载页面的时间过长, 如网速比较慢,内存时间过长,导致系统性能下降,数据库连接不能及时释放。 概念:1 lazy的概念,指在需要数据的时候才发出sql。 2 lazy策略只是在session打开期间才是有效的。 3 Hibernate加载本身就是延迟加载,在*.hbm.xml配置文件中<class>里配置lazy="false"将其改为非延迟加载。 4 Hibernate属性的延迟加载,在*.hnm.xml配置文件中<property>里配置lazy="false"即可。 5 类的延迟加载并不影响属性的延迟加载。 6 连接抓取会使lazy失效。
64、讲讲一下hibernate的抓取策略 答: hibernate的抓取分为:抓取策略就是抓与当前对象有关联的实体或集合。 连接抓取 fetch="join":通过select语句使用外连接来加载其关联实体或集合。? 查询抓取 fetch="select":在查询当前班级表后会另外发送一条select语句抓取当前对象关联实体或集合。 子查询抓取 fetch="subselect":另外发送一条select语句抓取在前面查询到的所有实体对象的关联实体或集合,通过子查询in完成 批量抓取(batch-size="数量"):给的数量是多少就一次抓取多少条记录(前提是数据库中有那么多记录)。
65、hibernate如何处理大数据量? 1.使用循环方法进行小量同步来清除缓存,当循环方法加载完指定的数量后就调用flush把数据同步到数据库,然后调用clear清除缓存。 2.用(无状态Session)StatelessSession接口:它不和一级缓存、二级缓存交互,也不触发任何事件、监听器、拦截器, 通过该接口的操作会立刻发送给数据库,与JDBC的功能一样。 3.使用Query调用executeUpdate()执行批量更新,会清除相关联的类二级缓存,也可能会造成级联,和乐观锁出现问题。 4.使用分页机制,setFirstResult(2)表示从第3条记录开始。setMaxResults(10)取10条记录。
66、你是如何进行Hibernate的性能优化的 在使用查询数据时第一次使用list方法查询,以后使用迭代方式查询,因为list查询它只会把数据放入缓存中而不会使用缓存, 迭代器查询出的数据它不会放入缓存但是会使用缓存,有一点注意,迭代器不使用查询缓存,list会使用查询缓存。 处理大数据量时使用优化方法(请看第12题的优化方式)。 使用懒加载,关联数据,主键生成机制也是对系统的一种优化。 根据命中率使用缓存,命中率过底部建议使用缓存。 在页面显示时如果有视频就尽量使用懒加载。 关联时使用双向双向映射。
67、讲讲hibernate的监听器 它有什么作用 细说一下hibernate保存数据的步骤? 答: 1. hibernate的数据保存是通过缓存来实现的,通过Configuration加载配置文件来创建SessionFactory,SessionFactory是线程安全的, 使用SessionFactory创建Session,创建Session有两种方式,使用openSession得到的Session是线程不安全的,可以通过本地线程解决这个问题, 需要手动关闭Session,还可以使用getCurrentSession得到的Session是线程安全的,它是自动关闭Session,通过Session调用save或saveOrUpdate方法, 调用方法时会触发一个事件,事件会通知监听器,触发监听器里的方法,然后把对象放进缓存登入动作,等待flush(福拉西)刷新, 提交事务,从缓存中获取数据同步到数据库,一般是指定字符串id。 调用两次flush是否会发两条sql,不会,因为它第一次会发送sql,但是它会登记的动作清除掉,flush是根据登记的动作发送sql的。 2. 自定义监听器 1 实现监听器接口 如:SaveOrUpdateEventListener 重写onSaveOrUpdate方法。 2 注册监听器 两种形式: 1 在hibernate的xml里面根据方法配置。 2 通过编程式,可以多个监听器类型之间共享监听器的实例,配置的每次都会产生一个新实例。 hibernate保存数据:调用save方法把对象放进缓存登入动作,等待flush(福拉西)刷新,提交事务,从缓存中获取数据同步到数据库。
68、spring是如何整合hibernate的 1 用spring提供的LocalSessionFactoryBean,创建Sessionfactory, 注入数据源dataSource,hibernate属性hibernateProperties和映射文件。 2 给HibernateTemplate类注入sessionFactory的实例,之后就可以用HibernateTemplate类封装的hibernate的api操作。 3 继承 HibernateDaoSupport类使代码更加简洁,给HibernateTemplate类注入sessionFactory, 之后就可以用它的getHibernateTemplate方法获得模板类。 4 事务整合,采用hibernate的事务管理器。
69、什么是离线查询 答:组装sql时需要Session,查询时不需要Session这是离线查询。 离线查询利用DetachedCriteria类使你在一个session范围之外创建一个查询,并且可以使用任意的Session来执行它,用在动态sql的构建上。 简单说明:就是在Session的范围之外创建查询方式。
70、 hibernate查询单条记录有几种方法 唯一记录使用Session调用uinqueResult方法,当你确定本次查询只有一条记录可以用uinqueResult返回一个实体对象。 get方法 load方法。
71、hibernate为什么能跨数据库? 答:因为hibernate是ORM模式的框架,ORM就一项专门为了解决面向对象与关系数据库不匹配而产生的一项技术, 因此在它的底层封装了很多数据库的方言,实现了跨数据库的功能,根据配置的方言,产生不同的sql,使用URL通过元数据得到需要使用什么数据库。 使用hibernate碰到什么问题? 1. 大数据量问题。 2. hibernate最好不要与其它系统共存,如:JDBC。 3. 使用一级缓存的问题,当Session关闭后里面的数据就没有了,因为不能控制,所以要注意大批量操作数据时可能造成内存溢出。 4. 使用二级缓存的问题,要注意它的命中率,如果命中率太低对系统性能的消耗会很大。 5. 使用缓存都会有存在无效数据的问题,使用缓存应该注意查询要大于修改,而且数据量不要过大。
72、性能优化
1.懒加载 2.List 和 iterator 3.批量处理 4.无状态session 5.分页