Hibernate快速入门

Hibernate框架
Hibernate框架
Hibernate快速入门
Hibernate基础语义
Hibernate基础配置
Hibernate O/R映射
Hibernate数据关联
Hibernate数据检索
HQL实用技术
Hibernate高级特性
Hibernate快速入门
OR映射问题一直是程序开发中最为复杂的问题,一直没有得到很好的解决
目前已知的OR解决方案有:
1、实体EJB,主要是指CMP方式的实体EJB
2、JDO,Java Data Object试图解决EJB存在的问题
3、TopLink,WebGain公司的产品,现在被Oracle收购
4、Hibernate
Hibernate快速入门
Hibernate与Struts一样,也是开源项目,提供了一种实现OR映射的框架,管理该项目的组织是JBoss
Hiberate诞生于2001年11月,是Gavin King 业余时间的个人作品,后被JBoss采纳,成为其主力推广产品
Gavin King目前已经成为
 EJB3.0专家委员,JBoss
 的核心成员,生于74年,
    澳大利亚墨尔本人。
Hibernate快速入门
Gavin King最早就职于澳大利亚悉尼一家IT公司Cirrus Technologies ,在决定开发Hibernate时,Gavin King对SQL一无所知。
2003年9月,Gavin King和hibernate的一些开发者加入了JBoss,专门从事Hibernate的开发
在Sun公司制订EJB3.0时,他成为了实际上的领导人
Gavin King曾经在网站上声明如果有人能使用Java代码写出比Hibernate效率高的片段来,他愿意为每一段代码付100美元
Hibernate快速入门
Hibernate的核心思想是将数据库中的数据映射到JavaBean对象上,并可直接将对象属性上的数据写到数据库相应字段上。
Hibernate中称用于存储数据的JavaBean对象为POJOs(Plain Old Java Objects,也称为Plain Ordinary Java Objects)
要解决的重要问题是JavaBean对象的每一个属性分别与哪一个表的哪一个字段相对应
Hibernate快速入门
如定义如下对象:
public class User {
   private String id;
   private String userId;
   private String password;
   public String getId() {
  return id;
   }
   public void setId(String id) {
  this.id = id;
   }
   …………….
}
Hibernate快速入门
Hibernate核心接口是Session,数据库操作通过这个接口实现。
例如:
Configuration cfg = new Configuration();
SessionFactory factory = cfg.configure().buildSessionFactory();
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setUserId(“teacher”);
user.setPassword(“teacher”);
session.save(user);
tx.commit();
session.close();
其中,session是Hibernate中Session,以上代码等同insert语句。
Hibernate快速入门
要实现以上效果,必须要解决两个问题,一是要屏蔽不同数据库之间的差异,二是要对象与表的映射。
Hibernate是通过配置文件解决上面的问题,配置文件有两种类型,一种配置了数据库方面的信息称为Hibernate配置,另一种配置了对象与表的映射关系称为Hibernate映射
这两种配置均可以XML文件的形式配置
Hibernate快速入门
Hibernate配置:
<?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=”dialect”>org.hibernate.dialect.MySQLDialect</property>
        <property name=”show_sql”>true</property>
        <property name=”connection.datasource”>java:comp/env/jdbc/DataSource</property>
        <mapping resource=”user.xml”/>
    </session-factory>
</hibernate-configuration>

Hibernate快速入门
dialect:配置SQL方言,只要作用是体现数据库间SQL语言的差异
show_sql:运行期是否在控制台打印SQL语句
connection.datasource:Hibernate使用数据源的JNDI名
mapping:指定POJO的配置文件

Hibernate快速入门
Hibernate对象映射:
<?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>
    <class name=“cn.com.lhhc.hibernate.first.dao.User” table=”user”>
        <id name=”id” column=”id”>
            <generator class=”identity”/>
        </id>
        <property name=”userId” column=”userid”/>
        <property name=”password” column=”password”/>
    </class>
</hibernate-mapping>

Hibernate快速入门
class指定POJO与哪个表对应
id指定类中哪个属性为主键,与哪个字段相对应
property定义了类属性与字段间的对应关系

