hibernate课程 - 图文

更新时间:2023-10-02 15:27:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

Hibernate课程

1. JDBC的优点和缺点

JDBC的优点

直接底层操作,提供了很简单、便捷的访问数据库的方法,跨平台性比较强。灵活性比较强,可以写很复杂的SQL语句。

JDBC的缺点

因为JAVA是面向对象的,JDBC没有做到使数据能够面向对象的编程,使程序员的思考仍停留在SQL语句上。

操作比较繁琐,很多代码需要重复写很多次。

如果遇到批量操作,频繁与数据库进行交互,容易造成效率的下降。 JDBC的程序操作可以封装一些什么内容?又不可以封装哪些内容?

2. Hibernate

为什么要用Hibernate

Hibernate实现了面向对象的数据库编程

Hibernate比起JDBC来,在代码的书写上比较简单化了。

Hibernate提出了缓存机制,这样可以使访问数据的效率提高很大。

Hibernate的缺点:

SQL语句是由hibernate生成的,程序员不能干涉生成sql语句,这就造成了优

化比较困难。

在大量数据的条件下,hibernate的执行效率是比较低的。

第一个Hibernate程序

例子包括的内容: 项目所需要的LIB包

配置文件hibernate.cfg.xml 持久化类 Customer.java 映射文件 Customer.hbm.xml

结论:通过这个例子可以看到利用面向对象的技术也可以操作数据库。Hibernate就是一个面向对象操作数据库的框架,是基于JDBC开发的。 例子解析

配置文件:

Hibernate.connection.url 表示要链接的数据库地址

Hibernate.connection.driver_class 表示要链接的数据库的驱动类 Hibernate.connection.username 要连接的数据库的用户名 Hibernate.connection.password 要连接的数据库的密码 Hibernate.dialect 表示要使用的数据库的类型

org.hibernate.dialect.MySQL5Dialect mysql数据库 org.hibernate.dialect.Oracle9Dialect oracle数据库 org.hibernate.dialect.SQLServerDialect SQLServer数据库 hibernate.hbm2ddl.auto

validate:加载hibernate时验证创建表结构

update:加载hibernate时自动更新数据库结构,如果表存在不用创建,如果不存在就创建。

create:每一次加载hibernate时都创建表结构 create-drop:加载hibernate时创建,退出时删除

持久化类:

映射文件:

客户端:

查询例子:

删除例子:

更新例子:

保存例子:

? Cnfiguration 类负责管理 Hibernate 的配置信息。包括如下内容:

? Hibernate运行的底层信息:数据库的URL、用户名、密码、JDBC驱动

类,数据库Dialect,数据库连接池等(对应 hibernate.cfg.xml 文件)。

? 持久化类与数据表的映射关系(*.hbm.xml 文件)

? 创建 Configuration 的两种方式

? 属性文件(hibernate.properties):

Configuration cfg = new Configuration();

? Xml文件(hibernate.cfg.xml)

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

第二种创建方式较为常见。Hibernate.cfg.xml文件默认的目录为系统的根目录。 (展示源代码以得到证实)

也可以利用config.config方法来指定配置文件所在的目录。

? Configuration对象根据当前的配置信息生成 SessionFactory 对象。

SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息(SessionFactory 对象中保存了当前的数据库配置信息和所有映射关系以及预定义的SQL语句。同时,SessionFactory还负责维护Hibernate的二级缓存)。

Configuration cfg = new Configuration().configure(); SessionFactory sf = cfg.buildSessionFactory();

? 是线程安全的。

? SessionFactory是生成Session的工厂: Session session = sf.openSession();

? 构造 SessionFactory 很消耗资源,一般情况下一个应用中只初始化一个

SessionFactory 对象。

? 在Hibernate中,Transaction tx = session.beginTransaction()相当于给数据库

操作起事务。Session.commit()则为提交事务。 ? Hibernate的整个运行过程如下:

1、应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息,

2、并用这些信息生成一个SessionFactory对象, 3、然后从SessionFactory对象生成一个Session对象, 4、并用Session对象生成Transaction对象;

A、可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、等操作;

B、在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;如果没有异常,Transaction对象将提交这些操作到数据库中。

ORM框架 ------hibernate

