您的位置:首页 > 其它

hibernate4.1.1 从入门到总结

2017-11-01 16:47 253 查看
Hibernate

流行的ORM框架

Hibernate:

非常优秀、成熟的 ORM
框架。

完成对象的持久化操作

Hibernate 允许开发者采用面向对象的方式来操作关系数据库。

消除那些针对特定数据库厂商的 SQL
代码

myBatis:

相比 Hibernate
灵活高,运行速度快

开发速度慢,不支持纯粹的面向对象操作,需熟悉sql语句,并且熟练使用sql语句优化功能

TopLink:

OJB:

什么是hibernate

hibernate是一个框架

hibernate是一个 Java
领域的持久化框架

hibernate是一个 ORM 框架

对象的持久化

狭义的理解,“持久化”仅仅指把对象永久保存到数据库中

广义的理解,“持久化”包括和数据库相关的各种操作:

保存:把对象永久保存到数据库中。

更新:更新数据库中对象(记录)的状态。

删除:从数据库中删除一个对象。

查询:根据特定的查询条件,把符合查询条件的一个或多个对象从数据库加载到内存中。

加载:根据特定的OID,把一个对象从数据库加载到内存中。

OID:为了在系统中能够找到所需对象,需要为每一个对象分配一个唯一的标识号。在关系数据库中称之为主键,而在对象术语中,则叫做对象标识(Object identifier-OID).

ORM

ORM(Object/Relation Mapping): 对象/关系映射

ORM 主要解决对象-关系的映射

面向对象概念

面向关系概念





对象

表的行(记录)

属性

表的列(字段)

1. ORM的思想:将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。

2. ORM 采用元数据来描述对象-关系映射细节,
元数据通常采用 XML
格式,
并且存放在专门的对象-关系映射文件中.

ORM模型图



Hibernate 与jdbc

Hibernate 实现

Public void insertData(Session sess,Uses users){

sess.save(users);

}

Jdbc实现

