hibernate 一对多_java面试题之Hibernate

1.为什么要使用 hibernate?

  • hibernate 是对 jdbc 的封装,大大简化了数据访问层的繁琐的重复性代码。
  • hibernate 是一个优秀的 ORM 实现,很多程度上简化了 DAO 层的编码功能。
  • 可以很方便的进行数据库的移植工作。
  • 提供了缓存机制,是程序执行更改的高效。

2.什么是 ORM 框架?

ORM(Object Relation Mapping)对象关系映射,是把数据库中的关系数据映射成为程序中的对象。

使用 ORM 的优点:提高了开发效率降低了开发成本、开发更简单更对象化、可移植更强。

什么是JPA,为什么要有JPA?

JPA的全称是Java Persistence API, 即JAVA 持久化API,是EJB 3.0 的专家推出,作为 JSR-220的一部分,简单点来说可以理解为是一个JAVA的标准规范,这个规范为对JAVA对象的持久化制定了一些标准的接口,也可以说,JPA是一个标准的ORM(对象关系映射)规范。

那么为什么要有JPA呢?时下ORM框架很多,不同的ORM框架相互之间并不兼容。SUN也意识到了,对于JAVA对象的持久化是一个重要的内容,提出这个规范,一方面是为了简化EJB中对于对象持久化的操作,另一方面,也希望通过制定统一规范,达到一统ORM标准的目的。

JPA虽然是为EJB服务,但并不对JAVA对象有特别的要求,它可以是一个普通的POJO,仅此而已。该规范主要包括三部分内容,即ORM映射元数据,标准接口API,以及查询语言。

要注意的是JPA只是一个接口规范,而不是实现,具体实现由各供应商来完成,目前的话,Hibernate,TopLink,OpenJPA都很好地实现了JPA接口。

要基于JPA操作数据库,需要有一个persistence.xml配置文件,具体的几个关键类是EntityManagerFactory, EntityManager, 通过EntityManager的实例,我们可以对JAVA对象进行操作,如persist, merge, remove, createQuery等。

3.hibernate 中如何在控制台查看打印的 sql 语句?

在 Config 里面把 hibernate. show_SQL 设置为 true 就可以。但不建议开启,开启之后会降低程序的运行效率。

4.hibernate 有几种查询方式?

三种:hql、原生 SQL、条件查询 Criteria。

一、HQL查询

• HQL(Hibernate Query Language)提供了丰富灵活的查询方式,使用HQL进行查询也是Hibernate官方推荐使用的查询方式。

• HQL在语法结构上和SQL语句十分的相同,所以可以很快的上手进行使用。使用HQL需要用到Hibernate中的Query对象,该对象专门执行HQL方式的操作。

二、QBC(Query By Criteria)查询

• Criteria对象提供了一种面向对象的方式查询数据库。Criteria对象需要使用Session对象来获得。

• 一个Criteria对象表示对一个持久化类的查询。

三、原生SQL查询:

参考:

https://blog.csdn.net/u010963948/article/details/16818043

hibernate查询一共6种方法

1.HQL查询

2.对象化查询Criteria方法

3.动态查询DetachedCriteria

4.例子查询

5.sql查询

6.命名查询

参考:https://www.cnblogs.com/huangjinyong/p/9067920.html


5.hibernate 实体类可以被定义为 final 吗?

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

不过,如果你的持久化类实现了一个接口而且在该接口中声明了所有定义于实体类中的所有public的方法的话,你就能够避免出现前面所说的不利后果。

6.在 hibernate 中使用 Integer 和 int 做映射有什么区别?

Integer 类型为对象,它的值允许为 null,而 int 属于基础数据类型,值不能为 null。

int是java的基本的数据类型 integer是int的包装类 在有的时候,比如集合只能放类而不能放基本的数据类型,这时候就需要将基本的的数据类型进行装箱操作,就是将它包装成类 hibeinate是完全面向对象的一个框架,所以在它里面,一般都将基本的数据类型包装成类进行操作,你也可以使用基本类型,只要在映射文件写清楚就可以了
初学者使用时往往会出问题,很多时候就是因为,类型不匹配。

