Hibernate详讲

一 概述

1.JPA

Java Persistence API,是Java EE为ORM框架定义的规范,任何使用java语言的ORM框架都必须实现该规范。Hibernate/Mybatis都是是JPA的一种实现。java

2.ORM

Object Relational Mapping,对象到关系的映射,在关系型数据库与对象之间创建映射关系,以实体对象操做关系型数据库。程序员

3.持久化

将对数据库的操做永久地反映到数据库中的过程叫作持久化。数据库

4.持久层框架

封装了对数据库进行持久化操做的框架叫作持久化框架。所谓框架就是在内部实现了一些经常使用的基本操做,提供简单的接口,缩减操做步骤,并且扩展了功能。缓存

二 数据库链接池

1.背景

数据库链接是一个极其有限的宝贵资源,在Web应用程序中表现得尤其突出。Web应用访问量大,而数据库支持的并发链接数目是有限的,链接越多,速度越慢,下降了用户体验。安全

在数据库链接池产生之前,访问数据库须要先创建链接,访问结束后关闭链接,下次须要时再重复建立与关闭过程,而数据库链接的建立与关闭自己消耗大量的系统资源,这时产生了共享数据库链接的思想,数据库链接池应运而生。session

2.数据库链接池的含义

  • 数据库链接池提供与管理用于访问数据库的链接。
  • 数据库链接池在初始化阶段会建立必定数量的数据库链接,投放到数据链接池中。
  • 用户访问数据库时不建立链接,而是从数据库链接池中获取链接,访问完毕后,不关闭链接,而是将链接归还链接池,以实现链接的重复使用。

3.经常使用数据链接池

  • C3P0是一种开放源代码的JDBC链接池。
  • DBCP。

4.配置数据源

Hibernate框架推荐使用C3P0:并发

<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

三 Hibernate简介

1.基本流程

2.Configuration

负责加载配置文件,启动Hibernate。配置文件的默认名称为“hibernate.cfg.xml”,放在类路径下。app

3.SessionFactory

负责建立Session对象,保存了当前数据库中全部的映射关系,线程安全。重量级对象,初始化过程耗费大量的资源,所以在应用程序中应避免建立多个SessionFactory对象。框架

4.Session

⑴含义

表明应用程序与数据库的一次会话,是Hibernate中持久化操做的核心,直接负责全部的持久化操做。ide

⑵非线程安全

session对象线程不安全,应避免多个线程共享一个Session对象。多个线程共享一个session对象,可能会由于session缓存的存在出现数据泄露,或者一个线程关闭了session对象,另外一个线程出现异常。

⑶依赖事务

Session只能在事务内部运行,即未开启事务,没法运行Session。

⑷建立

Session session=sessionFactory.openSession();
Session session=sessionFactory.getCurrentSession();
  • 第一种方式不须要配置,直接使用,第二种方式必须在配置文件中配置,配置以下:
    <property name="current_session_context_class">thread</property>
  • openSession每调用一次建立一个Session对象,getCurrentSession在同一个线程内部得到的是同一个Session对象。
  • 第二种方式建立的对象与线程绑定,第一种未与线程绑定。第二种方式建立的对象在事务提交或者回滚后自动关闭,第一种方式建立的链接必须手动关闭。

⑸关闭

  • Session一旦被关闭,就与线程解除了绑定关系,下一次经过该线程得到的是不一样的Session对象。
  • Session关闭之后,断开了与数据库的链接,就没法经过该Session对象访问数据库。

Session一旦关闭,就标志着应用程序与数据库的一次会话结束,再次通讯须要创建新的会话。

5.主键生成策略

生成策略 含义 维护方
increment 获取数据库中当前主键的最大值,加1做为新数据的主键 Hibernate
identity 按照自增方式生成主键 数据库
native 从底层数据库支持的主键生成策略中选择 数据库
assigned 由程序员手动生成主键 程序员
sequence 基于sequence表生成主键 数据库
uuid 生成一个全球惟一的32位主键  数据库
foreign 根据另外一张表的主键生成主键,造成一对一主键关联 Hibernate