Public void insertData(Connection conn,Users users){

String sql =
"insert into users (name, age) values(?,?)";

PreparedStatement pstmt =
conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

Try {

pstmt.setString(1,
users.getName());

pstmt.setString(2, users.getAge());

pstmt.executeUpdate();

//检索由于执行此
Statement 对象而创建的所有自动生成的键

ResultSet rs =
pstmt.getGeneratedKeys();

if (rs.next()) {

//知其仅有一列,故获取第一列

Long id =
rs.getLong(1);

System.out.println("-----预定义SQL模式-----id
= " + id);

}

}catch(Exception e){

}finally{

Rs.close();

Pstmt.close();

}

}

Hibernate下载

https://sourceforge.net/projects/hibernate/files找到要下载的版本即可

(文献:http://blog.csdn.net/chuck_kui/article/details/54811445)

安装 hibernate
插件

可以不装

安装方法说明(hibernatetools-4.1.1.Final):

可以先下载安装包,也可在线安装,以安装eclipse插件的方式安装:

Help --> Install New Software...

http://download.jboss.org/jbosstools/updates/stable/mars/

mars为eclipse版本名称



准备 Hibernate
环境

工程hibernateTest2的helloworld

导入 Hibernate
必须的 jar 包和测试表以及数据库驱动:

antlr-2.7.7.jar

c3p0-0.9.2.1.jar

dom4j-1.6.1.jar

hamcrest-all-1.3.jar

hibernate-c3p0-4.2.4.Final.jar

hibernate-commons-annotations-4.0.2.Final.jar

hibernate-core-4.2.4.Final.jar

hibernate-jpa-2.0-api-1.0.1.Final.jar

javassist-3.15.0-GA.jar

jboss-logging-3.1.0.GA.jar

jboss-transaction-api_1.1_spec-1.0.1.Final.jar

junit-4.12.jar

mchange-commons-java-0.2.3.4.jar

mysql-connector-java-5.1.7-bin.jar

Hibernate开发步骤

创建hibernate配置文件:hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- 配置连接数据库的基本信息
-->

<property name="connection.username">root</property>

<property name="connection.password">root</property>

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.url">jdbc:mysql:///hibernate5</property>

<!-- 配置 hibernate 的基本信息
-->

<!-- hibernate 所使用的数据库方言
-->

<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>

<!-- 执行操作时是否在控制台打印
SQL -->

<property name="show_sql">true</property>

<!-- 是否对 SQL
进行格式化 -->

<property name="format_sql">true</property>

<!-- 指定自动生成数据表的策略
-->

<property name="hbm2ddl.auto">update</property>

<!-- 指定关联的 .hbm.xml
文件 -->

<mapping resource="com/yr/hibernate/helloworld/News.hbm.xml"/>

</session-factory>

</hibernate-configuration>

创建持久化类

提供一个无参的构造器:使Hibernate可以使用Constructor.newInstance()
来实例化持久化类

提供一个标识属性(identifier property): 通常映射为数据库表的主键字段.
如果没有该属性,一些功能将不起作用,如:Session.saveOrUpdate()

为类的持久化类字段声明访问方法(get/set): Hibernate对JavaBeans
风格的属性实行持久化。

使用非 final 类:
在运行时生成代理是 Hibernate
的一个重要的功能.
如果持久化类没有实现任何接口, Hibnernate
使用 CGLIB
生成代理.
如果使用的是 final
类,
则无法生成 CGLIB
代理.

重写 eqauls 和
hashCode 方法:
如果需要把持久化类的实例放到 Set
中(当需要进行关联映射时),
则应该重写这两个方法

Hibernate 不要求持久化类继承任何父类或实现接口,这可以保证代码不被污染。这就是Hibernate被称为低侵入式设计的原因

package com.yr.hibernate.helloworld;

import java.sql.Blob;

import java.util.Date;

public class News {

private Integer id; // field

private String title;

private String author;

private String desc;

// 使用 title + "," + content
可以来描述当前的 News
记录.

// 即 title + "," + content
可以作为 News
的 desc
属性值

private String content;

private Blob picture;

public Blob getPicture() {

return picture;

}

public void setPicture(Blob picture) {

this.picture = picture;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public String getDesc() {

return desc;

}

public void setDesc(String desc) {

this.desc = desc;

}

private Date date;

public Integer getId() { // property

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public String getAuthor() {

return author;

}

public void setAuthor(String author) {

this.author = author;

}

public Date getDate() {

return date;

}

public void setDate(Date date) {

this.date = date;

}

public News(String title, String author, Date date) {

super();

this.title = title;

this.author = author;

this.date = date;

}

public News() {

// TODO Auto-generated constructor stub

}

@Override

public String toString() {

return "News [id=" + id + ", title=" + title + ", author=" + author + ", date=" + date + "]";

}

}

创建对象-关系映射文件*.hbm.xml

Hibernate 采用 XML
格式的文件来指定对象和关系数据之间的映射.
在运行时 Hibernate
将根据这个映射文件来生成各种 SQL
语句

映射文件的扩展名为 .hbm.xml

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.yr.hibernate.helloworld">

<class name="News" table="NEWS" dynamic-insert="true">

<id name="id" type="java.lang.Integer">

<column name="ID" />

<!--
指定主键的生成方式, native:
使用数据库本地方式 -->

<generator class="native" />

</id>

<property name="title" not-null="true" unique="true"

index="news_index" length="50"

type="java.lang.String" column="TITLE" >

</property>

<property name="author" type="java.lang.String"

index="news_index">

<column name="AUTHOR" />

</property>

<property name="date" type="date">

<column name="DATE" />

</property>

<property name="desc"

formula="(SELECT concat(title, ',', author) FROM NEWS n WHERE n.id = id)"></property>

<property name="content">

<column name="CONTENT" sql-type="text"></column>

</property>

<property name="picture" column="PICTURE" type="blob"></property>

</class>

</hibernate-mapping>

通过 Hibernate API
编写访问数据库的代码(测试代码)

package com.yr.hibernate.helloworld;

import java.sql.Date;

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.service.ServiceRegistry;

import org.hibernate.service.ServiceRegistryBuilder;

import org.junit.Test;

public class HibernateTest {

@Test

public void test() {

System.out.println("test...");

//1. 创建一个 SessionFactory
对象

SessionFactory sessionFactory = null;

//1). 创建 Configuration
对象:
对应 hibernate
的基本配置信息和 对象关系映射信息

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

//4.0 之前这样创建

// sessionFactory = configuration.buildSessionFactory();

//2). 创建一个 ServiceRegistry
对象: hibernate 4.x
新添加的对象

//hibernate 的任何配置和服务都需要在该对象中注册后才能有效.

ServiceRegistry serviceRegistry =

new ServiceRegistryBuilder().applySettings(configuration.getProperties())

.buildServiceRegistry();

//3).

sessionFactory = configuration.buildSessionFactory(serviceRegistry);

//2. 创建一个 Session
对象

Session session = sessionFactory.openSession();

//3. 开启事务

Transaction transaction = session.beginTransaction();

//4. 执行保存操作

News news = new News("hibernate", "张三", new Date(new java.util.Date().getTime()));

session.save(news);

//5. 提交事务

transaction.commit();

//6. 关闭 Session

session.close();

//7. 关闭 SessionFactory
对象

sessionFactory.close();

}

}

创建mysql表

DROP TABLE IF EXISTS news;

CREATE TABLE news (

id int(11) NOT NULL AUTO_INCREMENT,

title varchar(255) DEFAULT NULL,

author varchar(255) DEFAULT NULL,

desc varchar(255) DEFAULT NULL,

content varchar(255) DEFAULT NULL,

picture blob,

date datetime DEFAULT NULL,

PRIMARY KEY (id),

KEY news_index (title,author)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

执行结果





Hibernate开发与应用结构结合图



Configuration类

Configuration 类负责管理 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();

Configuration 的 configure
方法还支持带参数的访问:

File file = new File(“simpleit.xml”);

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

SessionFactory 接口

针对单个数据库映射关系经过编译后的内存镜像,是线程安全的。

SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息

SessionFactory是生成Session的工厂

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

Hibernate4 新增了一个 ServiceRegistry
接口,所有基于 Hibernate
的配置或者服务都必须统一向这个 ServiceRegistry 注册后才能生效

Hibernate4 中创建 SessionFactory
的步骤

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

ServiceRegistry serviceRegistry =
new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry();

SessionFactory
sessionFactory =
configuration.buildSessionFactory(serviceRegistry);

Session 接口

Session 是应用程序与数据库之间交互操作的一个单线程对象,是
Hibernate 运作的中心,所有持久化对象必须在
session 的管理下才可以进行持久化操作。此对象的生命周期很短。Session
对象有一个一级缓存,显式执行 flush
之前,所有的持久层操作的数据都缓存在 session
对象处。相当于
JDBC 中的 Connection。



持久化类与 Session 关联起来后就具有了持久化的能力。

Session 类的方法:

取得持久化对象的方法: get() load()

持久化对象都得保存,更新和删除:save(),update(),saveOrUpdate(),delete()

开启事务: beginTransaction().

管理 Session 的方法:isOpen(),flush(), clear(), evict(), close()等

Transaction(事务)

代表一次原子操作,它具有数据库事务的概念。所有持久层都应该在事务管理下进行,即使是只读操作。

Transaction tx = session.beginTransaction();

常用方法:

commit():提交相关联的session实例

rollback():撤销事务操作

wasCommitted():检查事务是否提交

Hibernate 配置文件的两个配置项

hbm2ddl.auto:该属性可帮助程序员实现正向工程,
即由 java
代码生成数据库脚本,
进而生成具体的表结构.
取值 create | update | create-drop | validate

create : 会根据 .hbm.xml 文件来生成数据表,
但是每次运行都会删除上一次的表 ,重新生成表,
哪怕二次没有任何改变

create-drop : 会根据 .hbm.xml
文件生成表,但是SessionFactory一关闭,
表就自动删除

update : 最常用的属性值,也会根据 .hbm.xml
文件生成表,
但若 .hbm.xml 文件和数据库中对应的数据表的表结构不同, Hiberante 将更新数据表结构,但不会删除已有的行和列

validate : 会和数据库中的表进行比较,
若 .hbm.xml
文件中的列在数据表中不存在,则抛出异常

format_sql:是否将 SQL
转化为格式良好的 SQL .
取值 true | false

通过 Session
操纵对象

工程hibernateTest2的entities

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

2. Session
具有一个缓存, 位于缓存中的对象称为持久化对象,
它和数据库中的相关记录对应. Session
能够在某些时间点, 按照缓存中对象的变化来执行相关的
SQL 语句,
来同步更新数据库,
这一过程被称为刷新缓存(flush)

3. 站在持久化的角度, Hibernate
把对象分为 4 种状态:
持久化状态,
临时状态,
游离状态,
删除状态. Session
的特定方法能使对象从一个状态转换到另一个状态.

Session 缓存

在 Session 接口的实现中包含一系列的
Java 集合,
这些 Java
集合构成了 Session
缓存.
只要 Session
实例没有结束生命周期,
且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期

Session 缓存可减少 Hibernate
应用程序访问数据库的频率。

@Test

public void getData() {

Session session =
sessionFactory.openSession();

News news1 = (News)
session.get(News.class,1);

News news2 = (News)
session.get(News.class,1);

System.out.println(news1 ==
news2);

//这段程序会后数据库发送几条sql?
答案是1条,两个对象是相等的

}



flush 缓存

flush:Session
按照缓存中对象的属性变化来同步更新数据库

默认情况下 Session 在以下时间点刷新缓存:

显式调用 Session 的
flush() 方法

当应用程序调用 Transaction 的
commit()方法的时,
该方法先 flush
,然后在向数据库提交事务

当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先
flush 缓存,以保证查询结果能够反映持久化对象的最新状态

flush 缓存的例外情况:
如果对象使用 native
生成器生成 OID,
那么当调用 Session
的 save()
方法保存对象时,
会立即执行向数据库插入该实体的 insert
语句.

commit() 和 flush()
方法的区别:flush
执行一系列 sql
语句,但不提交事务;commit
方法先调用flush()
方法,然后提交事务.
意味着提交事务意味着对数据库操作永久保存下来。

/**

* session flush方法 使数据表中的记录和
Session 缓存中的对象的状态保持一致.
为了保持一致,
则可能会发送对应的 SQL语句.

*/

@Test

public void testSessionFlush(){

News news = (News)
session.get(News.class, 1);//发起了select

news.setAuthor("gsl");
//修改了持久化对象中的值,执行完后,hibernate发送了一个update

/*

* 因为在同一session中,此时session还没有关,并在同一个事物中,所以修改持久化对象,session会知道

* 而在提交事务时,则会把持久化对象中的数据同步到数据库中 ,也就是会发update请求,

* 1,transaction.commit();执行前会执行flush操作,再提交事务

* 2,flush方法可能发送sql,但不会提交事务

*/

//session.flush();

//System.out.println("flush");

/*

* 注:有两种情况特殊情况,会自动调用flush方法

* 在未提交事务或未显示的调用flush方法,也可能会执行flush操作

* 1)执行HQL或QBC查询时会执行flush方法,以得到最新的记录

*/

//News news2 = (News) session.createCriteria(News.class).uniqueResult();

//执行这里行发执行flush方法,发一个update
以保证得到对象中的数据和数据库中一致,之后再发一个select以从数据库中得到最新的数据

//System.out.println(news2);

/*

* 2)若记录的 ID
是由底层数据库使用自增的方式生成的,
则在调用 save()
方法时,
就会立即发送 INSERT
语句.

* 因为 save
方法后,
必须保证对象的 ID
是存在的。执行完insert操作后持久化对象中的数据与数据库中一致此为flush操作

* 2-1)若记录ID是由hilo方式生成的,则在调用save方法时,会执行select
和update操作为得的保存ID在持久对象中存在

* 再执行commit时才发送insert

*/

News news3 =
new News("struts2",
"s2团队",
new Date());

session.save(news3);

}

refresh

/**

* refresh(): 会强制发送
SELECT 语句,
以使 Session
缓存中对象的状态和数据

表中对应的记录保持一致!

* 为了更好的看到强制发送select的效果,按以下步骤操作

*/

@Test

public void testRefresh(){

News news = (News)
session.get(News.class, 1);
//1

System.out.println(news);
//2
执行完这行代码后,手动到数据库中修改此条数据,再执行3

session.refresh(news);
//3
强制从数据库中查询了一次

System.out.println(news);
//4

}

Clear

/**

* clear(): 清理缓存

*/

@Test

public void testClear(){

News news1 = (News)
session.get(News.class, 1);
//同一个session中执行get方法只会发送一个select

session.clear(); //清理之后会再发送一次

News news2 = (News)
session.get(News.class, 1);

}

Hibernate 主键生成策略



设定刷新缓存的时间点

若希望改变 flush 的默认时间点,
可以通过 Session
的 setFlushMode()
方法显式设定 flush
的时间点



数据库的隔离级别

对于同时运行的多个事务,
当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制,
就会导致各种并发问题:

脏读: 对于两个事物
T1, T2, T1 读取了已经被
T2 更新但还没有被提交的字段.
之后,
若 T2
回滚, T1读取的内容就是临时且无效的.

不可重复读: 对于两个事物
T1, T2, T1 读取了一个字段,
然后 T2
更新了该字段.
之后, T1再次读取同一个字段,
值就不同了.

幻读: 对于两个事物
T1, T2, T1 从一个表中读取了一个字段,
然后 T2
在该表中插入了一些新的行.
之后,
如果 T1
再次读取同一个表,
就会多出几行.

数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力,
使它们不会相互影响,
避免各种并发问题.

一个事务与其他事务隔离的程度称为隔离级别.
数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度,
隔离级别越高,
数据一致性就越好,
但并发性越弱

数据库提供的 4 种事务隔离级别:



Oracle 支持的 2
种事务隔离级别:READ COMMITED和SERIALIZABLE.

Oracle 默认的事务隔离级别为: READ COMMITED

Mysql 支持 4
中事务隔离级别. Mysql
默认的事务隔离级别为: REPEATABLE READ

在 Hibernate
中设置隔离级别

JDBC 数据库连接使用数据库系统默认的隔离级别.
在 Hibernate
的配置文件中可以显式的设置隔离级别.
每一个隔离级别都对应一个整数:

1. READ UNCOMMITED

2. READ COMMITED

4. REPEATABLE READ

8. SERIALIZEABLE

Hibernate 通过为 Hibernate
映射文件指定 hibernate.connection.isolation
属性来设置事务的隔离级别

在 MySql
中设置隔离级别

每启动一个 mysql 程序,
就会获得一个单独的数据库连接.
每个数据库连接都有一个全局变量 @@tx_isolation,
表示当前的事务隔离级别. MySQL
默认的隔离级别为 Repeatable Read

查看当前的隔离级别: SELECT @@tx_isolation;

设置当前 mySQL 连接的隔离级别:

set transaction isolation level read committed;

设置数据库系统的全局的隔离级别:

set global transaction isolation level read committed;

持久化对象的状态

站在持久化的角度, Hibernate
把对象分为 4 种状态:
持久化状态,
临时状态,
游离状态,
删除状态. Session
的特定方法能使对象从一个状态转换到另一个状态.

临时对象(Transient):

在使用代理主键的情况下, OID
通常为 null

不处于 Session 的缓存中

在数据库中没有对应的记录

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

OID 不为 null

位于 Session 缓存中

若在数据库中已经有和其对应的记录,
持久化对象和数据库中的相关记录对应

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

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

删除对象(Removed):

在数据库中没有和其 OID
对应的记录

不再处于 Session
缓存中

一般情况下, 应用程序不该再使用被删除的对象

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

OID 不为 null

不再处于 Session
缓存中

一般情况需下, 游离对象是由持久化对象转变过来的,
因此在数据库中可能还存在与它对应的记录



Session 的 save()
方法

Session 的 save()
方法使一个临时对象转变为持久化对象

Session 的 save()
方法完成以下操作:

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

选用映射文件指定的标识符生成器,
为持久化对象分配唯一的 OID. 在 使用代理主键的情况下, setId()
方法为 News
对象设置 OID
使无效的.

计划执行一条 insert
语句:在 flush 缓存的时候

Hibernate 通过持久化对象的 OID
来维持它和数据库相关记录的对应关系.
当 News
对象处于持久化状态时,
不允许程序随意修改它的 ID

/**

* 1. save() 方法

* 1). 使一个临时对象变为持久化对象

* 2). 执行save方法之后要为对象分配
ID.

* 3). 在 flush
缓存时会发送一条 INSERT
语句.

* 4). 在 save
方法之前的 id
是无效的

* 5). 持久化对象的
ID 是不能被修改的

*/

//@Test

public void testSave(){

News news =
new News();

news.setTitle("mysql7");

news.setAuthor("mysql7");

news.setDate(new Date());

news.setId(11);
//save之前的ID无效

System.out.println(news);

session.save(news);

System.out.println(news);

news.setId(90);
//异常,不能修改ID

System.out.println(news);

}

persist() 和 save()
区别:

当对一个 OID 不为
Null 的对象执行
save() 方法时,
会把该对象以一个新的 oid
保存到数据库中; 但执行
persist() 方法时会抛出一个异常.

/**

* persist(): 也会执行
INSERT 操作

*

* 和 save()
的区别 :

* 在调用 persist
方法之前,
若对象已经有 id
了,
则不会执行 INSERT,
而抛出异常

*/

@Test

public void testPersist(){

News news =
new News();

news.setTitle("FF");

news.setAuthor("ff");

news.setDate(new Date());

news.setId(200);
//有ID就不再执行insert,也会抛异常

session.persist(news);

}

Session 的 get()
和 load()
方法

都可以根据跟定的 OID
从数据库中加载一个持久化对象

区别:

当数据库中不存在与 OID
对应的记录时, load() 方法抛出
ObjectNotFoundException 异常,
而 get()
方法返回 null

两者采用不同的延迟检索策略:load
方法支持延迟加载策略。而 get 不支持。

/**

* get VS load:

*

* 1. 执行 get
方法:
会立即加载对象.

* 执行 load
方法,
若不适用该对象,
则不会立即执行查询操作,
而返回一个代理对象

* get 是 立即检索, load
是延迟检索.

* 2. 若数据表中没有对应的记录, Session
也没有被关闭.

* get 返回 null

* load 若不使用该对象的任何属性,
没问题;
若需要使用(初始化),
抛出异常.

*

* 3. load 方法可能会抛出
LazyInitializationException 异常:
在初始化

* 代理对象之前已经关闭了
Session

* get方法不会

*

*/

@Test

public void testLoad(){

//News news = (News) session.load(News.class, 1); //如果没有使用对象,则不会发select,而只是返回一个代理

//System.out.println(news.getClass().getName());

//News news1 = (News) session.load(News.class, 100);

//System.out.println(news1); //查询一个不存在的数据,并使用这个对象,则抛异常

News news2 = (News)
session.load(News.class, 1);

session.close();

System.out.println(news2);
//初始化(使用)代理对象之前已经关闭了
Session会抛出
LazyInitializationException 异常

}

@Test

public void testGet(){

//News news = (News) session.get(News.class, 1);//没有使用对象中的任何属性,也会立刻发select,返回一个对象

//System.out.println(news.getClass().getName());

//News news1 = (News) session.get(News.class, 1000);

//System.out.println(news1); //查询一个不存在的数据,并使用这个对象,则返回null

News news2 = (News)
session.get(News.class, 1);

session.close();

System.out.println(news2);
//在get方法执行完之后news2对象已经加载了数据,则与关session没关系

}

Session 的 update()
方法

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

若希望 Session 仅当修改了
News 对象的属性时,
才执行 update()
语句,
可以把映射文件中 <class>
元素的 select-before-update
设为 true.
该属性的默认值为 false

当 update() 方法关联一个游离对象时,
如果在 Session
的缓存中已经存在相同 OID
的持久化对象,
会抛出异常

当 update() 方法关联一个游离对象时,
如果在数据库中不存在相应的记录,
也会抛出异常.

/**

* update 将游离对象变为持久对象:

* 1. 若更新一个持久化对象,
不需要显示的调用 update
方法.
因为在调用 Transaction

* 的 commit()
方法时,
会先执行 session
的 flush
方法.

* 2. 更新一个游离对象,
需要显式的调用 session
的 update
方法.
可以把一个游离对象

* 变为持久化对象

*

* 需要注意的:

* 1. 无论要更新的游离对象和数据表的记录是否一致,
只要调用update方法都会发送
UPDATE 语句.

* hibernate与触发器一起使用时,可能导致触发器做多余的工作。

* 在 .hbm.xml
文件的 class
节点设置

* select-before-update=true (默认为
false).表示在update之前查一次数据,这样就不会盲目的update

* 但通常不需要设置该属性.

*

* 2. 若数据表中没有对应的记录,
但还调用了 update
方法,
会抛出异常

*

* 3. 当 update()
方法关联一个游离对象时,

* 如果在 Session
的缓存中已经存在相同 OID
的持久化对象,
会抛出异常.
因为在 Session
缓存中

* 不能有两个 OID
相同的对象

*

*/

//@Test

public void testUpdate(){

//News news = (News) session.get(News.class, 1); //news从数据库中查出来是一个持久化对象

//news.setAuthor("sun");

/*若更新一个持久化对象,
不需要显示的调用 update
方法.
因为在调用 Transaction

*的 commit()
方法时,
会先执行 session
的 flush
方法

*/

/*News news = (News) session.get(News.class, 1); //查出一个持久对象news,

transaction.commit(); //提交事务,

session.close();//关了session,此时news对象成为了游离对象(数据库中存在对象的记录,但对象不在session中)

session = sessionFactory.openSession(); //再开一个session,这个session是新的

transaction = session.beginTransaction(); //新事务

news.setAuthor("oracle"); //执行完后不会再update,因为news对象在新的session不存在,不复合同一个session中持久化对象与数据库数据不一致的条件

//session.update(news);
如果希望持久化则需要显示调用update

*/

/*

News news2 = (News) session.get(News.class, 1);

transaction.commit();

session.close();

session = sessionFactory.openSession();

transaction = session.beginTransaction();

session.update(news2); //设置了select-before-update="true"当对象不在session中时就去更新,则会先查一次,如对象与数据库一致则不会再发update

*/

/*News news = new News();

news.setTitle("SS");

news.setAuthor("ss");

news.setDate(new Date());

news.setId(10000); //无对应记录

session.update(news);//异常

*/

/*News news2 = (News) session.get(News.class, 1);

transaction.commit();

session.close(); //news2是游离对象了

session = sessionFactory.openSession();

transaction = session.beginTransaction();

News news = (News) session.get(News.class, 1);//又查了一次,也就是在session的对象与游离对象数据相同,并在表中存在

session.update(news2); //更新这个游离对象,抛org.hibernate.NonUniqueObjectException表示session的对象不是唯一的(OID相同)

*/

}

Session 的 saveOrUpdate()
方法

Session 的 saveOrUpdate()
方法同时包含了 save()
与 update()
方法的功能



判定对象为临时对象的标准

Java 对象的 OID
为 null

映射文件中为 <id> 设置了
unsaved-value 属性,
并且 Java
对象的 OID
取值与这个 unsaved-value
属性值匹配

/**

* 当OID为空,则对象为临时对象,则么则执行save

* 如果OID不为空,并在表中有记录,则这是一个游离对象,则执行update

*

* 注意:

* 1. 若 OID
不为 null,
但数据表中还没有和其对应的记录.
会抛出一个异常(与update特点相同).

* 2. 了解: hbm.xml文件中的
<id> unsaved-value的属性
OID 值等于
id 的
unsaved-value
属性值的对象,

* 也被认为是一个游离对象

*/

@Test

public void testSaveOrUpdate(){

/*News news = new News("javaScript", "javaScript", new Date());

session.saveOrUpdate(news); */

/*News news = new News("S", "s", new Date());

news.setId(98308); //配置文件中有:unsaved-value="98308"
则news被认为是一个游离对象

session.saveOrUpdate(news);*/

}

Session 的 merge()
方法



Session 的 delete()
方法

Session 的 delete()
方法既可以删除一个游离对象,
也可以删除一个持久化对象

Session 的 delete()
方法处理过程

计划执行一条 delete
语句

把对象从 Session
缓存中删除, 该对象进入删除状态.

Hibernate 的 cfg.xml
配置文件中有一个 hibernate.use_identifier_rollback
属性,
其默认值为 false,
若把它设为 true,
将改变 delete()
方法的运行行为: delete()
方法会把持久化对象或游离对象的 OID
设置为 null,
使它们变为临时对象

/**

* delete: 执行删除操作.
只要 OID
和数据表中一条记录对应,
就会准备执行 delete
操作

* 若 OID
在数据表中没有对应的记录,
则抛出异常

*

* 可以通过设置 hibernate 配置文件
hibernate.use_identifier_rollback 为
true,

* 使删除对象后,
把其 OID
置为 null

*/

//@Test

public void testDelete(){

/*News news = new News();

news.setId(98310); //删一个游离对象

session.delete(news);

*/

/*News news = (News) session.get(News.class, 98305);

session.delete(news); //删一个持久对象

System.out.println(news); //如果不设置
hibernate.use_identifier_rollback= true,则该对象还有ID,此时,对象成为了临时对象

所以需要设置hibernate.use_identifier_rollback= true

*/

}

Session 的evict
方法

/**

* evict: 从 session
缓存中把指定的持久化对象移除

*/

@Test

public void testEvict(){

News news1 = (News)
session.get(News.class, 98308);

News news2 = (News)
session.get(News.class, 3);

news1.setTitle("e1");

news2.setAuthor("e2");

session.evict(news1); //news1被移除了,成为游离对象,flush时,无法发起update

}

通过 Hibernate
调用存储过程

Work 接口:
直接通过 JDBC API
来访问数据库的操作

Hibernate 与触发器协同工作

Hibernate 与数据库中的触发器协同工作时,
会造成两类问题

触发器使 Session
的缓存中的持久化对象与数据库中对应的数据不一致:触发器运行在数据库中,
它执行的操作对 Session
是透明的

Session 的 update()
方法盲目地激发触发器:
无论游离对象的属性是否发生变化,
都会执行 update
语句,
而 update
语句会激发数据库中相应的触发器

解决方案:

在执行完 Session
的相关操作后, 立即调用
Session 的
flush() 和
refresh() 方法,
迫使 Session
的缓存与数据库同步(refresh()
方法重新从数据库中加载对象)



在映射文件的的 <class>
元素中设置 select-before-update 属性:
当 Session
的 update
或 saveOrUpdate()
方法更新一个游离对象时,
会先执行 Select
语句,
获得当前游离对象在数据库中的最新数据,
只有在不一致的情况下才会执行 update
语句

Hibernate配置文件

Hibernate 配置文件主要用于配置数据库连接和 Hibernate
运行时所需的各种属性

每个 Hibernate
配置文件对应一个 Configuration 对象

Hibernate配置文件可以有两种格式:

hibernate.properties

hibernate.cfg.xml

Hibernate.cfg.xml的常用属性

JDBC 连接属性

connection.url:数据库URL

connection.username:数据库用户名

connection.password:数据库用户密码

connection.driver_class:数据库JDBC驱动

dialect:配置数据库的方言,根据底层的数据库不同产生不同的 sql
语句,Hibernate
会针对数据库的特性在访问时进行优化

C3P0 数据库连接池属性

导入c3p0的jar:

hibernate.c3p0.max_size: 数据库连接池的最大连接数

hibernate.c3p0.min_size: 数据库连接池的最小连接数

hibernate.c3p0.timeout: 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁

hibernate.c3p0.max_statements: 缓存 Statement
对象的数量

hibernate.c3p0.idle_test_period: 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时.
连接池本身不会把自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来做这件事,这个线程通过比较连接对象最后一次被使用时间和当前时间的时间差来和
timeout 做对比,进而决定是否销毁这个连接对象。

hibernate.c3p0.acquire_increment: 当数据库连接池中的连接耗尽时,
同一时刻获取多少个数据库连接

其他

show_sql:是否将运行期生成的SQL输出到日志以供调试。取值
true | false

format_sql:是否将 SQL
转化为格式良好的 SQL .
取值 true | false

hbm2ddl.auto:在启动和停止时自动地创建,更新或删除数据库模式。取值 create | update | create-drop | validate

hibernate.jdbc.fetch_size

hibernate.jdbc.batch_size

jdbc.fetch_size 和 jdbc.batch_size

hibernate.jdbc.fetch_size:实质是调用 Statement.setFetchSize()
方法设定 JDBC
的 Statement
读取数据的时候每次从数据库中取出的记录条数。

例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会
1 次性把1万条取出来的,而只会取出
fetchSize 条数,当结果集遍历完了这些记录以后,再去数据库取
fetchSize 条数据。因此大大节省了无谓的内存消耗。Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch
Size越小,读数据库的次数越多,速度越慢。Oracle数据库的JDBC驱动默认的Fetch
Size = 10,是一个保守的设定,根据测试,当Fetch Size=50时,性能会提升1倍之多,当
fetchSize=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。并不是所有的数据库都支持Fetch
Size特性,例如MySQL就不支持

hibernate.jdbc.batch_size:设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,类似于设置缓冲区大小的意思。batchSize
越大,批量操作时向数据库发送sql的次数越少,速度就越快。

测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch
Size = 50的时候,删除仅仅需要5秒!Oracle数据库
batchSize=30 的时候比较合适。

对象关系映射文件

POJO 类和数据库的映射文件*.hbm.xml

POJO 类和关系数据库之间的映射可以用一个XML文档来定义。

通过 POJO 类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表列之间的对应关系

在运行时 Hibernate
将根据这个映射文件来生成各种 SQL 语句

映射文件的扩展名为 .hbm.xml

映射文件说明

hibernate-mapping

类层次:class

主键:id

基本类型:property

实体引用类: many-to-one | one-to-one

集合:set | list | map | array

one-to-many

many-to-many

子类:subclass | joined-subclass

其它:component | any


查询语句:query(用来放置查询语句,便于对数据库查询的统一管理和优化)

每个Hibernate-mapping中可以同时定义多个类.
但更推荐为每个类都创建一个单独的映射文件

hibernate-mapping

hibernate-mapping 是 hibernate
映射文件的根元素

schema: 指定所映射的数据库schema的名称。若指定该属性,
则表明会自动添加该 schema
前缀

catalog:指定所映射的数据库catalog的名称。

default-cascade(默认为 none):
设置hibernate默认的级联风格.
若配置 Java
属性,
集合映射时没有指定 cascade
属性,
则 Hibernate
将采用此处指定的级联风格.

default-access (默认为 property):
指定 Hibernate
的默认的属性访问策略。默认值为 property,
即使用 getter, setter
方法来访问属性.
若指定 access,
则 Hibernate
会忽略 getter/setter
方法,
而通过反射访问成员变量.

default-lazy(默认为 true):
设置 Hibernat morning的延迟加载策略.
该属性的默认值为 true,
即启用延迟加载策略.
若配置 Java
属性映射,
集合映射时没有指定 lazy
属性,
则 Hibernate
将采用此处指定的延迟加载策略

auto-import (默认为 true):
指定是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。

package (可选):
指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。



Class

class 元素用于指定类和表的映射

name:指定该持久化类映射的持久化类的类名

table:指定该持久化类映射的表名, Hibernate
默认以持久化类的类名作为表名

dynamic-insert: 若设置为 true,
表示当保存一个对象时,
会动态生成 insert
语句, insert
语句中仅包含所有取值不为 null
的字段.
默认值为 false

dynamic-update: 若设置为 true,
表示当更新一个对象时,
会动态生成 update
语句, update
语句中仅包含所有取值需要更新的字段.
默认值为 false

select-before-update:设置 Hibernate
在更新某个持久化对象之前是否需要先执行一次查询.
默认值为 false

batch-size:指定根据 OID
来抓取实例时每批抓取的实例数.

lazy: 指定是否使用延迟加载.

mutable: 若设置为 true,
等价于所有的 <property>
元素的 update
属性为 false,
表示整个实例不能被更新.
默认为 true.

discriminator-value: 指定区分不同子类的值.
当使用 <subclass/>
元素来定义持久化类的继承关系时需要使用该属性





映射对象标识符

Hibernate 使用对象标识符(OID)
来建立内存中的对象和数据库表中记录的对应关系.
对象的 OID
和数据表的主键对应. Hibernate
通过标识符生成器来为主键赋值

Hibernate 推荐在数据表中使用代理主键,
即不具备业务含义的字段.
代理主键通常为整数类型,
因为整数类型比字符串类型要节省更多的数据库空间.

在对象-关系映射文件中, <id>
元素用来设置对象标识符. <generator>
子元素用来设定标识符生成器.

Hibernate 提供了标识符生成器接口: IdentifierGenerator,
并提供了各种内置实现

ID

id:设定持久化类的 OID
和表的主键的映射

name: 标识持久化类 OID
的属性名

column: 设置标识属性所映射的数据表的列名(主键字段的名字).

unsaved-value:若设定了该属性, Hibernate
会通过比较持久化类的 OID
值和该属性值来区分当前持久化类的对象是否为临时对象,

type:指定 Hibernate
映射类型. Hibernate
映射类型是 Java
类型与 SQL
类型的桥梁.
如果没有为某个属性显式设定映射类型, Hibernate
会运用反射机制先识别出持久化类的特定属性的 Java
类型,
然后自动使用与之对应的默认的 Hibernate
映射类型

Java 的基本数据类型和包装类型对应相同的 Hibernate
映射类型.
基本数据类型无法表达 null,
所以对于持久化类的 OID
推荐使用包装类型



Generator主键生成策略

generator:设定持久化类设定标识符生成器

class: 指定使用的标识符生成器全限定类名或其缩写名

<id name="id" type="java.lang.Integer">

<column name="ID" />

<!--
指定主键的生成方式, native:
使用数据库本地方式 -->

<generator class="native" />

</id>

Hibernate提供的内置标识符生成器:



increment 标识符生成器

increment 标识符生成器由 Hibernate
以递增的方式为代理主键赋值

Hibernate 会先读取 NEWS
表中的主键的最大值,
而接下来向 NEWS
表中插入记录时,
就在 max(id)
的基础上递增,
增量为 1.

适用范围:

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

适用于只有单个 Hibernate
应用进程访问同一个数据库的场合, 在集群环境下不推荐使用它(有并发的问题,适用于测试环境)

OID 必须为 long, int
或 short
类型,
如果把 OID
定义为 byte
类型,
在运行时会抛出异常

identity 标识符生成器由底层数据库来负责生成标识符,
它要求底层数据库把主键定义为自动增长字段类型

适用范围:

由于 identity 生成标识符的机制依赖于底层数据库系统,
因此,
要求底层数据库系统必须支持自动增长字段类型.
支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase


OID 必须为 long, int
或 short
类型,
如果把 OID
定义为 byte
类型,
在运行时会抛出异常

identity 标识符生成器

identity 标识符生成器由底层数据库来负责生成标识符,
它要求底层数据库把主键定义为自动增长字段类型

适用范围:

由于 identity 生成标识符的机制依赖于底层数据库系统,
因此,
要求底层数据库系统必须支持自动增长字段类型.
支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase


OID 必须为 long, int
或 short
类型,
如果把 OID
定义为 byte
类型,
在运行时会抛出异常

sequence 标识符生成器 (适用于oracle)

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



Hibernate 在持久化一个 News
对象时,
先从底层数据库的 news_seq
序列中获得一个唯一的标识号,
再把它作为主键值

适用范围:

由于 sequence 生成标识符的机制依赖于底层数据库系统的序列,
因此,
要求底层数据库系统必须支持序列.
支持序列的数据库包括: DB2, Oracle


OID 必须为 long, int
或 short
类型,
如果把 OID
定义为 byte
类型,
在运行时会抛出异常

hilo 标识符生成器

hilo 标识符生成器由 Hibernate
按照一种 high/low
算法*生成标识符,
它从数据库的特定表的字段中获取 high
值.



Hibernate 在持久化一个 News
对象时,
由 Hibernate
负责生成主键值. hilo
标识符生成器在生成标识符时,
需要读取并修改 HI_TABLE
表中的 NEXT_VALUE
值.

适用范围:

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

OID 必须为 long, int
或 short
类型,
如果把 OID
定义为 byte
类型,
在运行时会抛出异常

native 标识符生成器

native 标识符生成器依据底层数据库对自动生成标识符的支持能力,
来选择使用 identity, sequence
或 hilo
标识符生成器.

适用范围:

由于 native 能根据底层数据库系统的类型,
自动选择合适的标识符生成器,
因此很适合于跨数据库平台开发

OID 必须为 long, int
或 short
类型,
如果把 OID
定义为 byte
类型,
在运行时会抛出异常

Property

property 元素用于指定类的属性和表的字段的映射

name:指定该持久化类的属性的名字

column:指定与类的属性映射的表的字段名.
如果没有设置该属性, Hibernate
将直接使用类的属性名作为字段名.

type:指定 Hibernate
映射类型. Hibernate
映射类型是 Java
类型与 SQL
类型的桥梁.
如果没有为某个属性显式设定映射类型, Hibernate
会运用反射机制先识别出持久化类的特定属性的 Java
类型,
然后自动使用与之对应的默认的 Hibernate
映射类型.

not-null:若该属性值为 true,
表明不允许为 null,
默认为 false

access:指定 Hibernate
的默认的属性访问策略。默认值为 property,
即使用 getter, setter
方法来访问属性.
若指定 field,
则 Hibernate
会忽略 getter/setter
方法,
而通过反射访问成员变量

unique: 设置是否为该属性所映射的数据列添加唯一约束.



index: 指定一个字符串的索引名称.
当系统需要 Hibernate
自动建表时,
用于为该属性所映射的数据列创建索引,
从而加快该数据列的查询.

length: 指定该属性所映射数据列的字段的长度

scale: 指定该属性所映射数据列的小数位数,
对 double, float, decimal
等类型的数据列有效.

formula:设置一个 SQL
表达式, Hibernate
将根据它来计算出派生属性的值.

派生属性: 并不是持久化类的所有属性都直接和表的字段匹配,
持久化类的有些属性的值必须在运行时通过计算才能得出来,
这种属性称为派生属性

使用 formula 属性时

formula=“(sql)” 的英文括号不能少

Sql 表达式中的列名和表名都应该和数据库对应,
而不是和持久化对象的属性对应

如果需要在 formula
属性中使用参数, 这直接使用
where cur.id=id 形式,
其中 id
就是参数,
和当前持久化对象的 id
属性对应的列的 id
值将作为参数传入.

<!--
映射派生属性 -->

<property name="desc" formula="(SELECT
concat(author, ': ', title) FROM NEWS n WHERE n.id = id)"></property>

Java 类型, Hibernate
映射类型及 SQL
类型之间的对应关系





Java 时间和日期类型的 Hibernate
映射

在 Java 中,
代表时间和日期的类型包括: java.util.Date
和 java.util.Calendar.
此外,
在 JDBC API
中还提供了 3
个扩展了 java.util.Date
类的子类: java.sql.Date, java.sql.Time
和 java.sql.Timestamp,
这三个类分别和标准 SQL
类型中的 DATE, TIME
和 TIMESTAMP
类型对应

在标准 SQL 中, DATE
类型表示日期, TIME
类型表示时间, TIMESTAMP
类型表示时间戳,
同时包含日期和时间信息.



<property name="date" type="time">

<column name="DATE" />

</property>

/**

* java.util.Date 类的子类: java.sql.Date, java.sql.Time
和 java.sql.Timestamp,

* 这三个类分别和标准
SQL 类型中的
DATE, TIME 和
TIMESTAMP 类型对应

*/

//@Test

public void testDates(){

News news = (News)
session.get(News.class, 98309);

System.out.println(news.getDate().getClass());
//date的类型是type="time"

}

使用 Hibernate
内置映射类型

以下情况下必须显式指定 Hibernate
映射类型:

一个 Java 类型可能对应多个
Hibernate 映射类型.
例如:
如果持久化类的属性为 java.util.Date
类型,
对应的 Hibernate
映射类型可以是 date, time
或 timestamp.
此时必须根据对应的数据表的字段的 SQL
类型,
来确定 Hibernate
映射类型.
如果字段为 DATE
类型,
那么 Hibernate
映射类型为 date;
如果字段为 TIME
类型,
那么 Hibernate
映射类型为 time;
如果字段为 TIMESTATMP
类型,
那么 Hibernate
映射类型为 timestamp.

Java 大对象类型的 Hiberante
映射

在 Java 中, java.lang.String
可用于表示长字符串(长度超过
255), 字节数组
byte[] 可用于存放图片或文件的二进制数据.
此外,
在 JDBC API
中还提供了 java.sql.Clob
和 java.sql.Blob
类型,
它们分别和标准 SQL
中的 CLOB
和 BLOB
类型对应. CLOB
表示字符串大对象(Character Large Object), BLOB表示二进制对象(Binary Large Object)



Mysql 不支持标准 SQL
的 CLOB
类型,
在 Mysql
中, 用
TEXT, MEDIUMTEXT 及
LONGTEXT 类型来表示长度操作
255 的长文本数据

在持久化类中, 二进制大对象可以声明为
byte[] 或
java.sql.Blob 类型;
字符串可以声明为 java.lang.String
或 java.sql.Clob

实际上在 Java 应用程序中处理长度超过
255 的字符串,
使用 java.lang.String
比 java.sql.Clob
更方便

<!-- 映射大对象 -->

<!--
若希望精确映射 SQL 类型,
可以使用 sql-type
属性. -->

<property name="content">

<column name="CONTENT" sql-type="mediumtext"></column>

</property>

<property name="image">

<column name="IMAGE" sql-type="mediumblob"></column>

</property>

@Test

public void testBlob()
throws Exception{

/*News news = new News();

news.setAuthor("CC");

news.setContent("CONTENT");

news.setDate(new Date());

news.setDesc("DESC");

news.setTitle("CC");

InputStream stream = new FileInputStream("ASCII.jpg");

Blob image = Hibernate.getLobCreator(session).createBlob(stream, stream.available());

news.setImage(image);

session.save(news);*/

News news1 = (News)
session.get(News.class, 98313);

Blob image1 =
news1.getImage();

InputStream in =
image1.getBinaryStream();

System.out.println(in.available());

}

映射组成关系(了解)

建立域模型和关系数据模型有着不同的出发点:

域模型: 由程序代码组成,
通过细化持久化类的的粒度可提高代码的可重用性,
简化编程



在没有数据冗余的情况下,
应该尽可能减少表的数目, 简化表之间的参照关系,
以便提高数据的访问速度



类是两个,表是一个

Hibernate 把持久化类的属性分为两种:

1) 值(value)类型:
没有 OID,
不能被单独持久化,
生命周期依赖于所属的持久化类的对象的生命周期

2) 实体(entity)类型:
有 OID,
可以被单独持久化,
有独立的生命周期

显然无法直接用 property
映射 pay 属性

Hibernate 使用 <component>
元素来映射组成关系,
该元素表名 pay
属性是 Worker
类一个组成部分,
在 Hibernate
中称之为组件:

<!--
映射组成关系 -->

<component name="pay" class="Pay">

<parent name="worker"/>

<!--
指定组成关系的组件的属性 -->

<property name="monthlyPay" column="MONTHLY_PAY"></property>

<property name="yearPay" column="YEAR_PAY"></property>

<property name="vocationWithPay" column="VOCATION_WITH_PAY"></property>

</component>

Component属性

<component> 元素来映射组成关系

class:设定组成关系属性的类型,
此处表明 pay
属性为 Pay
类型

<parent> 元素指定组件属性所属的整体类

name: 整体类在组件类中的属性名



映射一对多关联关系

在领域模型中, 类与类之间最普遍的关系就是关联关系.

在 UML 中,
关联是有方向的.

以 Customer 和
Order 为例: 一个用户能发出多个订单,
而一个订单只能属于一个客户.
从 Order
到 Customer
的关联是多对一关联;
而从 Customer
到 Order
是一对多关联

单向关联



Order中有Customer的ID:单向多对一



Customer中有Order的集合:单向一对多

双向关联



Customer中有Order的集合,而Order中也有Customer的ID:双向关联



单向 n-1(many-to-one)

工程hibernateTest2的n21

单向 n-1 关联只需从
n 的一端可以访问
1 的一端

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



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



显然无法直接用 property
映射 customer 属性

Hibernate 使用 <many-to-one>
元素来映射多对一关联关系。

<!--

映射多对一的关联关系。 使用
many-to-one 来映射多对一的关联关系

name: 多这一端关联的一那一端的属性的名字

class: 一那一端的属性对应的类名

column: 一那一端在多的一端对应的数据表中的外键的名字

-->

<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>

<many-to-one> 元素来映射组成关系

name: 设定待映射的持久化类的属性的名字

column: 设定和持久化类的属性对应的表的外键

class:设定待映射的持久化类的属性的类型



双向 1-n

工程hibernateTest2的n21.both

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

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

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



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



set

<set> 元素来映射持久化类的 set
类型的属性

name: 设定待映射的持久化类的属性的



Key

<key> 元素设定与所关联的持久化类对应的表的外键

column: 指定关联表的外键名



one-to-many

<one-to-many> 元素设定集合属性中所关联的持久化类

class: 指定关联的持久化类的类名



<set> 元素的 inverse
属性

1,在hibernate中通过对
inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系. inverse = false
的为主动方,inverse = true
的为被动方,
由主动方负责维护关联关系

2,在没有设置 inverse=true
的情况下,父子两边都维护父子

关系

3,在 1-n
关系中,将 n
方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)

4,在 1-N
关系中,若将 1
方设为主控方

会额外多出 update
语句。

插入数据时无法同时插入外键列,因而无法为外键列添加非空约束

cascade 属性

在对象 – 关系映射文件中,
用于映射持久化类之间关联关系的元素, <set>, <many-to-one>
和 <one-to-one>
都有一个 cascade
属性,
它用于指定如何操纵与当前对象关联的其他对象.



<!-- 映射 1
对多的那个集合属性 -->

<!-- set:
映射 set
类型的属性, table: set
中的元素对应的记录放在哪一个数据表中.
该值需要和多对一的多的那个表的名字一致 -->

<!-- inverse:
指定由哪一方来维护关联关系.
通常设置为 true,
以指定由多的一端来维护关联关系 ,一的这一端放弃维护关系-->

<!-- cascade
设定级联操作. cascade="delete"级联删除

cascade="delete-orphan"
删除与当前对象没关联关系的对象

cascade="save-update"
级联保存

开发时不建议设定该属性.
建议使用手工的方式来处理 -->

<!-- order-by
在查询时对集合中的元素进行排序, order-by
中使用的是表的字段名,
而不是持久化类的属性名 -->

<set name="orders" table="ORDERS" inverse="true" cascade="save-update" order-by="ORDER_NAME
DESC">

<!--
执行多的表中的外键列的名字 -->

<key column="CUSTOMER_ID"></key>

<!--
指定映射类型 -->

<one-to-many class="Order"/>

</set>

在数据库中对集合排序

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

2,order-by
属性中还可以加入 SQL
函数



映射一对一关联关系

工程hibernateTest3的one2one

1 - 1

域模型



关系数据模型

1,按照外键映射



部门表中有经理表的ID,类似单向多对一。但是在部门表中的这个外键加上唯一约束。表示一个经理只能管一个部门:一对一

2,按照主键映射



基于外键映射的 1-1

对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素。为many-to-one元素增加unique=“true”
属性来表示为1-1关联

<!-- 使用 many-to-one
的方式来映射 1-1
关联关系 -->

<many-to-one name="mgr" class="com.yr.hibernate.one2one.foreign.Manager"

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

另一端需要使用one-to-one元素,该元素使用 property-ref
属性指定使用被关联实体主键以外的字段作为关联字段

<!--
映射 1-1 的关联关系:
在对应的数据表中已经有外键了,
当前持久化类使用 one-to-one
进行映射 -->

<!--

没有外键的一端需要使用one-to-one元素,该元素使用
property-ref 属性指定使用被关联实体主键以外的字段作为关联字段

-->

<one-to-one name="dept"

class="com.yr.hibernate.one2one.foreign.Department"

property-ref="mgr"></one-to-one>

不使用 property-ref
属性的 sql:关联查询的条件是不对的,用主键=主键了



使用 property-ref
属性的 sql



两边都使用外键映射的 1-1

如:部门表中有经理表的ID,并经理表中有部门表的ID,并都设置为外键。这样原则上讲也是1-1,但逻辑上是混乱的。

如:D1 对
M1

D2 对 M2

上面是正常的,

D1 对 M2

也是可以的。但是这样以来,M2就又对D1
又对D2,不是真正的1-1了



基于主键映射的 1-1

1,基于主键的映射策略:指一端的主键生成器使用
foreign 策略,表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键. <param>
子元素指定使用当前持久化类的哪个属性作为 “对方”

<id name="deptId" type="java.lang.Integer">

<column name="DEPT_ID" />

<!--
使用外键的方式来生成当前的主键 -->

<generator class="foreign">

<!-- property
属性指定使用当前持久化类的哪一个属性的主键作为外键 -->

<param name="property">mgr</param>

</generator>

</id>

2,采用foreign主键生成器策略的一端增加
one-to-one 元素映射关联属性,其one-to-one属性还应增加
constrained=“true” 属性;另一端增加one-to-one元素映射关联属性。

3,constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键

<!--

采用 foreign
主键生成器策略的一端增加 one-to-one
元素映射关联属性,

其 one-to-one
节点还应增加 constrained=true
属性,
以使当前的主键上添加外键约束

-->

<one-to-one name="mgr" class="Manager" constrained="true"></one-to-one>

映射多对多关联关系

工程hibernateTest3的n2n

单向 n-n

域模型



关系数据模型







n-n 的关联必须使用连接表

与 1-n 映射类似,必须为 set
集合元素添加 key
子元素,指定 CATEGORIES_ITEMS
表中参照 CATEGORIES
表的外键为 CATEGORIY_ID.
与 1-n
关联映射不同的是,建立 n-n
关联时,
集合中的元素使用 many-to-many. many-to-many
子元素的 class
属性指定 items
集合中存放的是 Item
对象,
column 属性指定 CATEGORIES_ITEMS
表中参照 ITEMS
表的外键为 ITEM_ID



双向 n-n



1,双向 n-n
关联需要两端都使用集合属性

2,双向n-n关联必须使用连接表

3,集合属性应增加 key
子元素用以映射外键列,
集合元素里还应增加many-to-many子元素关联实体类

4,在双向 n-n
关联的两边都需指定连接表的表名及外键列的列名.
两个集合元素 set
的 table
元素的值必须指定,而且必须相同。set元素的两个子元素:key
和 many-to-many
都必须指定 column
属性,其中,key
和 many-to-many
分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 key
与 many-to-many
的column属性交叉相同。也就是说,一边的set元素的key的
cloumn值为a,many-to-many
的 column
为b;则另一边的
set 元素的
key 的
column 值
b,many-to-many的
column 值为
a.

5,对于双向 n-n
关联,
必须把其中一端的 inverse
设置为 true,
否则两端都维护关联关系可能会造成主键冲突.

映射继承关系

对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。Hibernate
的继承映射可以理解持久化类之间的继承关系。例如:人和学生之间的关系。学生继承了人,可以认为学生是一个特殊的人,如果对人进行查询,学生的实例也将被得到。



Hibernate支持三种继承映射策略

1,使用 subclass
进行映射:将域模型中的每一个实体对象映射到一个独立的表中,也就是说不用在关系数据模型中考虑域模型中的继承关系和多态。

2,使用 joined-subclass
进行映射: 对于继承关系中的子类使用同一个表,这就需要在数据库表中增加额外的区分子类类型的字段。

3,使用 union-subclass
进行映射:域模型中的每个类映射到一个表,通过关系数据模型中的外键来描述表之间的继承关系。这也就相当于按照域模型的结构来建立数据库中的表,并通过外键来建立表之间的继承关系。

采用 subclass
元素的继承映射

工程hibernateTest3的subclass

1,采用 subclass
的继承映射可以实现对于继承关系中父类和子类使用同一张表

2,因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,使用该列来区分每行记录到低是哪个类的实例----这个列被称为辨别者列(discriminator).

3,在这种映射策略下,使用 subclass
来映射子类,使用 class
或 subclass
的 discriminator-value
属性指定辨别者列的值

4,所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类的实例在那些列其实并没有值,这将引起数据库完整性冲突,导致父类的实例无法保存到数据库中





采用 joined-subclass
元素的继承映射

工程hibernateTest3的joined

1,采用 joined-subclass
元素的继承映射可以实现每个子类一张表

2,采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。

3,在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用 key
元素映射共有主键。

4,子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中





采用 union-subclass
元素的继承映射

工程hibernateTest3的union

1. 采用 union-subclass
元素可以实现将每一个实体对象映射到一个独立的表中。

2. 子类增加的属性可以有非空约束 ---
即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。

3. 子类实例的数据仅保存在子类表中,
而在父类表中没有任何记录

4. 在这种映射策略下,子类表的字段会比父类表的映射字段要多,因为子类表的字段等于父类表的字段加子类增加属性的总和

5. 在这种映射策略下,既不需要使用鉴别者列,也无须使用 key
元素来映射共有主键.

6. 使用 union-subclass
映射策略是不可使用 identity 的主键生成策略,
因为同一类继承层次中所有实体类都需要使用同一个主键种子,
即多个持久化实体对应的记录的主键应该是连续的.
受此影响,
也不该使用 native
主键生成策略,
因为 native
会根据数据库来选择使用 identity
或 sequence.





三种继承映射方式的比较



Hibernate 检索策略

工程hibernateTest3的strategy

检索数据时的 2 个问题:

1. 不浪费内存:当 Hibernate
从数据库中加载 Customer 对象时,
如果同时加载所有关联的 Order
对象,
而程序实际上仅仅需要访问 Customer
对象,
那么这些关联的 Order
对象就白白浪费了许多内存.

2. 更高的查询效率:发送尽可能少的 SQL
语句



类级别的检索策略

1. 类级别可选的检索策略包括立即检索和延迟检索,
默认为延迟检索

a) 立即检索: 立即加载检索方法指定的对象

