Hiberante4.0篇

1、在hibernate中,对象有三种状态:临 时状态(Transient)、持久状态(Persistent)和游离状态(Detached)。

处于持久态的对象也称为 PO(PersistenceObject),临时对象和游离对象也称为VO(ValueObject).

1)临时状态

由 new命令开辟内存空间的Java对象,例如:

User user=new User();

临时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系.

(a) 如果没有变量对该对象进行引用,它将被gc回收;

(b) 在Hibernate中,可通过 session的save()或saveOrUpdate()方法将瞬时对象与数据库相关联,并将数据对应的插入数据库中,此时该临时对象转变成持久化对 象.

2)持久状态

处于该状态的对象在数据库中具有对应的记录,并拥有一个持久化标识.通过session的get()、load()等方法获得的对象都是持久对象。

持久化对象被修改变更后,不会马上同步到数据库,直到数据库事务提交。在同步之前,持久化对象是脏的(Dirty)。

(a) 如果是用hibernate的delete()方法,对应的持久对象就变成临时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联.

(b) 当一个session执行close()或 clear()、evict()之后,持久对象变成游离对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下.

持久对象具有如下特点:

(1)和session实例关联;

(2)在数据库中有与之关联的记录,并拥有持久化标识.

3)游离状态

当与某持久对象关联的session被关闭后,该持久对象转变为游离对象.当游离对象被重新关联到session上时,又再次转变成持久对象(在Detached(分离)其间的改动将被持久化到数据库中)。游离对象拥有数据库的识别值,但已不在持久化管理范围之内。

(a) 通过update()、saveOrUpdate()等方法,游离对象可转变成持久对象.

(b) 如果是用hibernate的delete()方法,对应的游离对象就变成临时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联.

(c) 在没有任何变量引用它时,它将被gc在适当的 时候回收;

三种状态演化图

游离对象具有如下特点:

(1)本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收;

(2)比瞬时对象多了一个数据库记录标识值.例如:

Session session=factory.openSession();
User user=new User();
//user对象处于临时状态。
user.setName("张三");
user.setAge(18);
Transaction tran=Session.beginTransaction();
session.save(user);
//user对象转化为持 久状态。
tran.commit();

session.close();
//user对象转化为游离状态。
user.setName(" 李四");
Session session2=factory.openSession();
Transaction tran2=Session2.beginTransaction();
Session2.update(user);
//user对象 转化为持久状态。
tran2.commit();(游离状态中对象的变动在再次持久时会被持久化到数据库) 
Transaction tran3=Session.beginTransaction();
Session2.delete(user);
//user对象转 化为临时状态。
tran3.commit();
session2.close();

Session的不同操作对对象状态的影响:

Session 的save()方法
save()方法将一个临时对象转变为持久对象。
Session的update()方法
update()方法将一个游离对象转变为持久对象。
Session的lock()方法
调用lock()方法将对象同Session相关联而不强制更新。
Session 的merge()方法
拷贝指定对象的状态到具有相同对象标识符的持久对象。
Session的saveOrUpdate()方法
saveOrUpdate() 方法对于临时对象,执行save()方法,对于游离对象,执行update()方法。

Session的load()和get()方法

load() 方法和get()方法都可以根据对象的标识符加载对象,这两个方法加载的对象都位于Session的缓存中,属于持久对象。

Session的 delete()方法

delete()方法用于从数据库中删除与持久化对象对应的记录。如果传入的是一个持久化对象,Session就执行一条 delete语句。如果传入的参数是游离对象,先使分离对象与Session关联,使它变为持久化对象,然后才计划执行一个delete语句。

Session 的evict()方法

evict()方法从Session的缓存中删除一个持久对象。

save 和update区别:

save的作用是把一个新的对象保存update是把一个脱管状态的对象或自由态对象(一定要和一个记录对应)更新到数据库。

update和saveOrUpdate区别:

这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update,而update只是update;引用 hibernate reference中的一段话来解释他们的使用场合和区别,

通常下面的场景会使用update()或saveOrUpdate():程序在第一个session中加载对象,接着把session关闭该对象被传递到表现层,对象发生了一些改动,该对象被返回到业务逻辑层,最终到持久层程序,创建第二session调用第二个session的update()方法持久这些改动。

persist和save区别:

1)persist把一个瞬态的实例持久化,但是并"不保证"标识符(identifier主键对应的属性)被立刻填入到持久化实例中,标识符的填入可能 被推迟到flush的时候。

2)save, 把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,所以它会立即执行Sql insert。下面是详细的解释:

一方面是为了照顾JPA的用法习惯,另一方面是他们之间有一个区别:在save()方法保存持久化对象时,该方法返回该持久化对象的标识属性值(即对应记录的主键值);但使用persist()方法来保存持久化对象时,该方法没有任何的返回值,因为save()方法需要立即返回持久化对象的标识符属性值,所有程序执行save()方法会立即将持久化对象对应的数据插入数据库;而persist()则保证当它在一个事务外部被调用时,并不立即转换成insert语句。这个功能是很有用的,尤其是需要封装一个长回话流程的时候,persist()方法就显得尤为重要了。

saveOrUpdate,merge和update区别

比较update和merge,update的作用是把一脱管状态的对象或自由态对象(一定要和一个记录对应)更新到数据库。Merge是:如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象覆盖session已有的持久实例
(1)当我们使用update的时候,执行完成后,会抛出异常
(2)但当我们使用merge的时候,把处理自由态的po对象A的属性copy到session当中处于持久态的po的属性中,执行完成后原来是持久状态 还是持久态,而我们提供的A还是自由态。

flush和update区别:

update操作的是在自由态或脱管状态(因session的关闭而处于脱管状态)的对象,而flush是操作的在持久状态的对象。默认情况下,一个持久状态的对象的改动(包含set容器)是不需要update的,只要你更改了对象的值,等待hibernate flush就自动更新或保存到数据库了。hibernate flush发生在以下几种情况中

1)调用某些查询的和手动flush(),session的关闭、SessionFactory关闭结合get()一个对象,把对象的属性进行改变,把资源关闭。
   2)transaction commit的时候(包含了flush)

lock和update区别:

update是把一个已经更改过的脱管状态的对象变成持久状态,lock是把一个没有更改过的脱管状态的对象变成持久状态(针对的是因Session的关闭而处于脱管状态的po对象(2),不能针对因delete而处于脱管状态的po对象)对应更改一个记录的内容,两个的操作不同:

update的操作步骤是:

(1)属性改动后的脱管的对象的修改->调用update
lock的操作步骤是:

(2)调用lock把未修改的对象从脱管状态变成持久状态-->更改持久状态的对象的内容-->等待flush或者手动flush

clear和evcit的区别:

clear完整的清除session缓存
evcit(obj)把某个持久化对象从session的缓存中清空。

10、Hibernate Session的saveOrUpdate()方法:

saveOrUpdate()方法同时包含了save()与update()方法的功能,如果传入的参数是临时对象,就调用save()方法;如果传入的参数是游离对象,就调用update()方法;如果传入的参数是持久化对象,那就直接返回。那么,saveOrUpdate()方法如何判断一个对象处于临时状态还是游离状态呢?如果满足以下情况之一,Hibernate就把它作为临时对象。

11、Hibernate延迟加载(get和load的区别):

在hibernate中我们知道如果要从数据库中得到一个对象,通常有两种方式,一种是通过session.get()方法,另一种就是通过session.load()方法,然后其实这两种方法在获得一个实体对象时是有区别的,在查询性能上两者是不同的。

一.load加载方式

当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象。

这个时候我们可能会想,那么既然调用load方法时,并不会发出sql语句去从数据库中查出该对象,那么这个User对象到底是个什么对象呢?

其实这个User对象是我们的一个代理对象,这个代理对象仅仅保存了id这个属性:

二、get加载方式

相对于load的延迟加载方式,get就直接的多,当我们使用session.get()方法来得到一个对象时,不管我们使不使用这个对象,此时都会发出sql语句去从数据库中查询出来:

因此我们可以看到,使用load的加载方式比get的加载方式性能要好一些,因为load加载时,得到的只是一个代理对象,当真正需要使用这个对象时再去从数据库中查询。

三、使用get和load时的一些小问题

当了解了load和get的加载机制以后,我们此时来看看这两种方式会出现的一些小问题:

①如果使用get方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报NullPointException的异常

这是因为通过get方式我们会去数据库中查询出该对象,但是这个id值不存在,所以此时user对象是null,所以就会报NullPointException的异常了。

②如果使用load方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报ObjectNotFoundException异常:

为什么使用load的方式和get的方式来得到一个不存在的对象报的异常不同呢?其原因还是因为load的延迟加载机制,使用load时,此时的user对象是一个代理对象,仅仅保存了当前的这个id值,当我们试图得到该对象的username属性时,这个属性其实是不存在的,所以就会报出ObjectNotFoundException这个异常了。

③org.hibernate.LazyInitializationException异常

这个异常是什么原因呢?还是因为load的延迟加载机制,当我们通过load()方法来加载一个对象时,此时并没有发出sql语句去从数据库中查询出该对象,当前这个对象仅仅是一个只有id的代理对象,我们还并没有使用该对象,但是此时我们的session已经关闭了,所以当我们在测试用例中使用该对象时就会报LazyInitializationException这个异常了。