Hibernate快速入门
一个hibernate.cfg.xml文件与一个SessionFactory相对应,SessionFactory不是轻量级的,Hibernate建议在一个应用程序中只创建一个SessionFactory实例
Session对象是非线程安全的,因此在开发时必须保证一个线程只创建一个session
所以应该解决两个问题,一是SessionFactory在整个应用中只有一个,二是Session在每个线程中只有一个。Hibernate给出了一种解决方案的建议:
Hibernate快速入门
public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (HibernateException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }
    public static final ThreadLocal session = new ThreadLocal();
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
        }
        return s;
    }
    public static void closeSession() throws HibernateException {
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)  s.close();
    }
}
Hibernate快速入门
SessionFactory使用的是单例模式,保证在全局只存在一个实例
Session采用了线程局部变量(ThreadLocal)的办法,保证在一个线程中仅存在一个Session实例
以上仅对于Hibernate2,在Hibernate3中SessionFactory提供了getCurrentSession方法,保证了线程中Session的惟一性
Hibernate快速入门
getCurrentSession在第一次调用时创建新的实例,以后调用则返回已经实例化的Session。
与此同时,Hibernate还保证了在线程结束时,或事务提交时关闭Session
使用getCurrentSession需要在配置中加入
<property name=”current_session_context_class”>
  thread
</property>
Hibernate快速入门
代码也应修改为:
Session session = factory.getCurrentSession();
session.beginTransaction();
User user = new User();
user.setUserId(“teacher”);
user.setPassword(“teacher”);
session.save(user);
session.getTransaction().commit();
总结
Hibernate采用POJO表示数据对象:
对象属性与表中字段值相对应;
通常一个JavaBean类与一个表对应;
一个JavaBean对象与表中一行相对应。
JavaBean与表的映射关系是由XML文档定义的。
总结
Hibernate中的数据库操作由Session对象来完成,Session非线程安全,必须保证一个线程只有一个Session对象
Hibernate2中建议使用ThreadLocal对象来保存Session对象
Hibernate3则提供了getCurrentSession方法,但要对SessionFactory做相应配置
总结
Session对象由SessionFactory创建,SessionFactory由hibernate.cfg.xml文件来配置
一个SessionFactory与一个hibernate.cfg.xml相对应,具体地指定了使用哪种数据库等相关信息。
SessionFactory是非轻量级的,一个应用中最好只有一个SessionFactory对象,使用单例模式
Hibernate基础语义
Configuration
SessionFactory
Session
Configuration
Configuration类负责管理Hibernate的配置信息
Hibernate运行时需要获取一些底层实现的基本信息 ,这些属性可以在hibernate.cfg.xml中,或hibernate.properties中设定
Configuration
当调用 Configuration config=new Configuration().configure();Hibernate会自动在当前Classpath中搜寻hibernate.cfg.xml文件并加载到内存中
Configuration
Configuration类一般只有在获取SessionFactory时涉及,当SessionFactory实例创建后,由于配置信息已经由Hibernate绑定在返回的SessionFactory之中,因此一般情况下不再用Configuration
如果不使用默认的hibernate.cfg.xml,可以指定:
Configuration config=new Configuration().configure(new File(“c:\\sample\\myhibernate.xml”));
SessionFactory
SessionFactory负责得到Session对象。
  SessionFactory
sessionFactory=config.buildSessionFactory();

SessionFactory中保存了对应当前数据库配置的所有映射关系,同时也负责维护当前的二级数据缓存和Statement Pool。
Session
Session是Hibernate持久化操作的基础。
这里的Session与web层的HttpSession没有关系,Hibernate Session与Hibernate,相当于JDBC Connection相对于JDBC
Insert,delete,update可以通过session相对应save,delete,update方法完成

Session
查询比较复杂,通过主键查询可以通过session的get或load
带条件的查询,称为find,可以通过Query,Criteria。
Session中的createQuery,createCriteria方法得到Query,Criteria
Query和Criteria不同之处在于,Query面向HQL和Native SQL,而Criteria提供了面向对象的查询模式。

Hibernate基础配置
SessionFactory配置
事务管理
SessionFactory配置
数据库连接配置
 JDBC连接配置:dialect,driver_class,url,username,
   password
 JNDI连接配置:datasource,user,pwd,dialect
数据库连接池配置
 四种连接池:默认数据库连接池,C3P0,dbcp,Proxool,推荐dbcp
事务管理
数据库连接配置
 JDBC连接配置:dialect,driver_class,url,username,password
 JNDI连接配置:datasource,user,pwd,dialect
数据库连接池配置
 四种连接池:默认数据库连接池,C3P0,dbcp,Proxool,推荐dbcp