b) 延迟检索: 延迟加载检索方法指定的对象。在使用具体的属性时,再进行加载

2. 类级别的检索策略可以通过 <class>
元素的 lazy
属性进行设置

3. 如果程序加载一个对象的目的是为了访问它的属性,
可以采取立即检索.

4. 如果程序加载一个持久化对象的目的是仅仅为了获得它的引用,
可以采用延迟检索。注意出现懒加载异常!

5. 无论 <class>
元素的 lazy
属性是 true
还是 false, Session
的 get()
方法及 Query
的 list()
方法在类级别总是使用立即检索策略。只对load()有效

6. 若 <class>
元素的 lazy 属性为
true 或取默认值, Session
的 load()
方法不会执行查询数据表的 SELECT
语句,
仅返回代理类对象的实例,
该代理类实例有如下特征:

a) 由 Hibernate
在运行时采用 CGLIB 工具动态生成

b) Hibernate 创建代理类实例时,
仅初始化其 OID
属性

c) 在应用程序第一次访问代理类实例的非 OID
属性时, Hibernate 会初始化代理类实例

一对多和多对多的检索策略

1. 在映射文件中, 用
<set> 元素来配置一对多关联及多对多关联关系. <set>
元素有 lazy
和 fetch
属性

a) lazy:
主要决定 orders 集合被初始化的时机. 即到底是在加载 Customer
对象时就被初始化,
还是在程序访问 orders
集合时被初始化