7.hibernate 是如何工作的?

Hibernate的5个核心接口

Configuration:负责管理hibernate配置信息

SessionFactory:负责创建session实例

Session:是hibernate持久化操作的基础,提供了众多的持久化方法

Transaction:是hibernate进行事务操作的接口,是对实际事务实现的一个抽象,这些实现包括JDBC的事务,JTA中的UserTransaction,甚至可以是CORBA事务。

Query和Criteria接口:查询接口,用于向数据库查询对象,包装了HQL查询语言,采用了新的面向对象的查询方式。

原理:

1.通过Configuration config = new Configuration().configure();//读取并解析hibernate.cfg.xml配置文件

2.由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取并解析映射信息

3.通过SessionFactory sf = config.buildSessionFactory();//创建SessionFactory

4.Session session = sf.openSession();//打开Sesssion

5.Transaction tx = session.beginTransaction();//创建并启动事务Transation

6.persistent operate操作数据,持久化操作

7.tx.commit();//提交事务

8.关闭Session

9.关闭SesstionFactory

为什么要用hibernate:

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

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

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

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

8.get()和 load()的区别?

  • 数据查询时,没有 OID 指定的对象,get() 返回 null;load() 返回一个代理对象。
  • load()支持延迟加载;get() 不支持延迟加载。

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。

这里get和load有两个重要区别:

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

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

(对于load和get方法返回类型:好多书中都说:“get方法永远只返回实体类”,实际上并不正 确,get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加 载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数 据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。)

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

9.说一下 hibernate 的缓存机制?

hibernate 常用的缓存有一级缓存和二级缓存:

一级缓存:也叫 Session 缓存,只在 Session 作用范围内有效,不需要用户干涉,由 hibernate 自身维护,可以通过:evict(object)清除 object 的缓存;clear()清除一级缓存中的所有缓存;flush()刷出缓存;

二级缓存:应用级别的缓存,在所有 Session 中都有效,支持配置第三方的缓存,如:EhCache。

Hibernate 缓存包括两大类:Hibernate 一级缓存和 Hibernate 二级缓存:
1). Hibernate 一级缓存又称为“Session 的缓存”
,它是内置的,不能被
卸载。由于 Session 对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。在第一级缓存中,持久化类的每个实例都具有
唯一的 OID。
2). Hibernate 二级缓 存又称 为“SessionFactory 的缓存 ”
,由于
SessionFactory 对象的生命周期和应用程序的整个过程对应,因此 Hibernate
二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用
适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存
是可选的,是一个可配置的插件,在默认情况下,SessionFactory 不会启用这
个插件。
当 Hibernate 根据 ID 访问数据对象的时候,首先从 Session 一级缓存中查;
查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数
据库,把结果按照 ID 放入到缓存删除、更新、增加数据的时候,同时更新缓存。

N+1问题:

返回查询列表时获取对象的两种方法:

list() 方法会发出sql 查询出该对象;(一条sql)

iterator()方法 会发出取 id 列表的sql ,在查询相应的具体的某个学生信息时,会发出相应的SQL去取学生信息;

iterator()方法 来获得对象时,hibernate会先发出取出所有对象id 值 的sql ,大会需要查询某个对象 的具体信息时,hibernate会根据取出的 id 值 发出相应的SQL 语句从数据库中去取具体的对象信息,这就是典型的N+1 问题!

二级缓存的应用:

适合二级缓存的数据存储:

1 很少被修改的数据

2 不是很重要的数据,允许出现偶尔并发的数据

3 不会被并发访问的数据

不适合二级缓存的数据存储:

1 经常被修改的数据

2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发

3 与其他应用共享的数据。

hibernate中的持久化对象 :

hibernate中的持久化对象 (四种状态):

