Hibernate常见面试题

一、Hibernate工作原理

hibernate核心接口

session:负责被持久化对象CRUD操作
sessionFactory:负责初始化hibernate,创建session对象
configuration:负责配置并启动hibernate,创建SessionFactory
Transaction:负责事物相关的操作
Query和Criteria接口:负责执行各种数据库查询

hibernate工作流程

1、读取并解析Hibernate核心配置文件hibernate.cfg.xml
2、读取并解析Hibernate映射文件,创建SessionFactory
3、打开Sesssion
4、创建事务Transation
5、持久化操作
6、提交事务
7、关闭Session
8、关闭SesstionFactory

二、什么是 ORM 框架?

对象关系映射
即通过类与数据库表的映射关系,将对象持久化到数据库中

三、Hibernate三级缓存

Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据

1、一级缓存
每个 Session 对象创建出来,就会分配一块缓存空间,可以存储 session 对象访问的对象信息。 session 关闭后会自动清除缓存,手动清除可以用session.clear() , session.evict(obj) 。 Session 一级缓存是独享。
load/get/save/update/saveorupdate 方法处理的对象都会放入缓存中

1.一级缓存生命周期跟一个session的生命周期相同,最多一个请求
2.一级缓存就是一个Map对象.作用:便于对对象的管理
3.用session查询的对象,其实都是一级缓存的数据

原理:
用seession取对象A:

生成key==A类全限名+#A.id值
从一级缓存的map中找该key.
若没有找到,从数据中查找,并以查找到的对象为value,存入map.
若有找到,直接返回一级缓存中的对象,不再发SQL语句

优点:可以减少查询数据库的次数,加快查询速度。
缺点:在批量操作中容易导致内存溢出问题。

2、二级缓存

二级缓存是SessionFactory 对象缓存,可以被创建出的多个 Session 对象共享、二级缓存包含了一级缓存。

生命周期伴随整个应用;作用:提高性能
二级缓存是属于第三方的

二级缓存默认是关闭的,如果要使用需要手动开启,下面是开启过程:

a.导入ehcache 工具包和 ehcache.xml 配置文件(配置文件放到src路径下)

echache工具包包括:ehcache-core-2.4.3.jar,hibernate-ehcache-4.2.21.Final.jar,slf4j-api-1.6.1.jar

ehcache.xml 文件

 ehcache.xml

b.在 hibernate.cfg.xml 中配置参数开启二级缓存,启用 ehcache

	hibernate.cache.use_second_level_cache=ture  //使用二级缓存
    hibernate.cache.region.factory_class=EhCacheRegionFactory的全限名  //二级缓存是第三方,这儿选的是EhCache

映射文件中声明

<class>
    <cache usage="缓存策略" region="使用的缓存参数"/>
</class>

缓存策略

read-only:只读模式,使用不可以更改的表;若被更改,会报错
read-write:支持并发的读写,更改缓存数据时候,缓存上锁,其他事务会访问数据库,确保数据的正确性
nonstrict-read-write:不锁的读写,可能从map中取到假数据
transactional:轻量级jave ee 不支持

c.在要缓存的对象类型中,指定 @Cache 注解标记

@Entity
@Table(name="user")//表示对应的表名
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class User {
    //........
}

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

3、查询缓存
一级和二级缓存,只能缓存单个对象,如果需要缓存一个结果集,必须使用查询缓存。

手动将查询的列表结果拿去缓存,
	1 . 在Hibernate的配置文件中
	hibernate.cache.use_query_cache=true
	2 . 在查询列表前,声明要缓存
	- 先去map中查找,有就取出
	- 没有就去数据库查询,在放入map;

查询缓存默认也是关闭的,如需使用需要手动开启,下面是开启过程:

a.针对的对象必需已经开启了二级缓存

b.在 hibernate.cfg.xml 中添加开启查询缓存的配置

true
c.在查询执行前,调用 query.setCacheable(true);