b) fetch:
取值为 “select” 或 “subselect” 时,
决定初始化 orders
的查询语句的形式; 若取值为”join”,
则决定 orders
集合被初始化的时机

c) 若把 fetch
设置为 “join”, lazy
属性将被忽略

d) <set> 元素的 batch-size
属性:用来为延迟检索策略或立即检索策略设定批量检索的数量.
批量检索能减少 SELECT
语句的数目,
提高延迟检索或立即检索的运行性能.

<set> 元素的 lazy
和 fetch
属性



延迟检索和增强延迟检索

1. 在延迟检索(lazy
属性值为 true) 集合属性时, Hibernate
在以下情况下初始化集合代理类实例

a) 应用程序第一次访问集合属性: iterator(), size(), isEmpty(), contains()
等方法

b) 通过 Hibernate.initialize()
静态方法显式初始化

2. 增强延迟检索(lazy
属性为 extra):
与 lazy=“true” 类似.
主要区别是增强延迟检索策略能进一步延迟 Customer
对象的 orders
集合代理实例的初始化时机:

a) 当程序第一次访问 orders
属性的 iterator() 方法时,
会导致 orders
集合代理类实例的初始化

b) 当程序第一次访问 order
属性的 size(), contains() 和
isEmpty() 方法时, Hibernate
不会初始化 orders
集合类的实例,
仅通过特定的 select
语句查询必要的信息,
不会检索所有的 Order
对象