四 持久化对象的状态

1.瞬时状态

对象被建立、未被Session引用时的状态,与数据库中的数据无链接,数据库不存在对应数据,一旦被Session经过save或者saveOrUpdate调用方法转化为持久化状态

2.持久化状态

对象被session引用,出如今session缓存时处于持久化状态。对象只有处于持久化状态,对其进行的操做才会持久到数据库中。

3.脱管状态

当Session对象关闭之后,持久化对象由持久状态转化为脱管状态,持久化对象处在脱管状态下仍然与数据库中的数据存在关联,数据库中存在对应数据,经过update或者saveOrUpdate转化为持久化状态。

脱管状态与瞬时状态对比:

  • 相同点:都不在session缓存中。
  • 不一样点:脱管状态的对象肯定存在于数据库中,主要用于修改数据库中的数据,经过update转化为持久化状态;瞬时状态的对象不存在于数据库中,主要用于插入数据,经过save转化为持久化状态。

4.集合角度

假如把关系型数据库中的表看作一个持久化类,每一行记录都是一个该类的对象,即持久化对象,一个对象若是在集合以外,则处于瞬时状态;在集合以内而且正在被Session调用,处在持久化状态;若是Session关闭,处在脱管状态。

 5.内存地址

对象的状态发生改变时,对象在内存中的地址并未发生改变,仍然是同一对象,只是对象与数据库的关系发生改变。

五 持久化操做

1.通常步骤

2.插入数据

session.save(Object obj);//底层生成一条insert语句

3.查询数据

⑴get

Object obj=(Object)session.get(Object.class,Serializable id);//底层生成一条select语句

⑵load

Object obj=(Object)session.get(Object.class,Serializable id);//底层生成一条select语句

返回对象的代理,在对象被调用时,才向数据库发送SQL语句。

4.删除数据

Object obj=session.get(Object.class,Serializable id);
session.delete(obj);//底层生成一条delete语句

5.修改数据

Object obj=session.get(Object.class,Serializable id);
obj.setter();//底层生成一条update语句

6.其余方法

  • clear():清空Session缓存,取消全部未执行的操做。
  • evict():当即将Session缓存中对象的引用变量赋值为null,不会生成SQL语句。

7.操做的本质