所以以后我们只要看到控制台报LazyInitializationException这种异常,就知道是使用了load的方式延迟加载一个对象了,解决这个的方法有两种,一种是将load改成get的方式来得到该对象,另一种是在表示层来开启我们的session和关闭session。

12、Hibernate中hibernate.hbm2ddl.auto:属性的值

    设置当前创建SessionFactory时,是否根据持久化类的映射关系自动建立数据库表。该属性可以有validate、update、create、和create-drop四个值。如果设置为create,每次创建SessionFactory时都会重新建表,因此前面插入的数据会丢失;如果设置为create-drop每次显示的关闭SessionFactory时,程序会自动drop刚刚建立的数据表,如果是设置为update,每次创建SessionFactory时,如果数据库中没有与持久化类对应的表和数据,只是更新或插入数据,因此该值通常是update。

13、在比较openSession和getCurrentSession这两个方法之前,先认识一下这两个方法。

在进行配置信息管理时,我们一般进行一下简单步骤:

Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂
1. Session session = sf.getCurrentSession(); // 获得Session

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

对于上述的两个方法,有以下区别:

1 getCurrentSession创建的session会和绑定到当前线程,而openSession不会。

2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭(调用session的close()方法)

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)中,需要配置:

14、Hibernate推荐使用c3p0数据源,数据源的解释:

    数据源是一种提高数据库连接性能的常规手段,数据源会负责维持一个数据库连接池,当程序创建数据源实例时,系统会一次性地创建多个数据库连接,并把这些数据库连接放在连接池中。

14、Hibernate有五大核心接口,分别是:Session 、Transaction 、Query 、SessionFactory、Configuration 。这五个接口构成了Hibernate运行的基本要素,可以执行存取,持久化,事务管理等操作。

Session接口:

  Session接口 Session 接口对于Hibernate 开发人员来说是一个最重要的接口。然而在Hibernate中,实例化的Session是一个轻量级的类,创建和销毁它都不会占用很多资源。这在实际项目中确实很重要,因为在客户程序中,可能会不断地创建以及销毁Session对象,如果Session 的开销太大,会给系统带来不良影响。但是Session对象是非线程安全的,因此在你的设计中,最好是一个线程只创建一个Session对象。 session可以看作介于数据连接与事务管理一种中间接口。我们可以将session想象成一个持久对象的缓冲区,Hibernate能检测到这些持久对象的改变,并及时刷新数据库。我们有时也称Session是一个持久层管理器,因为它包含这一些持久层相关的操作, 诸如存储持久对象至数据库,以及从数据库从获得它们。需要注意的是,Hibernate的session不同于JSP 应用中的HttpSession。当我们使用session这个术语时,我们指的Hibernate 中的session,而我们以后会将HttpSesion 对象称为用户session。 

SessionFactory接口:

  SessionFactroy接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory,是线程安全的。

Transaction接口:

  Transaction接口负责事务相关的操作,一般在Hibernate的增删改中出现,但是使用Hibernate的人一般使用Spring去管理事务。

事务对象管理对象,通过session来获取Transation,包括事务的开启,提交,回滚。

Query接口:

Query负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。它的返回值一般是List。需要自己转换。

Configuration接口:

  Configuration对象用于配置并根启动Hibernate,Hibernate应用通过Configuration实例来指定对象—---关系映射文件的位置或者动态配置Hibernate的属性,然后创建SessionFactory实例。

15、Hibernate多对一、一对一、一对多、多对多的配置方法:

hihernate一对多关联映射(单向Classes----->Student)一对多关联映射利用了多对一关联映射原理

多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一

一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多

也就是说一对多和多对一的映射策略是一样的,只是站的角度不同

在一一端维护关系的缺点:
   如果将t_student表里的classesid字段设置为非空,则无法保存,因为不是在student这一端维护关系,所以student不知道是哪个班的,所以需要发出多余的update语句来更新关系。

16、Hibernate能够解决的问题

    能够解决对象和数据库不匹配,没有侵入性,在代码中不用继承Hibernate类或是实现任何的hibernate接口。专注于业务,提高程序员开发效率,还具有课移植性。

17、实现数据事务的基本操作步骤:

      1新建Configuration对象

Configuration cfg =new Configuration().configure();

如果hibernate.cfg.xml文件的名字被改掉,那么可以在configure(“指定文件名”);

      2通过Configuration创建SessionFactory对象

在3.x的版本中 

SessionFactory sessionfactory=cfg.bulidSessionFactory();