<set> 元素的 batch-size
属性

<set> 元素有一个 batch-size
属性,
用来为延迟检索策略或立即检索策略设定批量检索的数量.
批量检索能减少 SELECT
语句的数目,
提高延迟检索或立即检索的运行性能.

用带子查询的 select
语句整批量初始化 orders 集合(fetch
属性为 “subselect”)

1. <set> 元素的 fetch
属性:
取值为 “select” 或 “subselect” 时,
决定初始化 orders
的查询语句的形式; 若取值为”join”,
则决定 orders
集合被初始化的时机.默认值为
select

2. 当 fetch 属性为 “subselect” 时

a) 假定 Session
缓存中有 n 个
orders 集合代理类实例没有被初始化, Hibernate
能够通过带子查询的 select
语句,
来批量初始化 n
个 orders
集合代理类实例

b) batch-size
属性将被忽略

c) 子查询中的 select
语句为查询 CUSTOMERS 表
OID 的
SELECT 语句

迫切左外连接检索(fetch
属性值设为 “join”)

1. <set> 元素的 fetch
属性:
取值为 “select” 或 “subselect” 时,
决定初始化 orders
的查询语句的形式; 若取值为”join”,
则决定 orders
集合被初始化的时机.默认值为
select

2. 当 fetch 属性为 “join” 时:

a) 检索 Customer
对象时, 会采用迫切左外连接(通过左外连接加载与检索指定的对象关联的对象)策略来检索所有关联的
Order 对象

b) lazy
属性将被忽略

c) Query
的list() 方法会忽略映射文件中配置的迫切左外连接检索策略,
而依旧采用延迟加载策略

多对一和一对一关联的检索策略

1. 和 <set> 一样, <many-to-one>
元素也有一个 lazy
属性和 fetch
属性.



a) 若 fetch
属性设为 join,
那么 lazy
属性被忽略

b) 迫切左外连接检索策略的优点在于比立即检索策略使用的 SELECT
语句更少.

c) 无代理延迟检索需要增强持久化类的字节码才能实现

2. Query
的 list 方法会忽略映射文件配置的迫切左外连接检索策略,
而采用延迟检索策略

3. 如果在关联级别使用了延迟加载或立即加载检索策略,
可以设定批量检索的大小,
以帮助提高延迟检索或立即检索的运行性能.

4. Hibernate 允许在应用程序中覆盖映射文件中设定的检索策略.

检索策略小结

1. 类级别和关联级别可选的检索策略及默认的检索策略



2. 3 种检索策略的运行机制



3. 映射文件中用于设定检索策略的几个属性



4. 比较 Hibernate
的三种检索策略