Hibernate O/R映射
Hibernate基本数据类型
实体映射
高级映射技术
复合主键
实体映射策略
O/R映射简介
Hibernate的核心思想是将数据库中的字段映射到JavaBean的属性上,因此明确了JavaBean属性与数据库映射关系的映射文件就显得十分重要
Hiberante中数据的增、删、改是围绕着Java对象展开的,而并非直接在数据库上进行,因此只有Java对象上的属性声明为持久化的才会对数据库造成影响
O/R映射简介
<?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>
    <class name=”dao.User” table=”user”>
        <id name=”id” column=”id”>
            <generator class=”identity”/>
        </id>
        <property name=”userId” column=”userid”/>
        <property name=”password” column=”password”/>
    </class>
</hibernate-mapping>
O/R映射简介
POJO类与表的映射是通过class完成的
标识属性与主键的映射是通过id及composite-id实现的,后者用于联合主键,同时还需要指定主键的生成器
属性与字段的映射是通过property元素完成的

Hibernate基本数据类型
在进行Hibernate实体属性关系定义时,需要提供属性的数据类型设定
通过这些类型定义,Hibernate即可完成Java数据类型到数据库特定数据类型的映射关系。
Hibernate基本数据类型
简单属性配置片断:
<property name=”age” type=”integer” column=”age”/>
  将integer类型的属性age映射到库表字段age
Hibernate提供 丰富的数据类型支持,其中包括了传统的Java数据类型,如String,integer,以及JDBC数据类型,如Clob,Blob等。除此之外,Hibernate还提供用户自定义数据类型支持。
Hibernate基本数据类型
Java原始类型:
Hibernate基本数据类型
大数据类型:各数据库之间由于实现机理差异,对于大数据存取方式难以通用
Hibernate基本数据类型
其他类型:
实体映射
实体映射技术作为类和表之间的联系纽带,在ORM中起着至关重要的作用。
对于Hibernate而言,映射关系更多体现在配置文件的维护过程中。
Hibernate选用XML作为映射配置文件的基础形式
实体映射
分三部分讲解
实体映射基础
高级映射技术
实体映射策略
实体映射
分三部分讲解
实体映射基础
介绍Hibernate中类/表映射、属性/字段映射的基本技术
高级映射技术
介绍自定义数据类型、复合主键、特殊字段(Blob/Clob)的相关映射技术。
实体映射策略
围绕实体映射中实体粒度、层次的设计思路进行探讨。对特殊情况下与实体逻辑结构,实体读写性能相关的一些通用设计策略进行介绍
实体映射基础
实体映射的核心内容,即实体类与数据库表之间的映射定义,
类表之间的映射主要包括3部分:
表名-类名映射,主键映射,字段映射
实体映射基础
类表映射:<class name=”com.TUser” table=”T_User”>
主键映射:<id name=”id” column=”id” type=”java.lang.Integer”><generator class=”native”></id>
Native表示主键生成方式由Hibernate处理,调用数据库本身的主键生成方式
属性/字段映射配置:<property name=”name” column=”name” type=”java.lang.String”/>
高级映射技术
Hibernate基本数据类型可以满足大部分应用需求。
对于特殊情况,出于设计上的统一性考虑,我们需要针对数据结构中可能重复出现的一些数据模式,引入自定义数据类型。
很少使用
高级映射技术
Hibernate中,通过composite-id节点对复合主键进行定义
对于复合主键而言,可以通过两种方式确定主键
基于实体类属性的复合主键
基于主键类的复合主键
Hibernate中有createBlob和createClob方法,创建Blob,Clob对象。其中Blob对象基于FileInputStream构建,Clob基于String构建
实体映射策略
对于Hibernate而言,所谓细粒度对象模型,主要是针对实体类设计的对象细分,对象的细分主要出于两个目的:
面向设计的粒度细分(通过对象细化,实现更加清晰的系统逻辑),
面向性能的粒度细分(针对业务逻辑,通过合理的细粒度对象,提高系统的能耗比(性能、资源消耗))
实体映射策略
面向设计的粒度细分
一张表可以对应多个实体类。
对于单表的对象细分,在Hibernate中可借助Component节点的定义完成。
Component与实体对象的根本差别,就在于Component没有标识(identity),它作为一个逻辑组成,完全从属于实体对象。

<class name=”” table=”T_USER”>
<id …></id>
<component name=”name” class=””>
<property …/>
<property …/>
</component>