transient(瞬时态):通过new关键字直接获取;尚未与Session关联对象,失去引用的话,就会被JVM回收。一般就是直接New创建的对象。

persistent(持久态):通过get/load、Query查询获得;已经与当前session产生关联,并且相关联的session没有关闭,并且事务尚未提交。

detached(脱管态):无法直接获得;存在持久化OID,但没有与当前session关联,脱管状态改变hibernate不能检测到

DELETED(删除态)(可以不管,如果说三种状态,就是没有此种):调用Session的delete方法之后,对象就变成删除态,此时Session中仍然保存有对象的信息,对删除态对象的引用依然有效,对象可继续被修改。删除态对象如果重新关联到某个新的 Session 上(也就是执行持久化操作), 会再次转变为持久的(在DELETED其间的改动将被持久化到数据库)。

持久化对象之间的转变:看图

4354ce8424e7051c5aa1db75be4a94fd.png

8c38aced4232155f19f94d8d2d1c39e9.png

瞬时——持久:save、saveOrUpdate(都是通过session获得)

瞬时——脱管:对象.setID(1);为瞬时对象设置新的OID

持久——瞬时:delete(被删除持久化对象,不建议再次使用)

持久——脱管:evict(清除一级缓存中某个对象)、close(关闭Session,清除一级缓存)、clear(清除一级缓存所有对象)

脱管——瞬时:对象.setID(null);删除对象OID

脱管——持久:update、saveOrUpdate、lock

上面提到 OID 什么是OID呢?

什么是OID
为了在系统中能够找到所需对象,我们需要为每一个对象分配一个唯一的表示号。在关系数据库中我们称之为关键字,而在对象术语中,则叫做对象标识(Object identifier-OID).
通常OID在内部都使用一个或多个大整数表示,而在应用程序中则提供一个完整的类为其他类提供获取、操作。

OID(Object ID)应当没有任何业务相关含义
一个非常关键的问题是OID绝对不应当具有任何业务含义。因为任何有业务含义的列都有改变的可能性,而计算机社团多年来从关系数据库学到的最重要的一个事实之一就是:不要给你的关键字任何意义。如果你的用户决定改变业务含义,也许他们想要增加几个数字或把数字变为字母数字,那么你需要在任何用到这个关键字的地方进行改变。一个表中的主关键字内的任何东西都有可能被其他表作为外键。就算是一个简单的改变,譬如在你的客户号码马上增加一个数字,可能会造成极大的维护上的开销。在关系数据库中,这种OID策略被称之为代理关键字。

OID的唯一性

一个 OID必须在一个类层次中保持唯一,理想上应该在所有对象上都唯一。

在分配对象OID时需要考虑两个问题:

l OID唯一性的层次

l 如何计算OID

我们先来看看第一个问题,对很多面向对象的新手来说,他们对这个问题的认识往往不够深刻。这里涉及到三个层次上的唯一性:一个类内的唯一性,一个类层次之间的唯一性,以及所有类之间的唯一性。

譬如,给一个客户对象的OID是只对所有客户实例唯一,还是对所有的人员还是所有的对象。一个具有值76766的OID是可以分配给一个客户对象,一个员工对象,一个订单对象呢?还是只能分配给一个客户而不是员工(因为客户和员工位于同一个类层次之内),还是除了客户外什么都不能分配。这里问题与多态相关:一个客户以后可能成为员工,但一个订单对象则不会如此。为了避免在一个对象改变类型的时候需要重新分配OID,你至少应当保证在类层次级别上的唯一性。当然在所有对象上保持唯一可以完全避免类似问题,尽管在实现上可能会有些困难。

具体参考:

hibernate缓存机制详细分析_Java_飞翔小猪的博客-CSDN博客​blog.csdn.net
bddb67852cfbcd438ea59afa4b6f6acd.png

OID问题参考:

Hibernate 缓存机制(N+1问题)详解​blog.csdn.net
4dceb800373d1069ed3f64bbe13fe7bd.png