Hibernate 检索方式

工程hibernateTest4

1. Hibernate 提供了以下几种检索对象的方式

a) 导航对象图检索方式: 根据已经加载的对象导航到其他对象

b) OID
检索方式: 按照对象的 OID
来检索对象(指的是session的get和load方式)

c) HQL 检索方式:
使用面向对象的 HQL
查询语言

d) QBC 检索方式:
使用 QBC(Query By Criteria) API
来检索对象.
这种 API
封装了基于字符串形式的查询语句,
提供了更加面向对象的查询接口.

e) 本地 SQL 检索方式:
使用本地数据库的 SQL
查询语句

HQL 检索方式

1. HQL(Hibernate Query Language) 是面向对象的查询语言,
它和 SQL
查询语言有些相似.
在 Hibernate
提供的各种检索方式中, HQL
是使用最广的一种检索方式.
它有如下功能

a) 在查询语句中设定各种查询条件

b) 支持投影查询, 即仅检索出对象的部分属性

c) 支持分页查询

d) 支持连接查询

e) 支持分组查询, 允许使用
HAVING 和
GROUP BY 关键字

f) 提供内置聚集函数,
如 sum(), min() 和
max()等

g) 支持子查询

h) 支持动态绑定参数

i) 能够调用 用户定义的 SQL
函数或标准的 SQL
函数

2. HQL 检索方式包括以下步骤:

a) 通过 Session
的 createQuery() 方法创建一个
Query 对象,
它包括一个 HQL
查询语句. HQL
查询语句中可以包含命名参数

b) 动态绑定参数

c) 调用 Query 相关方法执行查询语句.

3. Qurey
接口支持方法链编程风格, 它的 setXxx()
方法返回自身实例,
而不是 void
类型,那么可以继续再点setXxx()继续进行赋值.

4. HQL vs SQL

a) HQL 查询语句是面向对象的, Hibernate
负责解析 HQL
查询语句,
然后根据对象-关系映射文件中的映射信息,
把 HQL
查询语句翻译成相应的 SQL
语句. HQL
查询语句中的主体是域模型中的类及类的属性

b) SQL 查询语句是与关系数据库绑定在一起的. SQL
查询语句中的主体是数据库表及表的字段

5. 绑定参数

a) Hibernate 的参数绑定机制依赖于 JDBC API
中的 PreparedStatement
的预定义 SQL
语句功能

b) HQL 的参数绑定由两种形式

i. 按参数名字绑定:
在 HQL
查询语句中定义命名参数,
命名参数以 “:” 开头

ii. 按参数位置绑定:
在 HQL
查询语句中用 “?” 来定义参数位置

c) 相关方法

i. setEntity(): 把参数与一个持久化类绑定

ii. setParameter(): 绑定任意类型的参数.
该方法的第三个参数显式指定 Hibernate
映射类型

6. HQL 采用 ORDER BY
关键字对查询结果排序

7. 分页查询

a) setFirstResult(int firstResult): 设定从哪一个对象开始检索,
参数 firstResult
表示这个对象在查询结果中的索引位置,
索引位置的起始值为 0.
默认情况下, Query
从查询结果中的第一个对象开始检索

b) setMaxResults(int maxResults): 设定一次最多检索出的对象的数目.
在默认情况下, Query
和 Criteria
接口检索出查询结果中所有的对象

8. 在映射文件中定义命名查询语句

a) Hibernate 允许在映射文件中定义字符串形式的查询语句.

b) <query> 元素用于定义一个 HQL
查询语句,
它和 <class>
元素并列.



c) 在程序中通过 Session
的 getNamedQuery() 方法获取查询语句对应的
Query 对象.

投影查询

1. 投影查询: 查询结果仅包含实体的部分属性.
通过 SELECT
关键字实现.

2. Query 的 list()
方法返回的集合中包含的是数组类型的元素,
每个对象数组代表查询结果的一条记录

3. 可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录,
使程序代码能完全运用面向对象的语义来访问查询结果集.

4. 可以通过 DISTINCT
关键字来保证查询结果不会返回重复元素

5. 报表查询用于对数据分组和统计,
与 SQL 一样, HQL
利用 GROUP BY
关键字对数据分组,
用 HAVING
关键字对分组数据设定约束条件.

6. 在 HQL 查询语句中可以调用以下聚集函数

a) count()

b) min()

c) max()

d) sum()

e) avg()

HQL (迫切)左外连接

1. 迫切左外连接

a) LEFT JOIN FETCH
关键字表示迫切左外连接检索策略.

b) list() 方法返回的集合中存放实体对象的引用,
每个 Department
对象关联的 Employee 集合都被初始化,
存放所有关联的 Employee
的实体对象.

c) 查询结果中可能会包含重复元素,
可以通过一个 HashSet 来过滤重复元素

2. 左外连接

a) LEFT JOIN 关键字表示左外连接查询.

b) list()
方法返回的集合中存放的是对象数组类型

c) 根据配置文件来决定 Employee
集合的检索策略.

d) 如果希望 list()
方法返回的集合中仅包含 Department 对象,
可以在HQL
查询语句中使用 SELECT
关键字

3. 迫切内连接

a) INNER JOIN FETCH
关键字表示迫切内连接,
也可以省略 INNER
关键字

b) list()
方法返回的集合中存放 Department 对象的引用,
每个 Department
对象的 Employee
集合都被初始化,
存放所有关联的 Employee
对象

4. 内连接

a) INNER JOIN
关键字表示内连接, 也可以省略
INNER 关键字

b) list()
方法的集合中存放的每个元素对应查询结果的一条记录, 每个元素都是对象数组类型

c) 如果希望 list()
方法的返回的集合仅包含 Department 对象,
可以在 HQL
查询语句中使用 SELECT
关键字

关联级别运行时的检索策略

1. 如果在 HQL 中没有显式指定检索策略,
将使用映射文件配置的检索策略.

2. HQL 会忽略映射文件中设置的迫切左外连接检索策略,
如果希望 HQL
采用迫切左外连接策略,
就必须在 HQL
查询语句中显式的指定它

3. 若在 HQL 代码中显式指定了检索策略,
就会覆盖映射文件中配置的检索策略

QBC 检索和本地 SQL
检索

1. QBC 查询就是通过使用 Hibernate
提供的 Query By Criteria API
来查询对象,这种 API
封装了 SQL
语句的动态拼装,对查询提供了更加面向对象的功能接口

2. 本地SQL查询来完善HQL不能涵盖所有的查询特性

Hibernate 二级缓存

Hibernate 缓存

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

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

a) 第一级别的缓存是 Session
级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate
管理的

b) 第二级别的缓存是 SessionFactory
级别的缓存,它是属于进程范围的缓存

SessionFactory 级别的缓存

1. SessionFactory 的缓存可以分为两类

a) 内置缓存: Hibernate
自带的, 不可卸载.
通常在 Hibernate
的初始化阶段, Hibernate
会把映射元数据和预定义的 SQL
语句放到 SessionFactory
的缓存中,
映射元数据是映射文件中数据(.hbm.xml
文件中的数据)的复制.
该内置缓存是只读的.

b) 外置缓存(二级缓存):
一个可配置的缓存插件.
在默认情况下, SessionFactory
不会启用这个缓存插件.
外置缓存中的数据是数据库数据的复制,
外置缓存的物理介质可以是内存或硬盘

使用 Hibernate
的二级缓存

1. 适合放入二级缓存中的数据:

a) 很少被修改

b) 不是很重要的数据,
允许出现偶尔的并发问题

2. 不适合放入二级缓存中的数据:

a) 经常被修改

b) 财务数据, 绝对不允许出现并发问题

c) 与其他应用程序共享的数据

