hibernate4.1.1 从入门到总结

来源:互联网 发布:无间道风云知乎 编辑:程序博客网 时间:2024/06/10 19:19

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/

marseclipse版本名称

 

准备 Hibernate 环境

工程hibernateTest2helloworld

导入 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): HibernateJavaBeans风格的属性实行持久化。

使用非 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可以作为 Newsdesc属性值

 

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 操纵对象

工程hibernateTest2entities

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 缓存

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

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

    显式调用 Session flush() 方法

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

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

flush 缓存的例外情况:如果对象使用 native生成器生成 OID,那么当调用 Sessionsave()方法保存对象时,会立即执行向数据库插入该实体的 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请求,

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

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

 */

 //session.flush();

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

 /*

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

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

 * 1)执行HQLQBC查询时会执行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方法时,会执行selectupdate操作为得的保存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 的默认时间点,可以通过 SessionsetFlushMode()方法显式设定 flush的时间点

 

 

 

数据库的隔离级别

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

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

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

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

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

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

 

 

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

 

Oracle 支持的 2种事务隔离级别:READ COMMITEDSERIALIZABLE.

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()方法时,会先执行 sessionflush方法.

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

 * 变为持久化对象

 *

 * 需要注意的:

 * 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()方法时,会先执行 sessionflush方法

 */

/*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 对象的 OIDnull

映射文件中为 <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 属性:SessionupdatesaveOrUpdate()方法更新一个游离对象时,会先执行 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 数据库连接池属性

导入c3p0jar:

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()方法设定 JDBCStatement读取数据的时候每次从数据库中取出的记录条数。

例如一次查询1万条记录,对于OracleJDBC驱动来说,是不会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的时候,使用HibernateOracle数据库删除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, intshort类型, 如果把 OID 定义为 byte 类型, 在运行时会抛出异常

 

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

适用范围:

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

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

 

 

identity 标识符生成器

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

适用范围:

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

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

 

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

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

 


 

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

适用范围:

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

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

 

 

 

hilo 标识符生成器

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

 

 

 

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

适用范围:

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

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

 

 

 

native 标识符生成器

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

适用范围:

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

OID 必须为 long, intshort类型, 如果把 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.Datejava.util.Calendar.此外, JDBC API 中还提供了 3 个扩展了 java.util.Date类的子类: java.sql.Date, java.sql.Timejava.sql.Timestamp,这三个类分别和标准 SQL类型中的 DATE, TIMETIMESTAMP类型对应

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

 

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

            <column name="DATE" />

        </property>

/**

 *  java.util.Date 类的子类: java.sql.Date, java.sql.Timejava.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, timetimestamp.此时必须根据对应的数据表的字段的 SQL类型, 来确定 Hibernate映射类型.如果字段为 DATE类型, 那么 Hibernate映射类型为 date;如果字段为 TIME类型, 那么 Hibernate映射类型为 time;如果字段为 TIMESTATMP类型, 那么 Hibernate映射类型为 timestamp.

 

 

Java 大对象类型的 Hiberante映射

 

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

 

Mysql 不支持标准 SQLCLOB类型, Mysql , TEXT, MEDIUMTEXT LONGTEXT 类型来表示长度操作255 的长文本数据

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

实际上在 Java 应用程序中处理长度超过255 的字符串,使用 java.lang.Stringjava.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 为例: 一个用户能发出多个订单,而一个订单只能属于一个客户.OrderCustomer的关联是多对一关联;而从 CustomerOrder是一对多关联

单向关联

 

Order中有CustomerID:单向多对一

 

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

双向关联

 

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

 

 

单向 n-1many-to-one

工程hibernateTest2n21

单向 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

工程hibernateTest2n21.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子句进行排序

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

 

映射一对一关联关系

工程hibernateTest3one2one

 

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,并都设置为外键。这样原则上讲也是11,但逻辑上是混乱的。

如:D1 M1  

D2 M2

上面是正常的,

D1 M2

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

 

 

基于主键映射的 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元素映射关联属性。

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

<!--  

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

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

-->

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

        

 

 

映射多对多关联关系

 

工程hibernateTest3n2n

单向 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关联的两边都需指定连接表的表名及外键列的列名.两个集合元素 settable元素的值必须指定,而且必须相同。set元素的两个子元素:keymany-to-many都必须指定 column属性,其中,keymany-to-many分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 keymany-to-manycolumn属性交叉相同。也就是说,一边的set元素的keycloumn值为a,many-to-manycolumnb;则另一边的set 元素的key column b,many-to-manycolumn 值为a.  

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

 

 

映射继承关系

 

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

 

 

Hibernate支持三种继承映射策略

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

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

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

 

采用 subclass 元素的继承映射

工程hibernateTest3subclass

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

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

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

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

 

 

 

 

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

工程hibernateTest3joined

 

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

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

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

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

 

 

 

 

 

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

工程hibernateTest3union

 

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

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

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

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

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

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

 

 

 

三种继承映射方式的比较

 

 

Hibernate 检索策略

工程hibernateTest3strategy

检索数据时的 2 个问题:

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

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

 

 

类级别的检索策略

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<set> 元素的 lazyfetch属性

 

 

延迟检索和增强延迟检索

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来检索对象(指的是sessiongetload方式)

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): 设定一次最多检索出的对象的数目.在默认情况下, QueryCriteria接口检索出查询结果中所有的对象

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表示永不过期,此时将忽略timeToIdleSecondstimeToLiveSeconds属性;默认值是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 时刻对查询结果相关的表进行更新操作, HibernateT2时刻存放在 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 对象由 Queryscroll方法返回

     

 

通过 HQL 来进行批量操作

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

 

通过StatelessSession来进行批量操作

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

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

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

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

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

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

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

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

 




注意  :  如果想下载相关文档,可以去我的博客资源里面下载呢!