1.开源的持久层框架.

2.ORM(Object/Relational Mapping)映射工具,建立面向对象的域模型和关系数据模型之间的映射.

3.连接java应用和数据库的中间件.

4.对JDBC进行封装,负责java对象的持久化.

5.在分层结构中处于持久化层,封装对数据库的访问细节,

使业务逻辑层更专注于实现业务逻辑

Hibernate的优点:

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

2、Hibernate是一个基于jdbc的主流持久化框架,是一个优秀 的orm实现,它很大程度的简化了dao层编码工作。 3、Hibernate使用java的反射机制,而不是字节码增强程序类实现 透明性

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

5、Hibernate使数据库的操作完全面向对象。而不是从前的面向关系进行操作。

建立ORM思想

应用Hibernate意味着程序员将不直接操作SQL语句,也就是说以前对SQL的直接操作现在要用面向对象实现。实际上ORM到JDBC是紧密相连的。于此带来了一些问题:

主键是怎么样产生的

一对多、多对一的关系在面向对象中是怎么样去实现的

面向对象是怎么样由面向对象的操作映射到对SQL的操作

3. 主键生成机制

表示符生成器 Increment Identity 描述 由hibernate自动以递增的方式生成表识符,每次增量为1 由底层数据库生成表识符。条件是数据库支持自动增长数据类型。 Sequence Native Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。 根据底层数据库对自动生成表示符的能力来选择identity、sequence、hilo Hibernate采用128位的UUID算法来生成标识符。该算法 能够在网络环境中生成唯一的字符串标识符,这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间。 Uuid.hex assigned 适用于自然主键。由java程序负责生成标识符。不能把setID()方法声明为 Private的。尽量避免使用自然主键。 increment 标识符生成器

? increment 标识符生成器由 Hibernate 以递增的方式为代理主键赋值 ? Hibernate 会先读取 NEWS 表中的主键的最大值, 而接下来向 NEWS 表中插入

记录时, 就在 max(id) 的基础上递增, 增量为 1.(带走+1) ? 适用范围:

? 由于 increment 生存标识符机制不依赖于底层数据库系统, 因此它适合

所有的数据库系统

? 适用于只有单个 Hibernate 应用进程访问同一个数据库的场合 ? OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在

运行时会抛出异常

identity 标识符生成器

? identity 标识符生成器由底层数据库来负责生成标识符, 它要求底层数据库把主

键定义为自动增长字段类型(加1带走) ? 适用范围:

? 由于 identity 生成标识符的机制依赖于底层数据库系统, 因此, 要求底层

数据库系统必须支持自动增长字段类型. 支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase 等

? OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在

运行时会抛出异常

sequence 标识符生成器

? sequence 标识符生成器利用底层数据库提供的序列来生成标识符.

? Hibernate 在持久化一个 News 对象时, 先从底层数据库的 news_seq 序列中获

得一个唯一的标识号, 再把它作为主键值 ? 适用范围:

? 由于 sequence 生成标识符的机制依赖于底层数据库系统的序列, 因此,

要求底层数据库系统必须支持序列. 支持序列的数据库包括: DB2 Oracle 等

? OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在

运行时会抛出异常

native 标识符生成器

? native 标识符生成器依据底层数据库对自动生成标识符的支持能力, 来选择使

用 identity, sequence 或 hilo 标识符生成器.

? 适用范围:

? 由于 native 能根据底层数据库系统的类型, 自动选择合适的标识符生成

器, 因此很适合于跨数据库平台开发

? OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在

运行时会抛出异常

assigned 标识符生成器

? hibernate和底层数据库都不帮助你生成主键,也就是说得自己在程序中手动的

设置主键的值。 ? 适用范围:

主键有一定的含义,需要根据业务产生的情况。

Uuid标识符生成器

? Hibernate采用128位的UUID算法来生成标识符。该算法能够在网络环境中生

成唯一的字符串标识符,这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间 ? 使用范围:

主键是字符串,而且必须是唯一

4. Hibernate类型与java类型的映射关系

Hibernate映射类型 integer long short float double

big_decimal character string byte boolean yes_no true_false date java类型

java.lang.Integer java.lang.Long java.lang.Short java.lang.Float java.lang.Float

java.math.BigDecimal java.lang.String java.lang.String

byte或java.lang.Byte

boolean或java.lang.Boolean boolean或java.lang.Boolean boolean或java.lang.Boolean java.util.Date或java.sql.Date 标准SQL类型 integer bigint smallint float double numeric char(1) varchar tinyint bit

char(1)('Y'/'N') char(1)('Y'/'N') date

time

java.util.Date或java.sql.Time

timestamp java.util.Date或java.sql.timestamp calendar

java.util.Calendar calendar_date java.util.Calendar binary byte[]

text

java.lang.String

serializable java.io.Serializable实例 clob java.sql.Clob blob java.sql.Blob class java.lang.Class locale java.util.Locale timezone java.util.TimeZone currency

java.util.Currency

5. Myeclipse开发hibernate

开发步骤

1、

新建一个JavaProject工程 myeclipsehibernate

2、 添加Hibernate到myeclipsehibernate. 3、

生成以后的结构如图所示:

4、

利用hibernate自带的工具自动生成持久化类和映射文件。

具体方法在课堂上进行演示。

5、

建立客户端进行编辑

time

timestamp timestamp date

varbinary或blob clob

varbinary或blob clob

varbinary或blob varchar varchar varchar varchar

HibernateSessionFactory类

利用ThreadLocal类保证了Session的线程安全。具体内容看代码是怎么样形成的。

6. 映射一对多关联关系

单向关联

仅仅建立从Order到Customer的多对一关联,即仅仅在Order类中定义customer属性。或者仅仅建立从Customer到Order的一对多关联,即仅仅Customer类中定义orders集合。

单向 n-1 关联只需从 n 的一端可以访问 1 的一端 域模型: 从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中无需定义存放 Order 对象的集合属性

关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键

Hibernate 使用 元素来映射多对一关联关系

先保存订单,再保存客户

从这里可以看出执行了两条insert语句,一条update语句

先保存客户,再保存订单

可以看出这种情况程序是执行了两条insert语句,而没有执行update语句。

查询订单

先保存客户,再保存订单,在下面的代码中注释掉session.save(c),会有什么后果

级联保存和更新

当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,会抛出TransientObjectException.如果设定many-to-one元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。

双向关联

双向 1-n 与 双向 n-1 是完全相同的两种情形

双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然. 域模型:

从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中需定义存放 Order 对象的集合属性

关系数据模型:

ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键

建立一对多的双向关联关系

Hibernate使用set元素来映射一对多关联关系

保存客户和订单(客户和订单建立双向关联)

保存客户和不保存订单

在下面的代码中注释掉session.save(order1),会有什么后果

Expression.like Expression.in Expression.and Expression.or 对应sql的like子句 对应sql的in子句 and 关系 or关系 Expression.sqlRestriction Sql限定查询 Expression.asc() Expression.desc() 根据传入的字段进行升序排序 根据传入的字段进行降序排序 HQL和QBC支持的各种运算

运算类型 HQL运算符 = <> >= 比较运算 < <= is null is not null 范围运算符 In not in Between QBC运算方法 Express.eq() Express.not(Express.eq()) Express.ge() Express.lt() Express.le() Express.isNull() Express.isNotNull() Express.in() Express.not(Express.in()) Express.between() 含义 not between HQL和QBC支持的各种运算 运算类型 Express.not(Express.between()) HQL运算符 QBC运算方法 Expression.like() Expression.and()| Expression.conjunction() Or Expression.or()| Expression.disjunction() Not Expression.not() 含义 字符串模式匹配 Like 逻辑 And 13. 二级缓存

缓存的概念

计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存

Hibernate中提供了两个级别的缓存 Session 级别的缓存

它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,一般情况下无需进行干预

SessionFactory 级别的缓存

它是属于进程范围的缓存

启用二级缓存的条件 很少被修改

很多系统模块都要用到 不是私有的数据,是共享的 二级缓存的供应商

配置二级缓存

例子

从二级缓存中取数据

级联删除

cascade属性

在数据库中对集合进行排序

元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序

unsaved-value 应用环境

在非显示数据保存时,Hibernate将根据这个值来判断对象是否需要保存。所

谓显示保存就是指在代码中明确调用session的save、update等方法进行对象的持久化。而在某些情况下。如映射关系中,hibernate会根据级联关系对级联类进行保存,

此时,代码中没有针对级联类的显示保存语句,需要hibernate根据对象当前状态判断是否需要保存到数据库。此时,hibernate将根据unsaved-value进行判定。

对级联对象保存的判定

1、 2、 3、 4、

hibernate会取出目标对象的ID

将ID的值与unsaved-value的值进行对比

如果两个的值相等,则认为目标对象尚未保存。否则,则认为对象已经保存,无需再进行保存的操作。

一般情况下unsaved-value有none,any两个值。

7. 多对多的关联

两个一对多的关系 直接多对多的关系 8. 深入Session

session概述

Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载Java 对象的方法.

理解session的缓存

? 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了

Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期

? 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使

程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试

图load()对象时,会判断缓存中是否存在该对象,有则返回。没有在查询数据库

清理session的缓存

? Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相

关记录对应. Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为清理缓存(flush) ? 默认情况下 Session 在以下时间点清理缓存:

? 当应用程序调用 Transaction 的 commit()方法的时, 该方法先清理

缓存(session.flush()),然后在向数据库提交事务(tx.commit()) ? 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发

生了变化,会先清理缓存,以保证查询结果能够反映持久化对象的最新状态

? 显式调用 Session 的 flush() 方法.

? 区别:

flush: 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一些列sql语句,但不提交事务,;

commit:先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。

reresh:刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.

clear:清空缓存,等价于list.removeAll();

利用Session缓存读取持久化对象的数据

1 Customer c = new Customer(“TOM”,new HashSet());

2 session.save(c); //customer对象被持久化,并且加入到session的缓存中 3 Long id = c.getId();

4 c = null; //c变量不再引用customer对象

5 //从session缓存中读取customer对象,使c2变量引用customer对象 6 Customer c2 = (Customer)session.load(Customer.class,id); 7 tx.commit(); //缓存中的对象和数据库同步 8 session.close(); //关闭session 清空缓存

9 System.out.println(c2.getName()); //访问customer对象

10 C2 = null; //C2对象不再引用customer对象,customer对象结束生命周期 --------------------------------------------------------------------------------------------- 缓存的作用:

1。减少访问数据库的频率。

2。保证缓存中的对象与数据库中的相关记录保持同步。

Session执行批量操作

可以写一个for循环,Session可以批量插入上万条数据。如下面的代码: For(int i=0;i<10000;i++){

}

这样写的代码会产生一个什么问题?会使一万个对象的缓存全部存在于内存中,这样做加大了内存的压力。所以应该定期清理session的缓存,也就是flush一下,这样内存才能保证足够的空间。

Session.save(object);

openSession与currentSession

9. 持久化对象的状态

持久化对象有3种状态:

持久化状态 临时状态 游离状态

Session 的特定方法能使对象从一个状态转换到另一个状态

临时对象(transient)

? 在使用代理主键的情况下, OID 通常为 null ? 不处于 Session 的缓存中 ? 在数据库中没有对应的记录

持久化对象(也叫”托管”)(Persist)

? OID 不为 null ? 位于 Session 缓存中

? 持久化对象和数据库中的相关记录对应

? Session 在清理缓存时, 会根据持久化对象的属性变化, 来同步更新数据

? 在同一个 Session 实例的缓存中, 数据库表中的每条记录只对应唯一的

持久化对象

游离对象(也叫”脱管”)(Detached)

? OID 不为 null

? 不再处于 Session 的缓存中

? 一般情况需下, 游离对象是由持久化对象转变过来的, 因此在数据库中

可能还存在与它对应的记录

Session使用以下方法可以使持久化对象转变成游离对象:

测试hibernate中对象变化的状态:

程序代码 tx = session.beginTransaction(); Customer c = new Customer); Session.save(c) 生命周期 开始生命周期 处于生命周期中 Long id=c.getId(); c = null; Customer c2 = (Customer)session.load(Customer.class,id); tx.commit(); session.close(); 处于生命周期中 转变为持久化状态 处于持久化状态 状态 临时状态 处于生命周期中 转变为游离态 c2.getName(); 处于生命周期中 处于游离态 c2 = null; 结束生命周期 结束生命周期 对象状态转化图