</class>
实体映射策略
面向性能的粒度细分:
对于一些Clob,Blob字段,数据库读取代价较高,希望真正调用相应get方法时才真正从数据库中读取数据。Hibernate2中用继承完成。将高代价数据写在子类中。
在子类的xml中写:
 <class name=”” table=”” polymorphism=”explicit”>
实体层次设计
实体层次设计:继承关系是关系数据与面向对象数据结构之间的主要差异之一。如何在关系型数据库的基础之上,通过继承关系得到清晰合理的层次划分,是Hibernate实体层次设计中的一个关键问题。
Hibernate中支持3种类型的继承关系:
Table per concrete class(TPC)
表与类之间的独立一对一关系
Table per subclass(TPS)
每个子类对应一张子表,并与主类共享主表
Table per hierarchy(TPH)
表与类的一对多关系
继承-TPC
TPC情况下,每具体类对应一张表,不管是子类还是父类,只要不是抽象类均有一张表与之对应。
表与表之间不存在主外键对应关系,数据存在冗余
TPC情况下,主键不能使用identity生成器,并且必须保证子表中的字段名与父表中的字段名完全相同
继承-TPC
<class name=”Course” abstract=”true” >
    <id name=”id” column=”course_id” type=”string” length=”128″>
       <generator class=”uuid” />
    </id>
    <property name=”name” column=”course_name” />
    <property name=”period” column=”total_period” />
    <property name=”notes” />
    <union-subclass name=”PractiseCourse” table=”practise_course”>
       <property name=”projectName” column=”project_name” />
       <property name=”projectPeriod” column=”project_period” />
    </union-subclass>
    <union-subclass name=”CertificationCourse” table=”certification_course”>
       <property name=”certificationName” column=”cert_name” />
       <property name=”examTime” column=”exam_time”/>
    </union-subclass>
</class>
继承-TPC
在操作上,TPC与其它两种方式相同,也能够实现多态查询,多态插入操作
List gc = session.createQuery(“from Course”).list();
List pc = session.createQuery(“from PractiseCourse”).list();
List cc = session.createQuery(“from CertificationCourse”).list();
Iterator it = pc.iterator();
while(it.hasNext()) {
 Course c = (Course)it.next();
 System.out.println(c.getName());
}
加载时使用union关键字
继承-TPS
在TPH下,由于不同的子类包含有不同的字段值,因此在表中必然有空的字段,存储空间浪费比较严重
从数据库表设计出发,应该将那些非共性的字段单独建立表,共有数据存在另外一张表中。
这就要求每个类都对应一个单独的表,子类表维护一个到父类表的外键。
继承-TPS
继承-TPS
 <class name=”Course” table=”course” >
    <id name=”id” column=”course_id”>
       <generator class=”identity” />
    </id>
    <property name=”name” column=”course_name” />
    ……
    <joined-subclass name=”PractiseCourse” table=”practise_course”>
       <key column=”pcourse_id” />
       <property name=”projectName” column=”project_name” />
       <property name=”projectPeriod” column=”project_period” />
    </joined-subclass>
    <joined-subclass name=”CertificationCourse” table=”certification_course”>
       <key column=”ccourse_id” />
       <property name=”certificationName” column=”cert_name” />
       <property name=”examTime” column=”exam_time”/>
    </joined-subclass>
  </class>
继承-TPS
PractiseCourse pc = new PractiseCourse();
pc.setName(“web developer”);
pc.setPeriod(300);
pc.setProjectName(“Training System”);
pc.setProjectPeriod(100);
session.save(pc);
其使用方法与TPH相差无几,加载时使用外连接
继承-TPH
TPH下,一般只为父类创建一个表,在表中设置字段区分对象种类。
不同子类具有的不同属性保存在同一张表中,不具备的属性保存值为空。
以课程为例,包括普通课程、实习课程和认证考试课程,它们的关系如图:
继承-TPH
继承-TPH
<class name=”Course” table=”course” discriminator-value=”GC” >
    <id name=”id” column=”course_id”>
       <generator class=”identity” />
    </id>
    <discriminator column=”course_type” type=”string” />
    <property name=”name” column=”course_name” />
    <property name=”period” column=”total_period” />
    <property name=”notes” />
    <subclass name=”PractiseCourse” discriminator-value=”PC”>
       <property name=”projectName” column=”project_name” />
       <property name=”projectPeriod” column=”project_period” />
    </subclass>
    <subclass name=”CertificationCourse” discriminator-value=”CC”>
       <property name=”certificationName” column=”cert_name” />
       <property name=”examTime” column=”exam_time”/>
    </subclass>