Hibernate 二级缓存的架构



二级缓存的并发访问策略

1. 两个并发的事务同时访问持久层的缓存的相同数据时,
也有可能出现各类并发问题.

2. 二级缓存可以设定以下 4
种类型的并发访问策略, 每一种访问策略对应一种事务隔离级别

a) 非严格读写(Nonstrict-read-write):
不保证缓存与数据库中数据的一致性. 提供
Read Uncommited 事务隔离级别,
对于极少被修改,
而且允许脏读的数据,
可以采用这种策略

b) 读写型(Read-write):
提供 Read Commited 数据隔离级别.对于经常读但是很少被修改的数据,
可以采用这种隔离类型,
因为它可以防止脏读

c) 事务型(Transactional):
仅在受管理环境下适用. 它提供了
Repeatable Read 事务隔离级别.
对于经常读但是很少被修改的数据,
可以采用这种隔离类型,
因为它可以防止脏读和不可重复读

d) 只读型(Read-Only):提供
Serializable 数据隔离级别,
对于从来不会被修改的数据,
可以采用这种访问策略

管理 Hibernate
的二级缓存

1. Hibernate 的二级缓存是进程或集群范围内的缓存

2. 二级缓存是可配置的的插件, Hibernate
允许选用以下类型的缓存插件

a) EHCache: 可作为进程范围内的缓存,
存放数据的物理介质可以使内存或硬盘,
对 Hibernate
的查询缓存提供了支持

b) OpenSymphony OSCache:可作为进程范围内的缓存,
存放数据的物理介质可以使内存或硬盘,
提供了丰富的缓存数据过期策略,
对 Hibernate
的查询缓存提供了支持

c) SwarmCache: 可作为集群范围内的缓存,
但不支持 Hibernate
的查询缓存

d) JBossCache:可作为集群范围内的缓存,
支持 Hibernate
的查询缓存



配置进程范围内的二级缓存

1. 配置进程范围内的二级缓存的步骤

a) 选择合适的缓存插件: EHCache(jar
包和 配置文件), 并编译器配置文件

b) 在 Hibernate
的配置文件中启用二级缓存并指定和 EHCache 对应的缓存适配器

c) 选择需要使用二级缓存的持久化类,
设置它的二级缓存的并发访问策略

i. <class> 元素的 cache
子元素表明 Hibernate
会缓存对象的简单属性,
但不会缓存集合属性,
若希望缓存集合属性中的元素,
必须在 <set>
元素中加入 <cache>
子元素

ii. 在 hibernate
配置文件中通过 <class-cache/> 节点配置使用缓存

Ehcache.xml

1. <diskStore>: 指定一个目录:当 EHCache
把数据写到硬盘上时,
将把数据写到这个目录下.

2. <defaultCache>: 设置缓存的默认数据过期策略

3. <cache> 设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域

4. 缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>

5. Hibernate在不同的缓存区域保存不同的类/集合。

a) 对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer

b) 对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders

6. cache 元素的属性

a) name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字

b) maxInMemory:设置基于内存的缓存中可存放的对象最大数目

c) eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds
和 timeToLiveSeconds属性;
默认值是false

d) timeToIdleSeconds:设置对象空闲最长时间,以秒为单位,
超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。

e) timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。

如果此值为0,表示对象可以无限期地存在于缓存中.
该属性值必须大于或等于 timeToIdleSeconds
属性值

f) overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中

查询缓存

1. 对于经常使用的查询语句,
如果启用了查询缓存, 当第一次执行查询语句时, Hibernate
会把查询结果存放在查询缓存中.
以后再次执行该查询语句时,
只需从缓存中获得查询结果,
从而提高查询性能

2. 查询缓存使用于如下场合:

a) 应用程序运行时经常使用查询语句

b) 很少对与查询语句检索到的数据进行插入,
删除和更新操作

3. 启用查询缓存的步骤

a) 配置二级缓存, 因为查询缓存依赖于二级缓存

b) 在 hibernate
配置文件中启用查询缓存

c) 对于希望启用查询缓存的查询语句,
调用 Query 的
setCacheable() 方法

时间戳缓存区域

1. 时间戳缓存区域存放了对于查询结果相关的表进行插入,
更新或删除操作的时间戳. Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期,
其运行过程如下

a) T1 时刻执行查询操作,
把查询结果存放在 QueryCache
区域,
记录该区域的时间戳为 T1

b) T2 时刻对查询结果相关的表进行更新操作, Hibernate
把 T2
时刻存放在 UpdateTimestampCache
区域.

c) T3 时刻执行查询结果前,
先比较 QueryCache
区域的时间戳和 UpdateTimestampCache
区域的时间戳,
若 T2 >T1,
那么就丢弃原先存放在 QueryCache
区域的查询结果,
重新到数据库中查询数据,
再把结果存放到 QueryCache
区域;
若 T2 < T1,
直接从 QueryCache
中获得查询结果

Query 接口的 iterate()
方法

1. Query 接口的 iterator()
方法

a) 同 list() 一样也能执行查询操作

b) list() 方法执行的 SQL
语句包含实体类对应的数据表的所有字段

c) Iterator() 方法执行的SQL
语句中仅包含实体类对应的数据表的 ID
字段

d) 当遍历访问结果集时,
该方法先到 Session
缓存及二级缓存中查看是否存在特定 OID
的对象,
如果存在,
就直接返回该对象,
如果不存在该对象就通过相应的 SQL Select
语句到数据库中加载特定的实体对象

2. 大多数情况下, 应考虑使用
list() 方法执行查询操作. iterator()
方法仅在满足以下条件的场合,
可以稍微提高查询性能:

a) 要查询的数据表中包含大量字段

b) 启用了二级缓存, 且二级缓存中可能已经包含了待查询的对象

管理 Session

1. Hibernate 自身提供了三种管理 Session
对象的方法

a) Session
对象的生命周期与本地线程绑定

b) Session 对象的生命周期与 JTA
事务绑定

c) Hibernate 委托程序管理 Session
对象的生命周期

2. 在 Hibernate
的配置文件中, hibernate.current_session_context_class
属性用于指定 Session
管理方式,
可选值包括

a) thread: Session
对象的生命周期与本地线程绑定

b) jta*: Session 对象的生命周期与 JTA
事务绑定

c) managed: Hibernate 委托程序来管理 Session
对象的生命周期

Session 对象的生命周期与本地线程绑定

1. 如果把 Hibernate
配置文件的 hibernate.current_session_context_class
属性值设为 thread, Hibernate
就会按照与本地线程绑定的方式来管理 Session

2. Hibernate 按一下规则把 Session
与本地线程绑定

a) 当一个线程(threadA)第一次调用
SessionFactory 对象的
getCurrentSession() 方法时,
该方法会创建一个新的 Session(sessionA)
对象,
把该对象与 threadA
绑定,
并将 sessionA
返回

b) 当 threadA
再次调用 SessionFactory 对象的
getCurrentSession() 方法时,
该方法将返回 sessionA
对象

c) 当 threadA
提交 sessionA 对象关联的事务时, Hibernate
会自动flush sessionA
对象的缓存,
然后提交事务,
关闭 sessionA
对象.
当 threadA
撤销 sessionA
对象关联的事务时,
也会自动关闭 sessionA
对象

d) 若 threadA
再次调用 SessionFactory 对象的
getCurrentSession() 方法时,
该方法会又创建一个新的 Session(sessionB)
对象,
把该对象与 threadA
绑定,
并将 sessionB
返回

批量处理数据

1. 批量处理数据是指在一个事务中处理大量数据.

2. 在应用层进行批量操作,
主要有以下方式:

a) 通过 Session

b) 通过 HQL

c) 通过 StatelessSession

d) 通过 JDBC API

通过 Session
来进行批量操作

1. Session 的 save()
及 update()
方法都会把处理的对象存放在自己的缓存中.
如果通过一个 Session
对象来处理大量持久化对象,
应该及时从缓存中清空已经处理完毕并且不会再访问的对象.
具体的做法是在处理完一个对象或小批量对象后,
立即调用 flush()
方法刷新缓存,
然后在调用 clear()
方法清空缓存

2. 通过 Session
来进行处理操作会受到以下约束

a) 需要在 Hibernate
配置文件中设置 JDBC 单次批量处理的数目,
应保证每次向数据库发送的批量的 SQL
语句数目与 batch_size
属性一致

b) 若对象采用 “identity” 标识符生成器,
则 Hibernate
无法在 JDBC
层进行批量插入操作

c) 进行批量操作时, 建议关闭
Hibernate 的二级缓存

3. 批量插入数据



4. 批量更新: 在进行批量更新时,
如果一下子把所有对象都加载到 Session
缓存,
然后再缓存中一一更新,
显然是不可取的

5. 使用可滚动的结果集 org.hibernate.ScrollableResults,
该对象中实际上并不包含任何对象,
只包含用于在线定位记录的游标. 只有当程序遍历访问 ScrollableResults
对象的特定元素时,
它才会到数据库中加载相应的对象.

6. org.hibernate.ScrollableResults 对象由 Query
的 scroll
方法返回



通过 HQL
来进行批量操作

注意: HQL 只支持
INSERT INTO …
SELECT 形式的插入语句,
但不支持 INSERT INTO
… VALUES
形式的插入语句.
所以使用 HQL
不能进行批量插入操作.

通过StatelessSession来进行批量操作

1. 从形式上看,StatelessSession与session的用法类似。StatelessSession与session相比,有以下区别

a) StatelessSession没有缓存,通过StatelessSession来加载、保存或更新后的对象处于游离状态。

b) StatelessSession不会与Hibernate的第二级缓存交互。

c) 当调用StatelessSession的save()、update()或delete()方法时,这些方法会立即执行相应的SQL语句,而不会仅计划执行一条SQL语句

d) StatelessSession不会进行脏检查,因此修改了Customer对象属性后,还需要调用StatelessSession的update()方法来更新数据库中数据。

e) StatelessSession不会对关联的对象进行任何级联操作。

f) 通过同一个StatelessSession对象两次加载OID为1的Customer对象,得到的两个对象内存地址不同。

g) StatelessSession所做的操作可以被Interceptor拦截器捕获到,但是会被Hibernate的事件处理系统忽略掉。

注意 : 如果想下载相关文档,可以去我的博客资源里面下载呢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: