【面试】【Hibernate常见问题总结】【01】

【常见面试问题总结目录>>>】

001 hibernate是什么?

  hibernate是基于ORM对象关系映射(完成对象数据到关系数据映射的机制)实现的,做数据持久化的工具

002 为什么使用Hibernate?

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

003 inverse的好处。

  在关联关系中用inverse在控制由哪一端来控制关联关系。这样做有什么好处呢?举customer和order的例子来说。他们是一对多的关系,如果只单向关联,且由customer控制关联关系,则如果我想添加一个order,则先取customer, 然后getOrders得到所有的order集合,然后往集合里面多加入一个order,然后save(customer), 这样开销太大。 如果改双向关联且由order主控关系,则如果想为customer增加一个order, 则new一个order,然后给order设置customer,然后save(order)即可。

004 merge的含义:

  如果session中存在相同持久化标识(identifier)的实例,用户给出的对象的状态覆盖旧有的持久实例。如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例最后返回该持久实例用户给出的这个对象没有被关联到session上,它依旧是脱管的。

005 persist和save的区别

  persist不保证立即执行,可能要等到flush;persist不更新缓存;save, 把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,所以它会立即执行Sql insert

006 说出下面cascade属值表示的含义

  cascade用来指示在主对象和它包含的集合对象的级联操作行为,即对对象的更新怎么影响到子对象;
  save-update:级联保存(load以后如果子对象发生了更新,也会级联更新). 但它不会级联删除
  delete:级联删除, 但不具备级联保存和更新
  all-delete-orphan:在解除父子关系时,自动删除不属于父对象的子对象, 也支持级联删除和级联保存更新.
  all:级联删除, 级联更新,但解除父子关系时不会自动删除子对象.
  delete-orphan:删除所有和当前对象解除关联关系的对象

007 session.commit 和flush区别

  commit会先调用flash执行session清理,然后提交事物; flash执行session,但不一定提交事物(因为事物可能被委托给外围的aop代理来做);
session清理的顺序
  insert -> update -> delete -> 对集合进行delete –> 对集合的insert

008 Hibernate Session清理缓存的三种模式

  Session的setFlushMode()方法用于设定清理缓存的时间点。FlushMode类定义了三种不同的清理模式:FlushMode.AUTO、FlushMode.COMMIT和FlushMode.NEVER。例如,以下代码显示把清理模式设为FlushModo.Commit:session.setFlushMode(FlushMode.COMMIT);
【面试】【Hibernate常见问题总结】【01】_第1张图片
  FlushMode.AUTO是默认值,这也是优先考虑的清理模式,它会保证在整个事务中,数据保持一致。如果事务公包含查询数据库的操作,而不会修改数据库的数据,也可以选用FlushMode.COMMIT模式,这可以避免在执行Session查询方法时先清理缓存,以稍微提高应用程序的性能。

009 检索策略

  立即检索,lazy=false;延迟加载:lazy=true;预先抓取: fetch=“join”;

010 N+1问题。

  Hibernate中常会用到set,bag等集合表示1对多的关系, 在获取实体的时候就能根据关系将关联的对象或者对象集取出。解决方法一个是延迟加载, 即lazy=true;一个是预先抓取, 即fetch=join;

011 Hibernate主键生成策略?

  1、自动增长identity
  适用于MySQL、DB2、MS SQL Server,采用数据库生成的主键,用于为long、short、int类型生成唯一标识
  使用SQL Server 和 MySQL 的自增字段,这个方法不能放到 Oracle 中,Oracle 不支持自增字段,要设定sequence(MySQL 和 SQL Server 中很常用)
  数据库中的语法如下:
    MySQL:create table t_user(id int auto_increment primary key, name varchar(20));
    SQL Server:create table t_user(id int identity(1,1) primary key, name varchar(20));
  2、sequence
  DB2、Oracle均支持的序列,用于为long、short或int生成唯一标识
  数据库中的语法如下:
    Oracle:create sequence seq_name increment by 1 start with 1;
  需要主键值时可以调用seq_name.nextval或者seq_name.curval得到,数据库会帮助我们维护这个sequence序列,保证每次取到的值唯一,如:
    insert into tbl_name(id, name) values(seq_name.nextval, ‘Jimliu’);
  seq_name
    如果我们没有指定sequence参数,则Hibernate会访问一个默认的sequence,是hibernate_sequence,我们也需要在数据库中建立这个sequence
    此外,sequence还可以有另外一个参数是paramters,可以查看Hibernate的API了解它的用法,见org.hibernate.id.SequenceGenerator
    调用数据库的sequence来生成主键,要设定序列名,不然hibernate无法找到:
    NAME_SEQ(Oracle中很常用)
  3、hilo
  使用一个高/低位算法生成的long、short或int类型的标识符,给定一个表和字段作为高位值的来源,默认的表是hibernate_unique_key,默认的字段是next_hi。它将id的产生源分成两部分,DB+内存,然后按照算法结合在一起产生id值,可以在很少的连接次数内产生多条记录,提高效率
  MySQL:create table hi_value(next_hi integer not null);
  insert into hi_value(next_hi) values(1);
  在hibernate持久化的时候,由hibernate负责生成低位值。hilo标识符生成器在生成标识符时需要从hi_value表中取出next_hi的当前值,然后修改该值,这个操作是在单独的事务中完成的。最大的低值在属性max_lo中配置,但在Hibernate内存中生成的低位值超过此值时,就有需要到数据库的hi_value表中再次读取高位值了
  使用hilo生成策略,要在数据库中建立一张额外的表,默认表名为hibernate_unique_key,默认字段为integer类型,名称是next_hi(比较少用)
  我们也可以自己设置自定义的表名和字段名
  4、native
  会根据底层数据库的能力,从identity、sequence、hilo中选择一个,灵活性更强,但此时,如果选择sequence或者hilo,则所有的表的主键都会从Hibernate默认的sequence或者hilo表中取。并且,有的数据库对于默认情况主键生成测试的支持,效率并不是很高,对于 oracle 采用 Sequence 方式,对于MySQL 和 SQL Server 采用identity(自增主键生成机制),native就是将主键的生成工作交由数据库完成,hibernate不管(很常用)
  5、seqhilo
  sequence和hilo的结合,hilo的高位由sequence产生,所以也需要底层数据库的支持,通过hilo算法实现,但是主键历史保存在Sequence中,适用于支持 Sequence 的数据库,如 Oracle(比较少用)seq_name 100
  6、increment
这个是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,因此所有的数据库都可以使用,但问题也随之而来,由于是Hibernate生成的,所以只能有一个Hibernate应用进程访问数据库,否则就会产生主键冲突,不能在集群情况下使用,插入数据的时候hibernate会给主键添加一个自增的主键,但是一个hibernate实例就维护一个计数器,所以在多个实例运行的时候不能使用这个方法
  7、uuid.hex
  使用一个128-bit的UUID算法生成字符串类型的标识符,UUID被编码成一个32位16进制数字的字符串。UUID包含:IP地址、JVM启动时间、系统时间(精确到1/4秒)和一个计数器值(JVM中唯一)
  hibernate会算出一个128位的唯一值插入
  uuid.string
  hibernate会算出一个16位的值插入
  8、assigned
  由应用程序负责生成主键标识符,往往使用在数据库中没有代理主键,使用的主键与业务相关的情况,如:
  这种主键的生成方式不建议使用,在数据库表设计时就应该使用代理主键(surrogate key),不应使用自然主键(natural key具有业务含义),在没有指定标签时,默认就是assigned主键的生成方式
  在插入数据的时候主键由用户自己添加,hibernate也不管
  9、foreign
  使用外部表的字段作为主键
  10、select
  使用触发器生成主键(主要用于早期的数据库主键生成机制,少用)
代理主键是指与业务无关且能唯一标识数据库中记录,一般是数据库自动生成的,比如mysql可以使用auto_increment,Sql2000可以使用identity生成方式,oracle可以使用sequence生成方式 自然主键指业务相关,由用户指定,且能唯一标识数据库中的任意一条记录
  简介版
  increment:代理主键,适合于所有数据库,由hibernate维护主键自增,和底层数据库无关,但是不适合于2个或以上hibernate进程。
  identity:代理主键,适合于Mysql或ms sql server等支持自增的dbms,主键值不由hibernate维护。
  sequence:代理主键,适合于oracle等支持序列的dbms,主键值不由hibernate维护,由序列产生。
  native:代理主键,根据底层数据库的具体特性选择适合的主键生成策略,如果是mysql或  sqlserver,选择identity,如果是oracle,选择sequence。
  hilo:代理主键,hibernate把特定表的字段作为hign值,生成主键值
  uuid.hex:代理主键,hibernate采用uuid 128位算法生成基于字符串的主键值
  assign:适合于应用程序维护的自然主键。

012 HttpSession与Hibernate中Session的区别

  1. javax.servlet.http.HttpSession是一个抽象接口
用途:存放这个用户的一些经常被用到的信息,例如:用户名,权限等
  2. 它是hibernate操作数据库的一个句柄对象。手动或者通过业务层获取

013 Hibernate介绍

  Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

014 Hibernate有哪几种查询数据的方式

  (1)导航对象图查询
  (2)OID查询
  (3)HQL
  (4)QBC,QBC查询就是通过使用Hibernate提供的Query By Criteria API来查询对象
  (5)本地SQL

015 load()和get()的区别

  load加载方法:
    Users user = (Users)session.load(Users.class, userId);
  get加载方法:
    Users user = (Users)session.get(Users.class, userId);
  两加载方法区别:
  区别1:如果数据库中,没有userId的对象。如果通过get方法加载,则返回的是一个null;如果通过load加载,则返回一个代理对象,如果后面代码如果调用user对象的某个属性(比如user.getPassword())会抛出异常:org.hibernate.ObjectNotFoundException;
  区别2:load支持延迟加载,get不支持延迟加载。
  也就是说:Users user = (Users)session.load(Users.class, userId); 这句代码不会去执行数据库查询,只有用到user时才会去执行数据库查询。而:Users user = (Users)session.get(Users.class, userId); 则立即去执行数据库查询。 所以Users user = (Users)session.load(Users.class, userId);不会执行任何sql。
  注意:
    Users user = (Users)session.load(Users.class, userId);
    System.out.println(user.getId());
  上面这2句代码,不会去执行数据库操作。因为load后会在hibernate的一级缓存里存放一个map对象,该map的key就是userId的值,但是当你getId()时,它会去一级缓存里拿map的key值,而不去执行数据库查询。所以不会报任何错。不会执行任何数据库操作。

016 解释Session的load方法和get方法的不同;以及Query的list和iterator方法的不同

  load():现在session缓存中寻找,如果找到了返回该对象,如果没找到,程序会新建一个对象,并且返回,存在session中,不会在数据库中查找,在程序中如果没有用到这个实体类,可以节省一次数据库连接。如果在程序中用到了这个实体类,这个实体类就会在数据库去查找,如果没有查找到就会抛异常。(延时加载)
  get():在缓存在查找,如果找到该对象,返回该对象。如果没找到,会到数据库中查找,如果数据库中有则返回该对象,如果没找到这返回空。(立即加载)

017 Hibernate工作原理?

  原理:
  1. 读取并解析配置文件
  2. 读取并解析映射信息,创建SessionFactory
  3. 打开Sesssion
  4. 创建事务Transation
  5. 持久化操作
  6. 提交事务
  7. 关闭Session
  8. 关闭SesstionFactory

018 为什么要用Hibernate

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

019 Hibernate是如何延迟加载?

  1. Hibernate2延迟加载实现:
    a)实体对象 lazy=”true”
    b)集合(Collection)通过使用自定义集合类的实现,Hibernate实现了集合类型的延迟加载。
  2. Hibernate3 提供了属性的延迟加载功能
  当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。

020 Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)

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

021 说下Hibernate的缓存机制

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

022 Hibernate的查询方式

  Sql、
  Criteria、
  object comptosition、
  Hql:
    1、 属性查询
    2、 参数查询、命名参数查询
    3、 关联查询
    4、 分页查询
    5、 统计函数

023 如何优化Hibernate?

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

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

  当对象由瞬变状态(Transient)一save()时,就变成了持久化状态。当我们在Session里存储对象的时候,实际是在Session的Map里存了一份,也就是它的缓存里放了一份,然后,又到数据库里存了一份,在缓存里这一份叫持久对象(Persistent)。Session 一 Close()了,它的缓存也都关闭了,整个Session也就失效了,这个时候,这个对象变成了游离状态(Detached),但数据库中还是存在的。当游离状态(Detached)update()时,又变为了持久状态(Persistent)。当持久状态(Persistent)delete()时,又变为了瞬时状态(Transient),此时,数据库中没有与之对应的记录。

025 请解释说明Hibernate控制下的POJO会呈现三种状态,分别是transient、persistenet和detached,请解释这三种状态

  Transient瞬变状态:数据库中没数据。跟session不相关。没存过。
  Detached游离态:在数据库中有记录,但是在session中没有。需要手工同步。
  Persistenet持久态:数据库中有记录,session中也有这记录。自动更新

026 在数据库中条件查询速度很慢的时候,如何优化?

  1.建索引
  2.减少表之间的关联
  3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
  4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据.

027update()和saveOrUpdate()的区别?

  update()和saveOrUpdate()是用来对跨Session的PO进行状态管理的。
  update()方法操作的对象必须是持久化了的对象。也就是说,如果此对象在数据库中不存在的话,就不能使用update()方法。
  saveOrUpdate()方法操作的对象既可以使持久化了的,也可以使没有持久化的对象。如果是持久化了的对象调用saveOrUpdate()则会更新数据库中的对象;如果是未持久化的对象使用此方法,则save到数据库中

028 Hibernate中对象的三种状态

029 在hibernate中进行多表查询,每个表中各取几个字段,也就是说查询出来的结果集并没有一个实体类与之对应,如何解决这个问题?

  解决方案一,按照Object[]数据取出数据,然后自己组装bean。
  解决方案二,对每个表的bean写构造函数,比如表一要查出field1,field2两个字段,那么有一个构造函数就是Bean(type1 filed1,type2 field2) ,然后在hql里面就可以直接生成这个bean了。

030 Hibernate对象的三种状态是什么?

  瞬时态(Transient)持久态(Persistent)脱管态(Detached)。处于持久态的对象也称为PO(Persistence Object),瞬时对象和脱管对象也称为VO(Value Object)。
  瞬时态
  由new命令开辟内存空间的java对象,eg. Person person = new Person(”amigo”, “女”);
  如果没有变量对该对象进行引用,它将被java虚拟机回收。瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系,在Hibernate中,可通过session的save()或 saveOrUpdate()方法将瞬时对象与数据库相关联,并将数据对应的插入数据库中,此时该瞬时对象转变成持久化对象。
  持久态
  处于该状态的对象在数据库中具有对应的记录,并拥有一个持久化标识。如果是用hibernate的delete()方法,对应的持久对象就变成瞬时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联。
  当一个session执行close()或clear()、evict()之后,持久对象变成脱管对象,此时持久对象会变成脱管对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下。
  持久对象具有如下特点
    1. 和session实例关联;
    2. 在数据库中有与之关联的记录。
  脱管态
  当与某持久对象关联的session被关闭后,该持久对象转变为脱管对象。当脱管对象被重新关联到session上时,并再次转变成持久对象。
  脱管对象拥有数据库的识别值,可通过update()、saveOrUpdate()等方法,转变成持久对象。
  脱管对象具有如下特点
    1. 本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收;
    2. 比瞬时对象多了一个数据库记录标识值。

你可能感兴趣的:(Hibernate,面试,对象,状态,对象关系映射)