1|0第一天:hibernate的基础入门
1|1第一章 hibernate和ORM的概念
1.1 hibernate 概述
hibernate 框架是当今主流的Java持久层框架之一,由于它具有简单易学、灵活性强、扩展性强等特点,能够大大地简化程序的代码量,提高工作效率,因此受到了广大开发人员的喜爱。
hibernate 是一个开放源代码的ORM框架,它对JDBC进行了轻量级的对象封装,使得Java开发人员可以使用面向对象的编程思想来操作数据库。
1.2 ORM 概述
Object Relation Mapping 对象关系映射。
对象-关系映射(Object Relation Mapping,简称 ORM),是随着面向对象的软件开发发展而产生的。用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的SQL语句打交道,只需要简单的操作实体对象的属性和方法。ORM技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型数据通过这个桥梁来相互转化。
简单的说就是把我们程序中的实体类和数据表建立起来对应关系。
1.2.1 为什么要学习 Hibernate
使用传统的JDBC开发应用系统时,如果是小型应用系统,并不觉得有什么麻烦,但是对于大型应用系统的开发,使用JDBC就会显得力不从心。例如对几十、几百张包含几十个字段的表进行插入操作时,编写的SQL语句不但很长,而且繁琐,容易出错;在读取数据时,需要写多条getXxx语句从结果集中取出各个字段的信息,不但枯燥重复,并且工作量非常大。为了提高数据访问层的编程效率,Gavin King 开发出了一个当今最流行的ORM框架,它就是Hibernate框架。
所谓的ORM就是利用描述对象和数据库表之间映射的元数据,自动把Java应用程序中的对象持久化到关系型数据库中的表中。通过操作Java对象,就可以完成对数据库表的操作。可以把ORM理解为关系型数据和对象的一个纽带,开发人员只需要关注纽带一端映射的对象即可。ORM原理如图所示:
与其它操作数据库的技术相比,Hibernate具有以下几点优势:
-
Hibernate对JDBC访问数据库的代码做了轻量级封装,大大简化了数据访问层繁琐的重复性代码,并且减少了内存消耗,加快了运行效率。
-
Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化了DAO(Data Access Object,数据访问对象)层编码工作。
-
Hibernate的性能非常好,映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
-
可扩展性强,由于源代码的开源以及API的开放,当本身功能不够用时,可以自行编码进行扩展。
明确:
操作实体类就相当于操作数据库表
1|2第二章 Hibernate 快速入门
2.1 需求介绍
本章节我们是实现的功能是保存一个客户到数据库的客户表中。
2.2 开发包和版本介绍
下载网址:http://sourceforge.net/projects/hibernate/files/hibernate-orm/3.6.10.Final/
页面显示如下图:
开发包目录,如下图所示:
从图可以看出,hibernate5.0.7的解压s目录中包含一系列的子目录,这些子目录分别用于存放不同功能的文件,接下来针对这些子目录进行简单介绍,具体如下:
-
documentation文件夹:存放Hibernate的相关文档,包括参考文档的API文档。
-
lib文件夹:存放Hibernate编译和运行所依赖的JAR包。其中required子目录下包含了运行Hibernate5项目必须的JAR包。
-
project文件夹:存放Hibernate各种相关的源代码。
2.3 搭建hibernate 开发环境(重点内容)
2.3.1 第一步:导入jar包
2.3.2 第二步:创建数据库和实体类
持久化类是应用程序中的业务实体类,这里的持久化是指类的对象能够被持久化保存到数据库中。Hibernate使用普通Java对象(Plain Old Java Object),即POJO的编程模式来进行持久化。POJO类中包含的是与数据库表相对应的各个属性,这些属性通过getter和setter方法来访问,对外部隐藏了内部的实现细节。下面就来编写Customer持久化类。
在项目src目录下,创建com.ordinov.domain包,并在包中创建实体类Customer(对应数据库表cst_customer),Customer类包含与cst_customer数据表字段对应的属性,以及相应的getXxx ()和setXxx ()方法。
/创建客户表/
/客户的实体类/
2.3.3 第三步:编写映射配置文件(xml)
实体类Customer目前还不具备持久化操作的能力,而Hibernate需要知道实体类Customer映射到数据库Hibernate中的哪个表,以及类中的哪个属性对应数据库表中的哪个字段,这些都需要在映射文件中配置。
在实体类Customer所在的包中,创建一个名称为Customer.hbm.xml的映射文件,在该文件中定义了实体类Customer的属性是如何映射到cst_customer表的列上的。
2.3.4 第四步:编写主配置文件(hibernate.cfg.xml)
Hibernate的映射文件反映了持久化类和数据库表的映射信息,而Hibernate的配置文件则主要用来配置数据库连接以及Hibernate运行时所需要的各个属性的值。在项目的src下创建一个名称为hibernate.cfg.xml的文件
2.4 实现保存操作
在项目中新建一个名称为com.ordinov.test的包,然后在包中建立一个名为HibernateDemo1.java的文件,该文件是用来测试的类文件。
2.5 入门案例的执行过程
首先创建Configuration类的实例,并通过它来读取并解析配置文件hibernate.cfg.xml。然后创建SessionFactory读取解析映射文件信息,并将Configuration对象中的所有配置信息拷贝到SessionFactory内存中。接下来,打开Session,让SessionFactory提供连接,并开启一个事务,之后创建对象,向对象中添加数据,通过session.save()方法完成向数据库中保存数据的操作。最后提交事务,并关闭资源。
2.6 Hibernate 的常见配置
在案例中,已经接触过Hibernate的映射文件和配置文件。接下来,将对这些文件进行详细的讲解。
2.6.1 映射文件的配置
该文件用于向Hibernate提供持久化类到关系型数据库的映射,每个映射文件的的结构基本都是相同的,其普遍的代码形式如下所示。
2.6.2 核心配置
Hibernate的配置文件,包含了连接持久层与映射文件所需的基本信息,其配置文件有两种格式,具体如下:
-
一种是properties属性文件格式的配置文件,它使用键值对的形式存放信息,默认文件名称为hibernate.properties;
-
另一种是XML格式的配置文件,XML配置文件的默认名称为hibernate.cfg.xml。
上述两种格式的配置文件是等价的,具体使用哪个可以自由选择。XML格式的配置文件更易于修改,配置能力更强,当改变底层应用配置时不需要改变和重新编译代码,只修改配置文件的相应属性即可,而properties格式的文件则不具有此优势,因此,在实际开发项目中,大多数情况会使用XML格式的配置文件。下面将对XML格式的配置文件进行详细介绍。
hibernate.cfg.xml配置文件一般在开发时会放置在src的源文件夹下,发布后,该文件会在项目的WEB-INF/classes路径下。配置文件的常用配置信息如下所示。
在上述代码中,首先进行了xml声明,然后是配置文件的dtd信息,该信息同样可以在核心包hibernate-core-3.6.10.Final.jar下的org.hibernate包中的hibernate-configuration-3.0.dtd文件中找到,读者只需要复制过来用即可,不需要刻意记忆。
Hibernate配置文件的根元素是hibernate-configuration,该元素包含子元素session-factory,在session-factory元素中又包含多个property元素,这些property元素用来对Hibernate连接数据库的一些重要信息进行配置。例如,上面的配置文件中,使用了property元素配置了数据库的方言、驱动、URL、用户名、密码等信息。最后通过mapping元素的配置,加载出映射文件的信息。
Hibernate配置文件的一些常用属性名称及用途,如表所示。
名称 | 用途 |
---|---|
hibernate.dialect | 操作数据库方言 |
hibernate.connection.driver_class | 连接数据库驱动程序 |
hibernate.connection.url | 连接数据库URL |
hibernate.connection.username | 数据库用户名 |
hibernate.connection.password | 数据库密码 |
hibernate.show_sql | 在控制台上输出SQL语句 |
hibernate.format_sql | 格式化控制台输出的SQL语句 |
hibernate.hbm2ddl.auto | 当SessionFactory创建时是否根据映射文件自动验证表结构或自动创建、自动更新数据库表结构。该参数的取值为:validate、update、create和create-drop。 |
hibernate.connection.autocommit | 事务是否自动提交 |
1|3第三章 Hibernate 中 API介绍
3.1 Configuration 对象
3.1.1 作用
在使用Hibernate时,首先要创建Configuration实例,Configuration实例主要用于启动、加载、管理hibernate的配置文件信息。在启动Hibernate的过程中,Configuration实例首先确定Hibernate配置文件的位置,然后读取相关配置,最后创建一个唯一的SessionFactory实例。Configuration对象只存在于系统的初始化阶段,它将SessionFactory创建完成后,就完成了自己的使命。
Hibernate通常使用Configuration config = new Configuration().configure();的方式创建实例,此种方式默认会去src下读取 hibernate.cfg.xml配置文件。如果不想使用默认的hibernate.cfg.xml配置文件,而是使用指定目录下(或自定义)的配置文件,则需要向configure()方法中传递一个文件路径的参数,其代码写法如下:
此种写法hibernate会去指定位置查找配置文件,例如,想要使用src下config包中的 hibernate.cfg.xml文件,只需将文件位置加入configure()中即可,其代码如下所示:
【加载映射文件】
Hibernate除了可以使用Configuration对象加载核心配置文件以外,还可以利用该对象加载映射文件。因为如何使用properties文件作为Hibernate的核心配置文件,其他的属性可以使用key=value的格式来设置,但是映射没有办法加载。这时这个对象就有了用武之地。可以在手动编写代码的时候去加载映射文件。
3.1.2 常用方法
3.2 SessionFactory
3.2.1 作用
SessionFactory接口负责Hibernate的初始化和建立Session对象。它在Hibernate中起到一个缓冲区的作用,Hibernate可以将自动生成的SQL语句、映射数据以及某些可重复利用的数据放在这个缓冲区中。同时它还保存了对数据库配置的所有映射关系,维护了当前的二级缓存。
SessionFactory实例是通过Configuration对象获取的,其获取方法如下所示:
3.2.2 常用方法
3.2.3 细节
该对象维护了很多信息系:
- 连接数据库的信息
- hibernate的基本配置
- 映射文件的位置,以及映射文件中的配置
- 一些预定义的SQL语句(这些语句都是通用的)比如:全字段保存,根据id的全字段查询,根据id的删除等等。
- hibernate的二级缓存(了解)
同时,它是一个线程安全的对象,所有由该工厂产生的Session都共享工厂中维护的数据。
3.2.4 使用原则
由于SessionFactory维护了很多信息同时又是线程安全的,一般情况下,一个项目中只需要一个SessionFactory,只有当应用中存在多个数据源时,才为每个数据源建立一个SessionFactory实例。因此,不应该反复的创建和销毁。
原则:一个应用应该只有一个SessionFactory。在应用加载时创建,应用卸载时销毁。
3.2.5 在hibernate中使用数据源(连接池)
SessionFactory内部还维护了一个连接池,如果我们需要使用第三方的连接池如C3P0,那么需要我们自己手动进行配置。
配置C3P0步骤如下:
1、导入连接池的jar包
2、在hibernate主配置文件中配置
3.3 Session
3.3.1 作用
Session 是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心,它的主要功能是为持久化对象提供创建、读取和删除的能力,所有持久化对象必须在session的管理下才可以进行持久化操作。
创建SessionFactory实例后,就可以通过它获取Session实例。获取Session实例有两种方式,一种是通过openSession()方法,另一种是通过getCurrentSession()方法。两种方法获取session的代码如下所示:
以上两种获取session实例方式的主要区别是,采用openSession方法获取Session实例时,SessionFactory直接创建一个新的Session实例,并且在使用完成后需要调用close方法进行手动关闭。而getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。
在没有配置把Session绑定当前线程之前,getCurrentSession方法无法使用,所以今天我们只使用openSession方法。配置的方式,我们明天来讲解。
3.3.2 常用方法
3.3.3 细节
- 由于SessionFactory已经维护了很多数据,所以Session就维护较少的内容。
- 它是一个轻量级对象。并且:它不是线程安全的!!!!!!!
- 它维护了hibernate的一级缓存。
- 它的反复创建销毁不会消耗太多资源。
3.3.4 使用原则
每个线程都只有一个Session对象。
3.4 Transaction
3.4.1 作用
Transaction接口主要用于管理事务,它是Hibernate的数据库事务接口,且对底层的事务接口进行了封装。Transaction接口的事务对象是通过Session对象开启的,其开启方式如下所示。
3.4.2 常用方法
Session执行完数据库操作后,要使用Transaction接口的commit()方法进行事务提交,才能真正的将数据操作同步到数据库中。发生异常时,需要使用rollback()方法进行事务回滚,以避免数据发生错误。因此,在持久化操作后,必须调用Transaction接口的commit()方法和rollback()方法。如果没有开启事务,那么每个Session的操作,都相当于一个独立的操作。
1|4第四章 抽取HibernateUtil工具类
在上一章节中介绍了SessionFactory的这些特点,一般情况下,在实际项目使用中,通常会抽取出一个HibernateUtils的工具类,用来提供Session对象。
Hibernate的工具类:
1|5第五章 使用Hibernate实现增删改查
5.1 保存操作
5.2 查询一个实体
5.3 修改操作
5.4 删除操作
5.5 实体查询的另一个方法load
5.5.1 实体查询的概念
所谓实体查询即OID查询,就是使用主键作为条件来查询一个实体。其中涉及的方法是Session对象get方法和load方法。
在本章节都是使用客户查询示例。
5.5.2 方法的说明
get方法:
get方法的代码演示:
load方法:
load方法的代码演示:
问题:既然两个方法都是根据ID查询一个实体,他们有什么区别呢?
5.5.3 get和load的区别
区别:
1、查询的时机不一样
get方法任何时候都是立即加载,即只要一调用get马上发起数据库查询
load方法默认情况下是延迟加载,即真正用到对象的非OID字段数据才发起查询
load方法可以通过配置的方式改为立即加载。
配置的方式:
2、返回的结果不一样
get方法永远返回查询的实体类对象。
load方法当是延迟加载时,返回的是实体类的代理对象。
涉及的概念:
- 立即加载:
是不管用不用马上查询。
- 延迟加载:
等到用的时候才真正发起查询。
2|0第二天:hibernate的细节说明
2|1第一章 hibernate的持久化类和对象标识符
1.1 持久化类的编写规范
1.1.1 什么是持久化类:
Hibernate是持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。那么知道了什么是持久化,什么又是持久化类呢?其实所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为是持久化类。其实你可以简单的理解为持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系。那么我们在编写持久化类的时候有哪些要求呢?接下来我们来看一下:
1.1.2 持久化类的编写规则:
我们在编写持久化类的时候需要有以下几点需要注意:
-
持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。
-
持久化类的属性需要私有,对私有的属性提供公有的get和set方法。因为在Hibernate底层会将查询到的数据进行封装。
-
持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。举个例子:
-
持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象的,在关系型数据库的表中是通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个OID相同的持久化对象的。
-
持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么就不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。
持久化类我们已经可以正常编写了,但是在持久化类中需要有一个唯一标识OID与表的主键去建立映射关系。而且主键一般我们是不会让客户手动录入的,一般我们是由程序生成主键。那么Hibernate中也提供了相应的主键生成的方式,那么我们来看下Hibernate的主键生成策略。
简单的说:
我们的实体类都需要遵从JavaBean的编写规范。
什么是JavaBean:
Bean:在软件开发领域,Bean表示可重用组件。
JavaBean就是用java语言开发的可重用组件。
JavaBean的编写规范是什么:
类都是public的
都有默认无参构造函数
成员变量都是私有的
都有公有的get/set方法
一般都实现Serializable接口。
基本类型和包装类的选择问题:
由于包装类可以有null值。所以实际开发中都是用包装类。
1.2 hibernate中对象标识符(OID)
OID全称是Object Identifier,又叫做对象标识符。
它是hibernate用于区分两个对象是否是同一个对象的标识。
我们都知道,虚拟机内存区分两个对象看的是内存的地址是否一致。数据库区分两个对象,靠的是表的主键。hibernate负责把内存中的对象持久化到数据库表中,靠的就是对象标识符来区分两个对象是否是同一个。实体类中映射主键的字段就是OID,如下图所示:
1.3 hibernate的主键生成策略
在讲解Hibernate的主键生成策略之前,先来了解两个概念,即自然主键和代理主键,具体如下:
-
自然主键:把具有业务含义的字段作为主键,称之为自然主键。例如在customer表中,如果把name字段作为主键,其前提条件必须是:每一个客户的姓名不允许为null,不允许客户重名,并且不允许修改客户姓名。尽管这也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许客户重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。
-
代理主键:把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为“ID”,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间。在上面例子中,显然更合理的方式是使用代理主键。
名称 | 描述 |
---|---|
increment | 用于 long、short、或 int 类型。由Hibernate自动以递增的方式生成唯一标识符,每次增量为1。只有当没有其他进程向同一张表中插入数据时才可以使用,不能在集群环境下使用。适用于代理主键。 |
identity | 采用底层数据库本身提供的主键生成标识符,条件是数据库支持自动增长数据类型。在DB2、MySQL、MS SQL Server、Sybase和HypersonicSQL数据库中可以使用该生成器,该生成器要求在数据库中把主键定义成为自增长型。适用于代理主键。 |
sequence | Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。适用于代理主键。 |
native | 根据底层数据库对自动生成表示符的能力来选择 identity、sequence、hilo 三种生成器中的一种,适合跨数据库平台开发。适用于代理主键。 |
uuid | Hibernate采用128位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符。其UUID被编码为一个长度为32位的十六进制字符串。这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间。适用与代理主键。 |
assigned | 由java程序负责生成标识符。如果不指定id元素的generator属性,则默认使用该主键生成策略。适用于自然主键。 |
2|2第二章 Hibernate的缓存和对象状态
Hibernate缓存包括两大类:一级缓存和二级缓存
- 什么样的数据适合被放到缓存中?
- 很少被修改的数据
- 不是很重要的数据
- 不会被并发访问的数据
- 什么样的数据不适合被放到缓存中?
- 经常被修改的数据
- 绝对不允许出现并发访问的数据
- 与其他应用共享的数据
2.1 hibernate的一级缓存
2.1.1 hibernate中的一级缓存:
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存。只要 Session 实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为是Session基本的缓存。
Hibernate的一级缓存有如下特点:
-
当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
-
当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询对应对象,并添加到一级缓存中。
-
当调用Session的close()方法时,Session缓存会被清空。
2.1.2 测试一级缓存
2.1.3 快照机制:
Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。
数据库中的数据:
程序代码:
最后id为2的这条数据地址修改成了“北京市海淀区”。
当通过id查询到客户c1时,对象信息保存到了一级缓存和快照区,
调用set方法将地址改变,一级缓存中的对象相应改变。而快照区的对象不变,
当事务提交时,会依据OID,比较一级缓存和快照,当发现不一致时,使用一级缓存修改数据库,并修改快照。
2.2 二级缓存
Hibernate二级缓存又被称为“SessionFactory的缓存”。由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。
Hibernate中没有自己去实现二级缓存,而是利用第三方的。
导入pom
配置hibernate.cfg.xml
配置ehcache.xml
开启二级缓存。我们在这里使用的xml的配置方式,所以要在Customer.hbm.xml文件加一点配置信息:
如果是使用注解的方法,在要在Customer这个类中,加入@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
这个注解。
测试代码
2.3 对象的三种状态
2.3.1 对象的状态说明:
了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态的详细介绍如下。
1、 瞬时态(transient)
瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。
2、 持久态(persistent)
持久态的对象存在持久化标识OID ,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
3、 脱管态(detached)
脱管态也称离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
2.3.2 明确:
a、是为了更好的掌握hibernate中操作的方法。
b、区分状态只有两个标识
一是否有OID
二是否和Session建立的关系
临时状态:
没有OID,和Session没有关系。
持久化状态:
有OID,和Session有关系。
脱管状态:
有OID,和Session没有关系。
2|3第三章 Hibernate的事务控制
3.0 事务的相关概念
3.0.1 事务的四个特性(ACID):
a:原子性--事务中包含的一系列操作,要完成一起完成,要么全不执行
c:一致性--事务完成时,所有的数据要保持一致的状态。
i:隔离性--一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰。
d:持久性--指的是一个事务一旦提交,它对数据库中数据的改变是永久性的,提交后的其他操作或故障不会对其有任何影响。
3.0.2 事务并发问题
- 脏读:读到了某个事务还未提交的数据。
- 不可重复读:一个事务读到了另一个事务已经提交的更改的数据
- 幻读/虚读:一个事务读到了另一个事务已经提交的新增的数据
3.0.3 事务的隔离级别
- 1读未提交:一个事务在执行过程中,可以读取其他事务未提交的数据。如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据。
- 2读已提交:一个事务在执行过程中,可以读取其他事务已经提交的数据,可以有效防止脏读。
- 4可重复读:一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但是不可以访问其他事务成功修改的事务。读取数据的事务将会禁止写事务但是允许读事务,写事务则禁止任何其他事务。此隔离级别可有效的防止不可重复读和脏读。
- 8序列化/串行化:提供严格的事务隔离,它要求事务序列化执行,不能并发执行。可以有效的防止脏读、不可重复读和幻读。
3.1 配置Session和线程绑定
在Hibernate中,可以通过代码来操作管理事务,如通过“Transaction tx = session.beginTransaction();”开启一个事务;持久化操作后,通过“tx.commit();”提交事务;如果事务出现异常,又通过“tx.rollback();”操作来撤销事务(事务回滚)。
除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别。其具体的配置方法是在hibernate.cfg.xml文件中的<session-factory>
标签元素中进行的。配置方法如下所示。
到这我们已经设置了事务的隔离级别,那么我们在真正进行事务管理的时候,需要考虑事务的应用的场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个DAO实现一个业务逻辑的操作。具体操作如下显示:
其实最主要的是如何保证在Service中开启的事务时使用的Session对象和DAO中多个操作使用的是同一个Session对象。
其实有两种办法可以实现:
-
可以在业务层获取到Session,并将Session作为参数传递给DAO。
-
可以使用ThreadLocal将业务层获取的Session绑定到当前线程中,然后在DAO中获取Session的时候,都从当前线程中获取。
其实使用第二种方式肯定是最优方案,那么具体的实现已经不用我们来完成了,Hibernate的内部已经将这个事情做完了。我们只需要完成一段配置即可。
Hibernate中自身提供了三种管理 Session 对象的方法
Session 对象的生命周期与本地线程绑定
Session 对象的生命周期与 JTA 事务绑定
Hibernate 委托程序管理 Session 对象的生命周期
在 Hibernate 的配置文件中, hibernate.current_session_context_class 属性用于指定 Session 管理方式, 可选值包括
- thread:Session 对象的生命周期与本地线程绑定
-
jta:Session 对象的生命周期与 JTA 事务绑定
-
managed:Hibernate 委托程序来管理 Session 对象的生命周期
配置步骤:
- 在hibernate.cfg.xml文件中配置
- 获取Session时使用的方法:
细节:
当我们把Session绑定到当前线程之后,关闭session就是hibernate来做的,我们就不用关了。
到这里我们已经对Hibernate的事务管理有了基本的了解,但是之前我们所做的CRUD的操作其实还没有查询多条记录。那如果我们需要查询多条记录要如何完成呢,我们接下来去学习一下Hibernate的其他的相关的API。
2|4第四章 Hibernate查询对象的API
4.1 Query:(掌握)
4.1.1 概述
Query代表面向对象的一个Hibernate查询操作。在Hibernate中,通常使用session.createQuery()方法接受一个HQL语句,然后调用Query的list()或uniqueResult()方法执行查询。所谓的HQL是Hibernate Query Language缩写,其语法很像SQL语法,但它是完全面向对象的。
在Hibernate中使用Query对象的步骤,具体所示:
(1)获得Hibernate的Session对象。
(2)编写HQL语句。
(3)调用session.createQuery 创建查询对象。
(4)如果HQL语句包含参数,则调用Query的setXxx设置参数。
(5)调用Query对象的方法执行查询。
HQL的说明:
把表的名称换成实体类名称。把表字段名称换成实体类属性名称。
例如:
SQL:select * from cst_customer where cust_name like ?
HQL:select * from Customer where custName = ?
其中select * 可以省略,写为:from Customer where custName = ?
了解了使用Query对象的步骤后,接下来,通过具体示例来演示Query对象的查询操作。
4.1.2 常用查询:
4.1.2.1 基本查询
4.1.2.2 条件查询
4.1.2.3 分页查询
4.1.2.4 排序查询
4.1.2.5 统计查询
4.1.2.6 投影查询
4.1.3 Query中的方法说明:
-
list方法:该方法用于查询语句,返回的结果是一个list集合。
-
uniqueResult方法:该方法用于查询,返回的结果是一个Object对象。
-
setter方法:Query接口中提供了一系列的setter方法用于设置查询语句中的参数,针对不同的数据类型,需要用到不同的setter方法。
-
uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。
-
setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。
-
setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult()方法结合使用,用于限制结果集的范围,以实现分页功能。
4.2 Criteria:
4.2.1 概述
Criteria是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层如何实现,以及SQL语句如何编写,它是Hibernate框架的核心查询对象。Criteria 查询,又称为QBC查询(Query By Criteria),它是Hibernate的另一种对象检索方式。
org.hibernate.criterion.Criterion是Hibernate提供的一个面向对象查询条件接口,一个单独的查询就是Criterion接口的一个实例,用于限制Criteria对象的查询,在Hibernate中Criterion对象的创建通常是通过Restrictions 工厂类完成的,它提供了条件查询方法。
通常,使用Criteria对象查询数据的主要步骤,具体如下:
(1)获得Hibernate的Session对象。
(2)通过Session获得Criteria对象。
(3)使用Restrictions的静态方法创建Criterion条件对象。Restrictions类中提供了一系列用于设定查询条件的静态方法,这些静态方法都返回Criterion实例,每个Criterion实例代表一个查询条件。
(4)向Criteria对象中添加Criterion 查询条件。Criteria的add()方法用于加入查询条件。
(5)执行Criterita的 list() 或uniqueResult() 获得结果。
细节:
HQL能查的,QBC都能查,反之亦然。
了解了Criteria对象的使用步骤后,接下来,通过具体示例来演示Criteria对象的查询操作。
4.2.2 常用查询:
4.2.2.1 基本查询
4.2.2.2 条件查询
4.2.2.3 分页查询
4.2.2.4 排序查询
4.2.2.5 统计查询
4.2.2.6 离线查询
2|5第五章 QBC常用查询条件说明
短语 | 含义 |
---|---|
Restrictions.eq | 等于= |
Restrictions.allEq | 使用Map,使用key/value进行多个等于的判断 |
Restrictions.gt | 大于> |
Restrictions.ge | 大于等于>= |
Restrictions.lt | 小于< |
Restrictions.le | 小于等于<= |
Restrictions.between | 对应sql的between子句 |
Restrictions.like | 对应sql的like子句 |
Restrictions.in | 对应sql的in子句 |
Restrictions.and | and 关系 |
Restrictions.or | or关系 |
Restrictions.sqlRestriction | Sql限定查询 |
Restrictions.asc() | 根据传入的字段进行升序排序 |
Restrictions.desc() | 根据传入的字段进行降序排序 |
运算类型 | HQL运算符 | QBC运算方法 |
---|---|---|
比较运算 | = | Restrictions.eq() |
<> | Restrictions.not(Restrictions.eq()) | |
>= | Restrictions.ge() | |
< | Restrictions.lt() | |
<= | Restrictions.le() | |
is null | Restrictions.isNull() | |
is not null | Restrictions.isNotNull() | |
范围运算符 | in | Restrictions.in() |
not in | Restrictions.not(Restrictions.in()) | |
between | Restrictions.between() | |
not between | Restrictions.not(Restrictions.between()) |
运算类型 | HQL运算符 | QBC运算方法 |
---|---|---|
字符串模式匹配 | like | Restrictions.like() |
逻辑 | and | Restrictions.and()| Restrictions.conjunction() |
or | Restrictions.or() Restrictions.disjunction() | |
not | Restrictions.not() |
3|0第三天:hibernate的多表映射和操作
3|1第一章 多表映射
1.1 一对多XML关系映射
1.1.1 客户配置文件:
1.1.2 联系人配置文件:
关于单向与双向的问题
在上面的配置中,客户与联系人配置了双向的一对多的关系,即一的这一端配置了<one-to-many>
,在多的这一端配置了<many-to-one>
,这样的配置,在查询时,只需要查询客户,就可以通过客户中的get方法获取到联系人,也可以只查询联系人就可以获取到客户。那么客户与联系人就构成了一个你中有我,我中有你的双向的关系。
并不是所有的一对多都需要这样配置,比如,商品和订单的关系,查询商品并不关注商品有哪些订单,而查询订单时需要知道是哪个商品的订单。这种情况就可以建立单向多对一的关系
以客户和联系人为例需要这么配置
在客户的映射文件中去除
并且在实体类中去掉联系人集合属性。而联系人的映射配置不需要改变。
这样当查询客户时,只能获取客户,而查询联系人时,还可以根据联系人的get方法,获取到客户。
1.2 多对多关系映射
1.2.1 用户配置文件:
1.2.2 角色配置文件:
3|2第二章 多表增删改操作
2.1 一对多关系的操作
2.1.1 保存操作
保存原则:先保存主表,再保存从表。
2.1.1.1 保存时遇到的问题
我们已经分析过了,因为双向维护了关系,而且持久态对象可以自动更新数据库,更新客户的时候会修改一次外键,更新联系人的时候同样也会修改一次外键。这样就会产生了多余的SQL,那么问题产生了,我们又该如何解决呢?
其实解决的办法很简单,只需要将一方放弃外键维护权即可。也就是说关系不是双方维护的,只需要交给某一方去维护就可以了。通常我们都是交给多的一方去维护的。为什么呢?因为多的一方才是维护关系的最好的地方,举个例子,一个老师对应多个学生,一个学生对应一个老师,这是典型的一对多。那么一个老师如果要记住所有学生的名字很难的,但如果让每个学生记住老师的名字应该不难。其实就是这个道理。
所以在一对多中,一的一方都会放弃外键的维护权(关系的维护)。这个时候如果想让一的一方放弃外键的维护权,只需要进行如下的配置即可。
inverse的默认值是false,代表不放弃外键维护权,配置值为true,代表放弃了外键的维护权。代码如下:
2.1.2 修改操作
2.1.2.1 修改中遇到的问题
代码如下:
2.1.3 删除操作
2.1.3.1 删除中遇到的问题
我们之前学习过级联保存或更新,那么再来看级联删除也就不难理解了,级联删除也是有方向性的,删除客户同时级联删除联系人,也可以删除联系人同时级联删除客户(这种需求很少)。
原来JDBC中删除客户和联系人的时候,如果有外键的关系是不可以删除的,但是现在我们使用了Hibernate,其实Hibernate可以实现这样的功能,但是不会删除客户同时删除联系人,默认的情况下如果客户下面还有联系人,Hibernate会将联系人的外键置为null,然后去删除客户。那么其实有的时候我们需要删除客户的时候,同时将客户关联的联系人一并删除。这个时候我们就需要使用Hibernate的级联删除操作了。
【删除客户的时候同时删除客户的联系人】
确定删除的主控方式客户,所以需要在客户端配置:
如果还想有之前的级联保存或更新,同时还想有级联删除,那么我们可以进行如下的配置:
代码如下:
【删除联系人的时候同时删除客户】
同样我们删除的是联系人,那么联系人是主控方,需要在联系人端配置:
如果需要既做保存或更新有有级联删除的功能,也可以如下配置:
2.2 多对多关系的操作
2.2.1 保存操作
解决保存失败的问题:
2.2.2 删除操作
3|3第三章 hibernate中的多表查询
3.1 对象导航查询
3.1.1 概述
对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。
例如:我们通过OID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。
对象导航查询的使用要求是:两个对象之间必须存在关联关系。
3.1.2 对象导航检索示例
3.1.2.1 查询一个客户,获取该客户下的所有联系人
3.1.2.2 查询一个联系人,获取该联系人的所有客户
3.2 对象导航查询的问题分析
问题1:我们查询客户时,要不要把联系人查询出来?
分析:
如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。
如果我们查出来的,不使用时又会白白的浪费了服务器内存。
解决:
采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。
配置的方式:
问题2:我们查询联系人时,要不要把客户查询出来?
分析:
如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。
如果我们查出来的话,一个对象不会消耗太多的内存。而且多数情况下我们都是要使用的。
例如:查询联系人详情时,肯定会看看该联系人的所属客户。
解决:
采用立即加载的思想。通过配置的方式来设定,只要查询从表实体,就把主表实体对象同时查出来。
配置的方式:
4|0第四天:JPA概述和使用hibernate中JPA的实现
4|1第一章 JPA相关概念
1.1 JPA概述
全称是:Java Persistence API。是SUN公司推出的一套基于ORM的规范。hibernate框架中提供了JPA的实现。
JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
1.2 JPA的优势
标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
简单方便
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
查询能力
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
高级特性
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
1.3 明确
a、JPA是一套ORM规范,hibernate实现了JPA规范
b、hibernate中有自己的独立ORM操作数据库方式,也有JPA规范实现的操作数据库方式。
c、在数据库增删改查操作中,我们hibernate和JPA的操作都要会。
4|2第二章 JPA入门
2.1 介绍
本章节我们实现基于JPA注解的对象关系映射,配置实体类和数据库表的对应关系。
并且使用JPA规范中的方法实现CRUD操作。
2.2 JPA环境搭建
2.2.1 第一步:导如相关Jar包
2.2.2 第二步:创建配置文件
要求:在src下面的META-INF文件夹下面创建一个名称为persistence.xml的文件。
配置文件的内容:
2.2.3 第三步:编写工具类,用于获取JPA的操作数据库对象
2.2.4 第四步:编写实体类并使用注解配置
2.3 常用注解说明
2.4 主键生成策略
通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法。
JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO。具体说明如下:
2.4.1 IDENTITY
主键由数据库自动生成(主要是自动增长型)
用法:
2.4.2 SEQUENCE
根据底层数据库的序列来生成主键,条件是数据库支持序列。
2.4.3 AUTO
主键由程序控制。
用法:
2.4.4 TABLE
使用一个特定的数据库表格来保存主键(了解)
用法:
这里应用表tb_generator,定义为 :
插入纪录,供生成主键使用:
INSERT INTO tb_generator(id, gen_name, gen_value)VALUES (1,PAYABLEMOENY_PK', 1);
在主键生成后,这条纪录的value值,按allocationSize递增。
@TableGenerator的定义:
其中属性说明:
2.5 JPA的CRUD操作
2.5.1 保存
2.5.2 修改
merge方法实现修改
2.5.3 删除
2.5.4 查询一个
2.5.5 查询所有
4|3第三章 JPA的多表操作
3.1 一对多关系映射
3.1.1 常用注解
3.1.1.1 @OneToMany
3.1.1.2 @ManyToOne
3.1.1.3 @JoinColumn
3.1.2 配置代码
3.1.2.1 客户配置
3.1.2.2 联系人配置
3.2 多对多关系映射
3.2.1 常用注解
3.2.1.1 @ManyToMany
3.2.1.2 @JoinTable
3.2.1.3 @JoinColumn
3.2.2 配置代码
3.2.2.1 用户配置
3.2.2.2 角色配置
4|4第四章 JPA的多表操作
4.1 一对多关系的增删改操作
4.1.1 保存操作
保存原则:先保存主表,再保存从表。
JPA注解的配置方式:不涉及多一条update语句的问题
4.1.2 修改操作
解决没有保存联系人的问题,配置级联保存更新:
注解的配置方式:
4.1.3 删除操作
级联删除的配置:
4.2 多对多关系的增删改操作
4.2.1 保存操作
JPA注解的配置方式:不涉及保存失败的问题
4.2.2 删除操作
在多对多映射配置中不能出现双向级联删除的配置,无论注解还是XML配置
4|5第五章 JPA的其他说明
5.1 在JPA中使用C3P0连接池
5.1.1 第一步:导入jar包
5.1.2 第二步:在persistence.xml中配置
5.1.3 验证
5.2 JPA和Hibernate中操作数据的方法对照
操作 | Hibernate中的方法 | JPA中的方法 | 说明 |
---|---|---|---|
保存操作 | save(Object entity) | persist(Object entity) | 共同点:都是把临时态对象转成了持久态。 区别:提供者不一样: save方法是hibernate提供的。 persist方法是JPA规范提供的。 在没有事务的情况下: save会去数据库中保存,hibernate提供了一个内置的事务来执行。 persist什么都不会做。 |
更新操作 | update (Object entity) | merge (Object entity) | Hibernate和jpa都可以利用快照机制,不调用任何方法去更新。Update方法在更新时,如果遇到一级缓存已经包含了一个相同OID的对象会报错。merge则可以执行成功。 |
删除操作 | delete (Object entity) | remove (Object entity) | 都是删除一个实体 |
查询一个操作 | get (Class clazz,Serializable id); load(Class clazz,Serializable id) |
find(Class clazz,Object id) getReerence(Class clazz,Object id) |
get和find都是立即加载。load和getReference一样都是延迟加载。 |
查询所有操作 | Query:使用HQL语句查询 | Query:使用JPQL查询 | 查询语句的形式不一样。 |
查询返回唯一结果操作 | uniqueResult() | getSingleResult() | 查询都是返回一个唯一的结果。 |
5.3 关于 JTA 和 RESOURCE LOCAL 的区别
5.3.1 JTA
JTA事务(Java Transaction API)是J2EE规范中有关事务的标准。它是容器级别的事务,只能运行在J2EE服务器中。它的最大优势是可以支持分布式的事务,如果系统采用的是分布式的数据库,那么只能选择JTA管理EntityManager事务。
使用JTA管理EntityManager事务时,需要注意以下几个问题。
-
JTA事务只能运行在J2EE的环境中,即EJB容器中和Web容器中;而在J2SE环境中只能使用RESOURCE_LOCAL管理事务。
-
容器托管的EntityManager对象只能采用JTA的事务,而不能采用RESOURCE_LOCAL事务。
5.3.2 RESOURCE LOCAL
RESOURCE_LOCAL事务数据库本地的事务。它是数据库级别的事务,只能针对一种数据库,不支持分布式的事务。对于中小型的应用,可以采用RESOURCE_LOCAL管理EntityManager事务。
使用RESOURCE_LOCAL管理EntityManager事务时需要注意以下几个问题。
-
在J2SE环境中,只能使用RESOURCE_LOCAL管理EntityManager事务,并且EntityManager对象是以应用托管方式获得的。
-
代码中使用RESOURCE_LOCAL管理事务时,要通过调用EntityManager的getTransaction()方法获得本地事务。
5.4 常用注解
JPA | 含义和作用 |
---|---|
@Entity | 将 一个类声明为一个持久化类 |
@Id | 声明了持久化类的标识属性(相当于数据表的主键) |
@GeneratedValue | 定义主键生成策略 |
@Table | 为持久化类映射指定表(table)、目录(catalog)和schema的名称。默认值,持久化类名,不带包名 |
@UniqueConstraint | 定义表的唯一约束 |
@Column | 将属性映射到列 |
@Transient | 忽略这些字段和属性,不用持久化到数据库 |
@OneToOne | 建立持久化类之间一对一关联关系 |
@OneToMany | 建立关联一对多 |
@ManyToOne | 建立关联多对一 |
@ManyToMany | 建立关联多对多 |
@JoinColumn | 用于描述一个关联的字段,如外键字段名 |
@GenericGenerator | 策略生成器 |
@GeneratedValue
@Column注解的属性
@OneToOne、@OneToMany、@ManyToOne、ManyToMany的共有属性:
JDBC驱动程序类型
JDBC简单示例代码
JDBC SQL语法
JDBC是什么?
JDBC快速入门教程
JUnit4参数化测试实例
JUnit套件测试实例
JUnit4时间(超时)测试实例
JUnit4忽略(Ignore)测试实例