下面看一看测试:

    String hql="from User";
    Configuration conf=new Configuration();
    conf.configure("hibernate.cfg.xml");
    SessionFactory factory=conf.buildSessionFactory();
    Session session1 = factory.openSession();
    Query query1 = session1.createQuery(hql);
    query1.setCacheable(true);//设置开启缓存
    List list1 = query1.list();
    for(Object user:list1){
        System.out.println(((User)user).getName());
    }
    System.out.println("------------------------");
    Session session2 = factory.openSession();
    Query query2 = session2.createQuery(hql);
    query2.setCacheable(true);
    List list2 =query2.list();
    for(Object user:list2){
        System.out.println(((User)user).getName());
    }

极少用原因:

1.两条query对象必须要有一致的HQL
2.存的是一个list,当一个成员改变,会导致整个list的改变,效率低.
3.只适用于,不能更改的list集合

四、Hibernate一对多、多对多配置

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

五、hibernate的三种状态之间如何转换

1、Transient 瞬时 :对象刚new出来,还没设id,设了其他值。

2、Persistent 持久:调用了save()、saveOrUpdate(),就变成Persistent,有id

3Detached 脱管 : 当session close()完之后,变成Detached。

六、hibernate中session的绑定

session

session是一种单实例对象 简单说就是自己用 别人不能用。在一些项目中很多人一起来操作 所以我们可以把session与我们的本地线程一起绑定,本地线程的特点就是执行一次 从创建到销毁。但我们使用完session后 别人可以再去使用。这种线程的绑定 底层使用的是threadLocal原理 而在hibernate中 hibernate框架已经帮我们实现了。

方法:

 1.在hibernate核心配置文件中配置。

 2.调用sessionFactory里面的方法实现

1.配置

<property name="hibernate.current_session_context_class">thread
</property>

2.调用方法实现

如果你有工具类 在工具类中调用

public static Session getSessionObject(){
        
        return sessionFactory.getCurrentSession();
    }

注意 : 在你运行前要把session关闭的代码去掉 以为当你运行结束时你的session就和本地线程一起关闭了

七、hibernate中的事务管理
在这里插入图片描述
在Hibernate框架中设置事务的隔离级别,可在hibernate.cfg.xml核心配置文件中添加如下配置:

<!--数值可设置为1、2、4、8-->
<property name="hibernate.connection.isolation">4</property>

hibernate中事务的应用
a、事务在service层管理。
b、确保service层和dao层使用的时同一个Connection,调用getCurrentSession方法是从ThreadLocal中获得与线程绑定的session。
c、getCurrentSession必须配置才能使用

<!--指定getCurrent获得session与当前线程绑定-->
<property name="hibernate.current_session_context_class">thread</property>

b、getCurrentSession方法获得的session对象,会在事务提交时自动关闭,不用手动关闭。

八、Hibernate是如何延迟加载?

1.、对于Hibernate get方法,Hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据 库中没有就返回null。这个相对比较简单,也没有太大的争议。主要要说明的一点就是在这个版本(bibernate3.2以上)中get方法也会查找二级缓存!

2.、Hibernate load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论:

(1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

(2)若为false,就跟Hibernateget方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

Hibernate中的事务管理

九、什么是SessionFactory,她是线程安全么

SessionFactory 是Hibrenate单例数据存储和线程安全的,以至于可以多线程同时访问。一个SessionFactory 在启动的时候只能建立一次。SessionFactory应该包装各种单例以至于它能很简单的在一个应用代码中储存.

十、hibernate 有几种查询方式?

1、对象导航查询(objectcomposition)
2、HQL查询

1、 属性查询
2、 参数查询、命名参数查询
3、 关联查询
4、 分页查询
5、 统计函数

3、Criteria 查询
4、SQLQuery本地SQL查询

十一、hibernate 实体类可以被定义为 final 吗?

你可以将Hibernate的实体类定义为final类,但这种做法并不好。因为Hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义成final类之后,因为 Java不允许对final类进行扩展,所以Hibernate就无法再使用代理了, 如此一来就限制了使用可以提升性能的手段。

十二、在 hibernate 中使用 Integer 和 int 做映射有什么区别?

hibernate的PO类中 经常会用到int 型得变量 这个时候如果使用基本类型的变量(int ) 如果数据库中对应的存储数据是 null 时使用PO类进行获取数据 会出现类型转换异常 如果使用的是对象类型(Integer)这个时候则不会报错。

十三、get()和 load()的区别?

如果未能发现符合条件的记录,Hibernate get方法返回null,而load方法会抛出一个ObjectNotFoundException。

load方法可返回没有加载实体数据的代 理类实例,而get方法永远返回有实体数据的对象。

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

十四、在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

1、getCurrentSession会绑定当前线程,而openSession不会,因为我们把hibernate交给我们的spring来管理之后,我们是有事务配置,这个有事务的线程就会绑定当前的工厂里面的每一个session,而openSession是创建一个新session。
2、getCurrentSession事务是有spring来控制的,而openSession需要我们手动开启和手动提交事务,
3、getCurrentSession是不需要我们手动关闭的,因为工厂会自己管理,而openSession需要我们手动关闭。
4、而getCurrentSession需要我们手动设置绑定事务的机制,有三种设置方式,jdbc本地的Thread、JTA、第三种是spring提供的事务管理机制org.springframework.orm.hibernate4.SpringSessionContext,而且srping默认使用该种事务管理机制

十五、hibernate 实体类必须要有无参构造函数吗?为什么?
首先答案是肯定的。

原因
Hibernate框架会调用这个默认构造方法来构造实例对象,即Class类的newInstance方法 ,这个方法就是通过调用默认构造方法来创建实例对象的 。

当查询的时候返回的实体类是一个对象实例,是Hibernate动态通过反射生成的。反射的Class.forName(“className”).newInstance()需要对应的类提供一个无参构造方法,必须有个无参的构造方法将对象创建出来,单从Hibernate的角度讲 他是通过反射创建实体对象的 所以没有默认构造方法是不行的,另外Hibernate也可以通过有参的构造方法创建对象。

提醒一点
如果你没有提供任何构造方法,虚拟机会自动提供默认构造方法(无参构造器),但是如果你提供了其他有参数的构造方法的话,虚拟机就不再为你提供默认构造方法,这时必须手动把无参构造器写在代码里,否则new Xxxx()是会报错的,所以默认的构造方法不是必须的,只在有多个构造方法时才是必须的,这里“必须”指的是“必须手动写出来”。

十六、如何优化Hibernate?

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

十七、为什么要用hibernate:

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

十八、比较hibernate的三种检索策略优缺点

1、立即检索

优点: 对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象; 
缺点: 1.select语句太多;2.可能会加载应用程序不需要访问的对象白白浪费许多内存空间; 

2、延迟检索

优点: 由应用程序决定需要加载哪些对象,可以避免可执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并且能节省内存空间; 
缺点: 应用程序如果希望访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化; 

3 、迫切左外连接检索

优点: 1对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便地冲一个对象导航到与它关联的对象。2使用了外连接,select语句数目少; 
缺点: 1 可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;2复杂的数据库表连接也会影响检索性能;

十九、什么是Hibernate的并发机制?怎么去处理并发问题?
Hibernate并发机制:

a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次, 然后就丢弃。

  如果一个Session 实例允许共享的话,那些支持并发运行的,例如Http request,session beans将会导致出现资源争用。 

  如果在Http Session中有hibernate的Session的话,就可能会出现同步访问Http Session。只要用户足够快的点击浏览器的“刷新”,  就会导致两个并发运行的线程使用同一个Session。  

b、多个事务并发访问同一块资源,可能会引发第一类丢失更新,脏读,幻读,不可重复读,第二类丢失更新一系列的问题。

解决方案:设置事务隔离级别。
Serializable:串行化。隔离级别最高
Repeatable Read:可重复读
Read Committed:已提交数据读
Read Uncommitted:未提交数据读。隔离级别最差
设置锁:乐观锁和悲观锁。
乐观锁:使用版本号或时间戳来检测更新丢失,在的映射中设置 optimistic-lock=”all”可以在没有版本或者时间戳属性映射的情况下实现 版本检查,此时Hibernate将比较一行记录的每个字段的状态 行级悲观锁:Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。类LockMode 定义了Hibernate所需的不同的锁定级别:LockMode.UPGRADE,LockMode.UPGRADE_NOWAIT,LockMode.READ;

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值