4.3x版本的写法

ServiceRegistryregistry=new StanderdServiceRegistryBuilder().applySettings(cfg.getProperties()).build();

SessionFactory sf=cfg.bulidSessionFactory(registry);

      3通过SessionFactory得到Session

Session  session=sf.openSession();

4通过session对象得到Transation对象

Transaction tx=session.beginTransaction();

      5保存数据

      6提交事务

      7关闭session

18、组合主键映射:

    编写组合主键的类,该类必须实现Serializable接口,并且生成对应的get/set方法,最好是实现equals和hashCode方法。大对象映射存储需要用Blob(图像,音视频)和Clod(字符串文件)。在hbm文件中要加入类型如(java.sql.Blob)

19、持久化的级联操作:

    从数据库的建模角度来说,俩个表之间的1-N关联关系总是用外键约束来表示,其中保留外键的数据表是从表,被从表参照的数据表称为是主表。

如果将从表记录也映射成持久化的组件,这些组件的生命周期总是依赖于父对象,Hibernate会默认的启用级联操作,不需要额外的操作。当父对象被保存时这些组件子对象也将被保存,父对象被删除时子对象也删除。

如果将从表记录映射成持久化实体,则从表实体也有了自己的生命周期,从而应该允许其他实体共享对它的引用,例如:从集合中移除一个实体,不意味着它可以被删除,所以Hibernate默认不启用实体到其他关联实体之间的级联操作。

对于关联实体来说,Hibernate默认不会启用级联操作,当父对象被保存时,它关联的子实体不会被保存,父对象被删除时,它关联的子实体不会被删除。为了启用不同持久化操作的级联行为,Hibernate定义如下级联风格:All(将所有的持久化操作都级联到关联实体),merger(将merger操作级联到关联实体),persist(将persist操作级联到关联实体),refresh(将refresh操作级联到关联实体),(remove将remove操作级联到关联实体)。

cascade和inverse有什么区别?

可以这样理解,cascade定义的是关系两端对象到对象的级联关系,而inverse定义的是关系和对象的级联关系。

all : 所有情况下均进行关联操作。

none:所有情况下均不进行关联操作。这是默认值。
save-update:在执行save/update/saveOrUpdate时进行关联操作。
delete:在执行delete时进行关联操作。

20、在持久化的实体类中如果有集合属性的时候注意:

    对于集合属性来说,Hibernate默认采用的是延迟加载策略,例如,对于持久化类Persion来说,有集合属性score,加载Persion的时候,score属性默认是不会加载的,如果session关闭,Persion实例将无法访问关联的属性。为了解决该问题,可以在Hibernate持久化注解中指定fetch=FetchType.EAGER来关闭延迟加载。解决该问题的另一种方法就是使用join fetch ;比如说Hql语句,

From Persion as p join fetch p.score  这样就可以在初始化Persion的时候,同时抓取该Persion关联的score集合的属性。

 使用fetch注意的点:

1)Fetch不应该与setMaxResult()或setFirstRedult()共用,应为这些操作是基于结果集的,而在预先抓取集合类时可能包含重复的数据,即无法预先知道精准的行数。

2)Fetch不能与独立的with条件语句一起使用。

3)如果在一次查询中fetch多个集合,可以查询返回笛卡尔积。

4)Full Fetch join与right join fetch 是没有任何的意义的。

21、关于二级缓存

    Hibernate包括俩个级别的缓存,默认开启的是Session的一级缓存,可选的是SessionFactory的二级缓存。

    Session级别的缓存不需要开发者关心,默认是有效的,当应用保存持久化实体,修改持久化实体是,Session并不会立即把这种改变flush到数据库,而是缓存到当前Session的一级缓存中,除非程序显示调用Sessionde 的flush()方法,或程序关闭Session时才会将这些改变一次性的flush到底层数据库中,通过这种缓存,可以减少与数据库的交互,从而提高数据库访问性能。

    SessionFactory级别的二级缓存是全局的,应用的所有Session都共享这个二级缓存,这个二级缓存是默认关闭的,必须有程序显示的打开,一旦开启了二级缓存,当session需要抓取数据时,Session将会先查找一级缓存,再查找二级缓存,只有当一级缓存和二级缓存中都没有需要抓取的数据时,才会去数据库的底层去查找。如果在配置文件中开启了二级缓存,那么久需要在设置该二级缓存的实现类。

    Session级别的一级缓存是局部缓存,它只对当前Session有效SessionFactory级别的二级缓存是全局缓存,它对所有的Session都有效。

posted @ 2016-12-27 09:24  静醉丶那份默念  阅读(75)  评论(0)    收藏  举报