假定session缓存中保存的是对象自己,而不是对象的引用,那么清空session缓存,会销毁对象,测试一下:

    @Test
    public void testClear() { Session session = HibernateUtils.getSession(); try { session.beginTransaction(); Student stu = session.get(Student.class, 1); System.out.println("stu=" + stu); session.clear(); System.out.println("stu=" + stu); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }

输出:

很明显,session缓存清空之后,依然能够输出对象的详情,说明对象依然存在,从而得出结论:session缓存中保存的不是对象,而是对象的引用。

加载完成之后,在session缓存中创建一个对象的引用变量,而且使引用变量stu也指定该对象。

  • get()/load()/save(stu):在session缓存中创建引用变量stu,指向内存中的对象obj。
  • delete(stu):不是从内存中删除对象obj,而是使session缓存中的引用变量“stu0=null”。
  • update(stu):对数据库的修改直接来源于session缓存,因此在执行修改操做前必须保存session缓存中存在该对象,即session缓存中存在对象的引用。

六 同步时间点与刷新时间点

1.同步时间点

事务提交是Hibernate中惟一的同步时间点,事务提交时将Session缓存中的数据同步到数据库。

2.刷新时间点

遇到Query查询、flush方法、事务提交时,执行删除与修改操做更新session缓存中的内容,只有在事务提交时才同步到数据库。

3.本质

同步时间点与刷新时间点所作的工做就是将以面向对象形式对数据的操做转化为以SQL形式对数据库的操做,由于不管框架怎么封装,底层数据库所能识别与执行的命令只有SQL语句,框架提供了面向对象这种相对简单的操做数据库的方式,底层再将这种方式转化为数据库接受的方式。

刷新时间点与同步时间点就是启动转化的时机。刷新时间点将转化的SQL语句写入session缓存,同步时间点将转化的SQL语句写入数据库。

4.flush

修改缓存中已有的对象,好比修改对象属性值,删除对象,不会清空缓存,不会同步到数据库,不影响那些与缓存内容无关的操纵,好比save\get操做不受flush影响,当即执行。

5.快照约束

同步时间点与刷新时间点都受快照的约束。

七 缓存

1.Hibernate缓存结构

将从数据库中加载的对象分别保存在内存的3个区域,各区域之间相互独立,互不影响。

2.一级缓存与二级缓存内容结构

一级缓存与二级缓存中的内容都以Map集合的形式存储,key是持久化类与惟一性标识(id)的组合,value是对象的引用变量。

判断一级缓存或者二级缓存中是否存在一个对象的依据是,该对象的持久化类与惟一性标识构成的组合是否存在于Map集合中。

3.一级缓存

⑴生命周期

一级缓存是Session级的,生命周期与Session相同,随Session的建立而建立,随Session的销毁而销毁。

⑵内部共享

Session缓存中的数据在Session内部共享,Session之间不共享。

4.快照

⑴做用

快照内存中一块存储区域,提供了一种修改审查机制。

⑵基本原理

事务提交时,将缓存中准备同步到数据库中的内容与快照中的内容进行对比,不一致才同步到数据库,避免了对数据库没必要要的访问,减轻了数据库的负担。

5.二级缓存

⑴做用范围

二级缓存即SessionFactory缓存,为全部Session对象共享。

⑵配置

Hibernate自己并未实现二级缓冲,须要使用第三方插件,其中一种插件为EHCache,注册后才可以使用

  • 开启二级缓存:
  • <property name="hibernate.cache.use_second_level_cache">true</property>
  • 二级缓存对应内存一块区域,须要指明该区域的管理者,即二级缓存提供商:
  • <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  • 在src目录下加入缓冲配置文件:ehcache.xml。

实体类只有在二级缓存中设定了使用策略以后才能够缓存在二级缓存中,设定使用策略有两种方式:

  • 在映射文件中配置:
  • <class>
         <cache usage="read-only"/>
    </class>
  • 在配置文件中配置:
  • <class-cache class=""usage=""/>

6.Query缓存

⑴什么是Query缓存?

用来存储Query查询结果的内存区域,独立于一级与二级缓存,供Query查询使用。

⑵开启

Hibernate默认关闭了Query缓存,若是须要使用,须要在配置文件中开启:

<property name="cache.use_query_cache">true</property>

⑶存储结构

Query缓存不只包含查询结果,并且包含hql语句,搜索Query缓存时,首先对比hql语句,Query缓存只有对彻底相同的hql语句可见。

⑷使用

Query q=session.createQuery(hql);
List list=q.setCacheable(true).list();

八 实体关联关系映射

1.多对一单向关联

在多的一段添加外键:

<many-to-one name="属性名"class="一方全限定性类名">
     <column name="在本表中添加的外键字段">//多的一方表中外键名
</many-to-one>

2.多对一双向关联

//多方映射文件
<many-to-one name="属性名" class="一方">
      <column name="外键">
</many-to-one>

//一方映射文件
<set name="属性名" inverse="true">
      <key column="外键"/>
      <one-to-many class="多方"/>
</set>
  • inverse:指明关联关系由哪一方控制,关联关系是经过外键创建起来的,所以维护关联关系就是维护外键,默认为false,双方均可控制,有可能致使数据重复;取值true,由另外一方维护关联关系。
  • 没有维护权的一方尝试维护关联关系时,没法修改外键字段。
  • 为了防止出现双方都放弃维护权的状况,只有一方才有权利设置维护权。
  • 在MySQL中关联关系只能由父表维护,在Hibernate中子表也能够维护关联关系,底层实现也是遵循MySQL规则的。

3.一对一主键关联

一个实体与另外一个实体一一对应,能够经过主键关联实现这种关联关系,造成表与表之间的一对一主键关联。也能够在一张表中创建外键,外键字段惟一,引用另外一张表的主键,造成一对一外键关联。

一对一主键关联的核心是一张表的主键由另外一张表的主键生成,即从另外一张表的主键中取值,至关于主键又做为外键,引用一张表的主键。

映射文件的建立:

<id>
     <generator class="foreign">
         <param name="property">当前实体中另外一个实体的引用变量</param>
     </generator>
</id>
<one-to-to name="引用变量名"class="类名"constrained="true"/>

constrained:用来设定子表与父表之间是否存在外键约束,便是否把子表的主键当作外键关联父表的主键。可取值false/true,取值为false时,两张表孤立,无任何关系,取值为true时,子表的主键引用父表的主键。通常设定为为true。

4.一对一外键关联

一对一外键关联是多对一外键关联的特例,将外键字段设定为不可重复,即由多对一外键关联转化为一对一外键关联。

<many-to-one name=""class=""unique="true">
      <column name=""/>
</many-to-one>

5.多对多关联

将多对多关联分解成2个一对多关联,提供第三张表做为中间表,做为多的一方。

<set name="属性名"table="tb_mid">
      <key column="中间表中的外键"/>
      <many-to-many class=""column="关联实体在中间表中的外键"/>
</set>
  • 多对多关联的实现是在中间表中建立了一个联合主键,联合主键的每个字段都是外键,分别引用一张表的主键。
  • 在双向关联中,因为数据之间的相互引用,若是设置了级联删除,那么一旦删除一条数据,整个数据库中就可能被清空,所以在双向关联中尽可能不设置级联删除。

6.自关联

⑴什么是自关联?

实体A与实体B的属性彻底相同,其中一方包含另外一方,据此建立一个实体,包含A与B双向的关联关系。

⑵目的

由于两个实体属性全相同,存在共用一张表的可能,放在一张表中不只能够节省空间,并且能够提升查询效率。

⑶处理措施

将自关联看做多对一双向关联。

九 级联操做

1.什么是级联操做?

相关联的两方,一方为主动方,另外一方为被动方,当主动方发生变更时,被动方产生一样的变更,这种变化上的关联关系叫作级联。

2.做用

一张表中的某个字段引用另外一张表中的某个字段,为了保证源字段发生该表时,引用字段作出一样的改变,保证数据的正确性,能够设置级联操做。

3.cascade

在映射文件中级联属性用cascade表示,可取值:

  • all:所用状况下均采用级联操做。
  • none:默认状况。所用状况下均不采用级联操做。
  • save-update:只有在主动方执行save或者update操做时,被动方才发生相应的改变。
  • delete:只有在主动方执行delete操做时,被动方才发生相应的改变。

4.all-delete-orphan

删除与当前对象不存在关联的对象,只能从父表删除,由于可能子表中多个对象引用同一父表对象,若是从子表删除,那么子表中引用该父表的其余对象也可能被删除,而这是不指望的。

Child oneChild=session.get(xxxxx);//首先获取子对象
parentObj.remove(oneChild);//解除子对象与父对象之间的关系,将子对象从子表中删除

不设置成all-delete-orphan时,只解除关联关系,即子表中对应数据的外键为空。

十 检索优化

1.优化切入点

  • 加载对象的时机,经过属性lazy设定。
  • 加载对象的方式,经过属性fetch设定,取值join时采用左外链接加载,延时加载失效;取值select时,两张表分别查询。 

2.多端加载优化

对一方做为主加载方加载多方的操做进行优化。

lazy可取值:

  • true:延迟加载,只有在访问多端对象中的未知数据数据时才加载多端对象。
  • false:在加载一段对象时,当即加载多端对象。
  • extra:相比true更加延迟加载,若是当前操做可以经过其余不加载详情的方式实现,就不加载详情。

3.一端加载优化

对多方做为主加载方加载一方的操做进行优化。

lazy可取值:

  • false:不采用延时加载,当即加载。
  • proxy:是否当即加载由一方在映射文件中的设置决定。

4.默认状况

不管是一端加载,仍是多端加载,默认采用延时加载,分别查询的方式。

十一 实体继承关系映射

1.类继承关系树映射成一张表

将存在继承关系的多个实体映射成一张表,表中既有共有字段,又有特有字段,为了代表数据从属的实体,增长一个身份标识字段:

<discriminator column="字段名"type="string">//身份标识字段,紧跟主键定义
//省略父类属性映射
<subclass name=""class=""discriminator-value="身份名">
   <property name=""/>//特有字段定义
</subclass>

2.子类新增属性单独映射成一张表

父类映射成一张表,子类新增属性映射成一张表,子类主键做为外键引用父类的主键,实现表的链接:

<joined-subclass name="全限定性类名"table="子表名">
   <key column="id">//在子表中增长外键,关联本表的主键,该外键同时是子表的主键
   <property name=""/>
</joined-class>

3.子类映射成一张独立的表

将子类继承属性与新增属性映射到一张表中,该表为子类独有。

<class name="父类"abstract="true">//由于只为子类建立表,不为父类建立表,因此将父类定义为abstract
   <id name="id">
       <generator class="assigned"/>//主键由程序负责生成
   </id>
-------------------xxxxxxxxxxxxxx-----------------------
   <union-subclass name="子类"table="子表名">
       <property name="子类新增属性名"/>
   </union-subclass>
</class>
  • 由于父类多是一个抽象类,现实中不存在对应的对象,只是充当过渡环节,所以没有必要为父类建立表,因此将父表定义为abstract,表示不为该实体建立表。
  • 主键由程序负责生成。

十二 HQL

1.什么是HQL?

Hibernate Query Language,一种彻底面向对象的查询语言,查询范围是对象的集合,返回结果也是对象的集合。

2.基本语法

select obj.attr from Object obj where obj.attr xxxx group by xxxx having order by xxx;

3.基本操做

String hql="from xxxx";
Query q=session.createQuery(hql);
List list=q.list();//以List集合返回查询结果

4.参数绑定机制

Hibernate参数绑定机制提供了两种占位符,不一样的占位符有不一样的操做:

  • ?:采用索引肯定占位符,索引从0开始。为占位符赋值:
  • Query.setParameter(0,"具体指");
  • :parameterName:采用参数名肯定占位符。为占位符赋值:
  • Query.setParameter("parameterName","具体指");

5.limit

hql语句中支持limit关键字,分页查询能够经过如下方式实现:

String hql="不含hql";
Query q=session.createQuery(hql).setFirst(beginIndex).setMaxResults(length);

6.namedQuery

若是修改了hql语句,不但愿从新编译java文件,能够预先把hql语句定义在映射文件中,在java代码中经过名称调用hql语句,这种查询方式叫作命名查询。实现方式:

<class>
    xxxxxxxxxxxxxxx
</class>
<query name="namedQuery">hql</>

Query q=session.getNamedQuery("namedQuery");

十三 注解式开发

在配置文件中注册实体类 :

<mapping class="实体类全限定性类名" />

经常使用注解:

  • @Entity:注释在类名上,代表该类是一个实体类。
  • @Table:注释在类名上,指明该类映射的表名,省略时采用类名做为表名。
  • @Id:注释在标识属性上,代表该属性映射为主键。
  • @GeneratedValue(Strategy=GenarationType.xxxx):注释在@Id下面,设置主键生成策略。
  • @Basic:注释在非标识属性上,将该属性映射为非主键字段,是一个默认注释,能够取消。
  • @Column:注释在@Basic下面,设定字段名称,能够省略,省略时采用属性名做为字段名。
  • @Transient:注释在不创建映射字段的属性上,必须注释,由于系统默认将全部属性映射到表中。
相关文章
相关标签/搜索