</class>
继承-TPH
PractiseCourse pc = new PractiseCourse();
Course gc = new Course();
CertificationCourse cc = new CertificationCourse();
Course gcc = new CertificationCourse();
session.save(pc);
session.save(gc);
session.save(cc);
session.save(gcc);
继承-TPH
List gc = session.createQuery(“from Course”).list();
List pc = session.createQuery(“from PractiseCourse”).list();
List cc = session.createQuery(“from CertificationCourse”).list();
Iterator itg = gc.iterator();
Iterator itp = pc.iterator();
Iterator itc = cc.iterator();
while(itg.hasNext()) {
 Course c = (Course)itg.next();
 System.out.println(c.getName());
}
关联关系
在数据库设计中常用的一种建模方法就是实体关系模型
实体就是现实世界中独立存在的事物,例如学生、老师;电脑、用户等等,其在数据库的反映就是一张张关系表
实体包含了反映事物特征的属性,其在数据库中的反映就是表中的字段
关系是实体间的联系,如学生和老师之间是多对多的关系,电脑与用户是一对一的关系
实体间的关系并不是固定不变的,这完全取决于需求,比如,电脑与用户间的对应关系也可以是一对多
关联关系
关系主要有以下几种:
1、一对一关系
2、一对多关系
3、多对一关系
4、多对多关系
除多对多关系需要关系表来反映外,其余都是直接通过外键来体现的
关联关系的方向
关系是有方向的,单向的关系是指从实体A能得到与其相关联另外一个实体B,但从实体B不能得到实体A
例如,如果老师和课程之间是一对多的关系,并且通过一个老师能够知道他所讲的全部课程,但不能通过课程知道任课老师,这种关系就是单向的
双向的关系是从关系中的任意一个角色都能得到另一实体
Hibernate中定义关系的方法
Hibernate中定义关联关系的要点:
1、关联一方持有关联对象,通常是以属性的方式保存在对象中。
2、在映射文件中,使用one-to-one、one-to-many、many-to-one、many-to-many定义映射关系。
3、对于one-to-many、many-to-many需要使用集合持有。
一对一关系
一对一关系有三种实现方式:
一是通过外键方式实现,即当前对象持有关联对象的外键;
二是通过主键方式实现,即当前对象的主键即是主键也是外键,也就是说当前对象的主键和与之相关联对象的主键是相同的。
三是通过关系表实现,即关联双方都以外键的形式存储在关系表中,通过关系表反映一对一的关系,这种方式很少使用
一对一关系-外键方式
<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
 <many-to-one name=”address”
  column=”addressId”
  unique=”true”
  not-null=”true”/>
</class>
<class name=”Address”>
 <id name=”id” column=”addressId”>
  <generator class=”native”/>
 </id>
</class>
一对一关系-外键方式
在关联的另一端如果也要持有对象,应该写成:
<class name=”Address”>
 <id name=”id” column=”addressId”>
  <generator class=”native”/>
 </id>
    <one-to-one name=”owner” class=”Person” property-ref=”addressId”/>
</class>
property-ref定义参照对象的外键属性是什么
一对一关系-主键方式
<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
</class>
<class name=”Address”>
 <id name=”id” column=”personId”>
  <generator class=”foreign”>
   <param name=”property”>person</param>
  </generator>
 </id>
 <one-to-one name=”person” constrained=”true”/>
</class>
一对一关系-主键方式
如果定义双向一对一,则
<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
      <one-to-one name=”address”/>
</class>
<class name=”Address”>
 <id name=”id” column=”personId”>
  <generator class=”foreign”>
   <param name=”property”>person</param>
  </generator>
 </id>
 <one-to-one name=”person” constrained=”true”/>
</class>
多对一关系
多对一关系中,当前对象持有外键。通过持有外键找到与之对应的对象。因此,在多对一关系中,当前对象包含一个与之关联的对象,这个对象以属性的方式存在对象中。
例如,单元(Unit)和楼房(Buiding)之间就是多对一关系。一幢楼可以包括多个单元,单元只对应一幢楼

多对一关系
多对一关系