10.hibernate 对象有哪些状态?

hibernate中的持久化对象 (四种状态):

transient(瞬时态):通过new关键字直接获取;尚未与Session关联对象,失去引用的话,就会被JVM回收。一般就是直接New创建的对象。

persistent(持久态):通过get/load、Query查询获得;已经与当前session产生关联,并且相关联的session没有关闭,并且事务尚未提交。

detached(脱管态):无法直接获得;存在持久化OID,但没有与当前session关联,脱管状态改变hibernate不能检测到

DELETED(删除态)(可以不管,如果说三种状态,就是没有此种):调用Session的delete方法之后,对象就变成删除态,此时Session中仍然保存有对象的信息,对删除态对象的引用依然有效,对象可继续被修改。删除态对象如果重新关联到某个新的 Session 上(也就是执行持久化操作), 会再次转变为持久的(在DELETED其间的改动将被持久化到数据库)。

持久化对象之间的转变:看图

4354ce8424e7051c5aa1db75be4a94fd.png

8c38aced4232155f19f94d8d2d1c39e9.png

瞬时——持久:save、saveOrUpdate(都是通过session获得)

瞬时——脱管:对象.setID(1);为瞬时对象设置新的OID

持久——瞬时:delete(被删除持久化对象,不建议再次使用)

持久——脱管:evict(清除一级缓存中某个对象)、close(关闭Session,清除一级缓存)、clear(清除一级缓存所有对象)

脱管——瞬时:对象.setID(null);删除对象OID

脱管——持久:update、saveOrUpdate、lock

参考:https://www.cnblogs.com/aisam/articles/3625520.html

11.在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

  • getCurrentSession 会绑定当前线程,而 openSession 则不会。
  • getCurrentSession 事务是 Spring 控制的,并且不需要手动关闭,而 openSession 需要我们自己手动开启和提交事务。

区别:

1. openSession 从字面上可以看得出来,是打开一个新的session对象,而且每次使用都是打开一个新的session,假如连续使用多次,则获得的session不是同一个对象,并且使用完需要调用close方法关闭session。

2. getCurrentSession ,从字面上可以看得出来,是获取当前上下文一个session对象,当第一次使用此方法时,会自动产生一个session对象,并且连续使用多次时,得到的session都是同一个对象,这就是与openSession的区别之一,简单而言,getCurrentSession 就是:如果有已经使用的,用旧的,如果没有,建新的。

注意:在实际开发中,往往使用getCurrentSession多,因为一般是处理同一个事务(即是使用一个数据库的情况),所以在一般情况下比较少使用openSession或者说openSession是比较老旧的一套接口了;

对于getCurrentSession 来说,有以下一些特点:

1.用途,界定事务边界

2.事务提交会自动close,不需要像openSession一样自己调用close方法关闭session

3.上下文配置(即在hibernate.cfg.xml)中,需要配置:

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

(需要注意,这里的current_session_context_class属性有几个属性值:jta 、 thread 常用 , custom、managed 少用 )

a).thread使用connection 单数据库连接管理事务

b).jta (Java transaction api) Java 分布式事务管理 (多数据库访问),jta 由中间件提供(JBoss WebLogic 等, 但是tomcat 不支持)

getCurrentSession()的缺点:

由于是同一个session,容易造成session在各个线程中的同步问题。
也由于只有一个session,在request并发量很大的时候就效率会相对变低。

参考:https://blog.csdn.net/chuck_kui/article/details/54845235

12.hibernate 实体类必须要有无参构造函数吗?为什么?

hibernate 中每个实体类必须提供一个无参构造函数,因为 hibernate 框架要使用 reflection api,通过调用 ClassnewInstance() 来创建实体类的实例,如果没有无参的构造函数就会抛出异常。

原因:

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

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

提醒一点:

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

何为反射?参考这篇文章:https://blog.csdn.net/sinat_38259539/article/details/71799078

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值