对象状态的总结

操纵持久化对象-save()

? Session 的 save() 方法使一个临时对象转变为持久化对象 ? Session 的 save() 方法完成以下操作:

? 把 News 对象加入到 Session 缓存中, 使它进入持久化状态

? 选用映射文件指定的标识符生成器, 为持久化对象分配唯一的 OID. 在使用代理

主键的情况下, setId() 方法为 News 对象设置 OID 使无效的.

? 计划执行一条 insert 语句,把Customer对象当前的属性值组装到insert语句中 ? Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系. 当

News 对象处于持久化状态时, 不允许程序随意修改它的 ID

操纵持久化对象-update()

? Session 的 update() 方法使一个游离对象转变为持久化对象, 并且计划执行一

条 update 语句.

操纵持久化对象-saveOrupdate()

saveOrUpdate:

该方法同时包含save和update方法,如果参数是临时对象就用save方 法,如果是游离对象就用update方法,如果是持久化对象就直接返回。 如果参数是临时对象就用save方法

如果是游离对象就用update方法

如果是持久化对象就直接返回,不执行操作

10. 延迟加载

类的延迟加载

lazy 为true或者为false 利用session.load方法完成

集合的延迟加载

True false extra

extra为更进一步的延迟加载策略。

当调用getStudents()时不会加载hql语句,当加载student的属性的时候才会发出SQL语句

调用getStudents().size()方法的时候,会触发类似于:Hibernate: select count(id) from T_Student where classid =? 这样的SQL查询语句(这是一种很聪明的做法,如果lazy=”true”,getStudents().size()将会使得hibernate加载所有集合的数据到内存中)。

调用getStudents().contains()方法的时候(即判断是否包含某个对象),会触发类似于:select 1 from T_Student where classid =? and id =? 这样的SQL查询语句。

单端关联

False proxy no-proxy

proxy:当前对象的単值相关对象只有在调用它的主键外的其他属性的get方法时才加载它。

no-proxy:当前对象的単值相关对象只有在调用它的属性时才加载,需要字节码增强。

11. Hibernate抓取策略

抓取策略是指当应用程序需要在关联关系进行导航的时候,hibernate如何获取关联对象的策略。

初始化数据

类级别检索策略 立即检索 延迟检索

默认的检索策略是立即检索。在Hibernate映射文件中,通过在上配置 lazy属性来确定检索策略。对于Session的检索方式,类级别检索策略仅适用于load方法;也就说,对于get、qurey检索,持久化对象都会被立即加载而不管lazy是false还是true.一般来说,我们检索对象就是要访问它,因此立即检索是通常的选择。由于load方法在检索不到对象时会抛出异常(立即检索的情况下),因此我个人并不建议使用load检索;而由于中的lazy属性还影响到多对一的检索策略,因此使用load方法就更没必要了。

关联级别检索策略

fetch (默认值select) Join Join join select select select Lazy (默认值是true) 策略 False True Extra False True Extra 采用迫切左外联接检索。 采用迫切左外联接检索。 采用迫切左外联接检索。 采用立即检索 采用延迟检索 采用延迟检索 c.getOrders().size() 执行 select count(id) from orders where customer_id =? for(Order o:set){ o.getOrderNumber();} 将执行: select customer_id , id,order_number ,price from orders where customer_id=? subselect false/true/extra 嵌套子查询(检索多个customer对象时) Lazy属也分为3中情况 性决定检索策略) select customer_id,order_number,price from orders where customer_id in (select id from customers) 检索策略 立即检索 对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象 (1)select语句多 (2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。 优点 缺点 优先考虑使用的场合 (1)类级别 (2)应用程序需要立即访问的对象 (3)使用了二级缓存 (1)一对多或者多对多关联 (2)应用程序不需要立即访问或者根本不会访问的对象 延迟检索 由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。 应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。 迫切左外连接检索 (1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。 (2)使用了外连接,select语句少 (1)可能会加载应用程序不需要访问的对象,浪费内存。 (2)复杂的数据库表连接也会影响检索性能。 (1)多对一 (2)需要立即访问的对象 (3)数据库有良好的表连接性能。

本文来源:https://www.bwwdw.com/article/skxd.html

Top