<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
 <many-to-one name=”address”
  column=”addressId”
  not-null=”true”/>
</class>
<class name=”Address”>
 <id name=”id” column=”addressId”>
  <generator class=”native”/>
 </id>
</class>

一对多关系
一对多关联在系统实现中非常常见,一对多关联分为
单向一对多
双向一对多。
单向一对多关系只需要在“一”方进行配置
双向一对多需要在双方配置。
一对多关系
一对多关系有两种方式实现:
一是通过外键方式实现,“一”的角色不持有外键,外键被多的一方持有。Hibernate不鼓励使用这种方式实现一对多关系。
二是通过关系表实现,外键被关系表持有,每一条对应关系都必须在关系表中注册。
一对多关系-外键方式
<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
 <set name=”addresses”>
  <key column=”personId”
   not-null=”true”/>
  <one-to-many class=”Address”/>
 </set>
</class>
<class name=”Address”>
 <id name=”id” column=”addressId”>
  <generator class=”native”/>
 </id>
</class>
一对多双向-外键方式
<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
 <set name=”addresses”>
  <key column=”personId”
   not-null=”true”/>
  <one-to-many class=”Address”/>
 </set>
</class>
<class name=”Address”>
 <id name=”id” column=”addressId”>
  <generator class=”native”/>
 </id>
       <many-to-one name=“person” column=“personId”/>
</class>
一对多关系-关系表
<class name=”Person”>
 <id name=”id” column=”personId”>
  <generator class=”native”/>
 </id>
 <set name=”addresses” table=”PersonAddress”>
  <key column=”personId”/>
  <many-to-many column=”addressId”
   unique=”true”
   class=”Address”/>
 </set>
</class>
<class name=”Address”>
 <id name=”id” column=”addressId”>
  <generator class=”native”/>
 </id>
</class>
一对多双向-关系表
<class name=”Person”>
  <id name=”id” column=”personId”>
    <generator class=”native”/>
  </id>
  <set name=”addresses”
         table=”PersonAddress”>
    <key column=”personId”/>
    <many-to-many column=”addressId”
     unique=”true”
    class=”Address”/>
  </set>
</class>
多对多关系
多对多只能通过关系表的形式实现,每一条关系都必须在关系表中注册。
关系双方都不持有外键,但均持有一个集合属性,代表与之关联的关系另一角色。

一对多关联
单向一对多:
对于one-to-many关联关系,可以采用java.util.Set,或者net.sf.hibernate.collection.Bag,类型的Collection,表现在XML映射文件中就是<set></set>或者<bag></bag>
单向一对多的实现相对比较简单,但是存在一个问题,由于是单向关联,为了保持关联关系,我们只能通过主控方对被动方进行级联更新。如果被关联方的关联字段为not null,当Hibernate创建或更新关联关系时,可能出现约束违例。
一对多关联
双向一对多关系的出现解决了这个问题。它除了避免约束违例和提高性能的好处之外,还有另外优点:由于建立了双向关联,可以在关联任意一方,访问关联另一方,提供了更丰富的灵活的控制手段
一对多关联
双向一对多关联:
双向一对多关联,实际上是一对多和多对一的组合。
多了Inverse=true的配置,控制方向反转
Inverse=false的一方为主控方
多对多关联
多对多关联比较特殊,与一对多,一对一关联不同,需要借助中间表完成多对多映射信息的保存。
由于采用中间表,所以性能欠佳,避免大量使用。根据情况,采取延迟加载机制避免开销。
Hibernate数据检索
Criteria Query
DetachedCriteria
HQL
Criteria Query
Criteria Query通过面向对象化的设计,将数据查询条件封装为一个对象。
简单来讲,Criteria Query可以看作是传统SQL的对象化表示
如:
Criteria criteria=session.createCriteria(Tuser.class);
Criteria.add(Expression.eq(“name”,”Erica”));
Criteria.add(Expression.eq(“sex”,new Integer(1)));
相当于:select * from t_user where name=’Erica’ and sex=1
DetachedCriteria Query
Hibernate2中,Criteria生命周期位于session生命周期中,也就是说,criteria对象很难重用,Hibernate3提供了一个新的实现:DetachedCriteria
DetachedCriteria可以脱离session实例独立存在。
DetachedCriteria
deCriteria=DetachedCriteria.forClass(Tuser.class);
DeCriteria.add(Expression.eq(“name”,”Erica”));
….
Criteria criteria=deCriteria.getExecutableCriteria(session);
HQL
Criteria作为一种对象化的查询封装模式,非常适合程序员。不过Hibernate在实现过程中,更加集中在HQL查询语言上,因此Criteria的功能时间还有欠缺。实际开发中,最常用的还是Hibernate官方推荐的查询封装模式:HQL
HQL和Criteria并不是非此即彼,相互孤立的技术,两者可以相辅相成,在系统开发中,可以适当搭配使用这两种技术。
HQL
HQL提供了更加丰富灵活的特性,涵盖了Criteria所有功能,提供了更强大的查询能力。
HQL提供了更接近传统SQL语句的查询语法,也提供了更全面的特性。
HQL语法如下
[select/update/delete…][from…][where …][group by [having…]] [order by…]
HQL基于SQL,同时提供了更加面向对象的封装。
HQL实用技术
实体查询
属性查询
实体更新与删除
分组与排序
参数绑定
引用查询
联合查询
子查询
数据加载方式
SQL查询
HQL实用技术-实体查询
HQL子句本身大小写无关,但是其中出现的类名和属性名必需主要大小写区分
如:
String hql=”from TUser”
Query query=session.createQuery(hql)
List userList=query.list();
上面语句查出Tuser以及子类的所有记录。
如果写 from Object,则查出数据库表中所有表的所有记录。
可以加where条件,写法与SQL基本一致
HQL实用技术-属性查询
有时候我们并不需要获取完整的实体对象,如只想或许用户的名字,可以如下:
 List list=session.createQuery(“select user,name from Tuser user”).list();
直接可以对list迭代,每个元素是String
也可以获得多个属性,如
  select user,name,user,age from Tuser as user
对返回的List可以迭代,每个元素是Object[]
HQL实用技术-属性查询
返回数组方式不复合面向对象风格,可以通过在HQL中动态构造对象的方法进行封装。
 CreateQuery(“select new Tuser(user,name,user.age) from Tuser as user”).List()
此时返回的是List,迭代后,每个元素为TUSer对象
通过在HQL中动态构造对象实例,我们实现了对查询结果的对象化封装。注意的是,结果中的Tuser除了查询出来的属性被赋值外,其他的属性均没有赋值。
在HQL的Select子句中可以使用统计函数,如count(*),也可以用数据中函数,如Upper(user.name).也可以用distinct
HQL实用技术-实体更新与删除
Hibernate2中,HQL只支持查询,更新如下:
Tuser user=(TUser)session.get(Tuser.class,new Integer(1))
User.setAge(new Integer(18))
Session.save(user)
如果批量修改,就很麻烦。
HQL实用技术-实体更新与删除
Hibernate3中:
Query query=session.createQuery(“update Tuser set age=18 where id=1”)
Query.executeUpdate();
删除:
Query query=session.createQuery(“delete Tuser where age>18”)
Query.executeUpdate();
需要注意的是:使用HQL delete/update子句时候,注意对缓存策略的影响。
HQL实用技术-分组与排序
与SQL类似,HQL可以用order by排序
也可以用Group by和having对组进行操作
分组后,每组的数据都封装到数组里。
HQL实用技术-参数绑定
可以在HQL语句中直接拼写查询参数。但是这种方式编码凌乱,可读性差,性能低,额外风险(SQL Injection)
参数可以通过绑定方式解决
session.find方法(Hibernate2)
session.find(“from Tuser where name=?”,”Erica”,Hibernate.STRING)
 多参数情况用数组实现。
HQL实用技术-参数绑定
可以通过Query接口进行参数填充:
Query query=session.createQuery(“from Tuser user where user.name=? and user.age>?”);
Query.setString(0,”Erica”);
Query.setInteger(1,20);
HQL实用技术-参数绑定
除了顺序占位符之外,Hibernate还支持引用占位符
Query query=session.createQuery(“from Tuser where name : =name”)
Query.setParameter(“name”,”Erica”)

还可以用JavaBean封装查询参数
class UserQuery{
 private String name;
 private Integer age;
 getter/setter
}
String hql=“from Tuser where name:=name and age:=age”
query=session.createQuery(hql)
userQuery uq=new UserQuery();
uq.setName(..);
uq.setAge(…);
query.setProperties(up)
Iterator it=query.iterate()

HQL实用技术-引用查询
Hibernate提供了HQL可配置化的内置支持。
在实体映射中,通过query节点定义查询语句
通过session.getNamedQuery(…)方法获得Query
HQL实用技术-联合查询、子查询
Hibernate支持多表联合查询、子查询
语法与SQL类似
HQL实用技术-数据加载方式
在传统的JDBC操作中,我们通常通过SQL语句加载所需要的数据进行处理,当SQL提交之后,这些数据就被读取待用。
而在Hibernate世界里,我们有更多选择(针对关联数据)
HQL实用技术-数据加载方式
即时加载(Immediate Loading)
当实体加载完成后,立即加载其关联数据。
延迟加载(Lazy Loading)
实体加载时,其关联数据并非立即获取,而是当关联数据第一次被访问时再进行读取
预先加载(Eager Loading)
实体及关联对象同时读取,这与即时加载类似。不过实体及其关联数据是通过一条SQL语句(基于外连接)同时读取
批量加载(Batch Loading)
对于即时加载和延迟加载,可以采用批量加载方式进行性能上的优化。
HQL实用技术-数据加载方式
即时加载(Immediate Loading)
<set … lazy=”false”>
Session.find方法执行时,Hibernate连接了两条SQL,分别完成了Tuser和Taddress对象的加载。这就是即时加载的基本原理,当宿主实体(关联主题)加载时,Hibernate会立即自动读取关联的数据并完成关联属性的填充
HQL实用技术-数据加载方式
延迟加载:
即时加载中,加载Tuser对象同时,即同时加载了其所关联的Taddress对象。这就就导致如下性能损耗:读取Tuser对象数据,不用地址信息时,必需付出同时读取地址数据的性能代价。
延迟加载就是为了解决这个问题。
<set … lazy=true>
在真正需要关联的Address时,才加载Address
HQL实用技术-数据加载方式
预先加载
在一对一的例子中,我们已经使用过。预先加载即通过outer-join完成关联数据的加载,这样,通过一条SQL语句即可以完成实体及其关联数据的读取操作,相对即时读取的两条甚至若干条SQL而言,无疑这种机制在性能上带来了更多的提升。
不过,对于集合类型,(也就是一对多,多对一,或者多对多关系中),我们并不推荐采用预先加载的方式,理由与即时加载一样,对于集合,只要允许,尽量用延迟加载,避免性能损耗。
HQL实用技术-数据加载方式
预先加载
在特殊情况下,特别对于复杂的关联关系,如多层关联,Hibernate生成的Outer-JoinSQL可能太复杂,此时,我们应该根据情况判断预先加载在当时环境中的可用性。同时,也可以调整hibernate.max-fetch-depth限定outer-join层次,一般设定到5层
HQL实用技术-数据加载方式
批量加载:
批量加载,就是通过批量提交多个限定条件,一次完成多个数据的读取。
对于以下形式的SQL:
select * from T_User where id=1
select * from T_User where id=2
可以合成:
select * from T_User where id=1 or id=2
HQL实用技术-数据加载方式
这就是所谓批量加载机制,如果使用了批量加载机制,Hibernate在进行数据查询操作前,会自动在当前session中寻找是否还有其他同类型待加载的数据,如果有,将条件合并在当前select语句一并提交,这样,通过一次数据库操作完成多个读取任务。
实体配置时,可以通过batch-size打开批量加载机制,并限定每次批量加载的数量
<class name=”TUser” table=”T_User” batch-size=”5”>
batch-size应该设定为一个合理数值(<10)
实体对象生命周期
一个持久化类的实例可能处于三种不同状态中的某一种:
瞬态(transient)
持久化(persistent)
托管(detached)
瞬态(transient)
该实例从未与任何持久化上下文关联过。
它没有持久化标识(相当于主键值)。
持久化(persistent)
实例目前与某个持久化上下文有关联。
它拥有持久化标识(相当于主键值),并且可能在数据库中有一个对应的行。
对于某一个特定的持久化上下文,Hibernate保证持久化标识与Java标识(其值代表对象在内存中的位置)等价。
托管(detached)
实例曾经与某个持久化上下文发生过关联,不过那个上下文被关闭了,或者这个实例是被序列化(serialize)到另外的进程。
它拥有持久化标识,并且在数据库中可能存在一个对应的行。
对于托管状态的实例,Hibernate不保证任何持久化标识和Java标识的关系。

发表评论

邮箱地址不会被公开。 必填项已用*标注

昵称 *