Hibernate出现的原因:
直接使用JDBC操作数据库的步骤很繁琐;
JDBC操作的是关系型数据库;
我们用JAVA开发程序,则使用面向对象的思想;
Hibernate正是在这两种不同的模型之间建立关联,
Hibernate给我们提供了利用面向对象的思想来操作关系型数据的接口
关系模型(Relational Model)
关系模型把世界看作是由实体(Entity)和联系(Relationship)构成的。
所谓实体就是指现实世界中具有区分与其它事物的特征或属性并与其它实体有联系的对象。在关系模型中实体通常是以表的形式来表现的。表的每一行描述实体的一个实例,表的每一列描述实体的一个特征或属性。
所谓联系就是指实体之间的关系,即实体之间的对应关系。
1:1 1:n m:n
关系数据库
表
字段
主键
外键
面向对象三大特征:封装、继承(一般与特殊)
、多态(覆盖与重载)
类,对象,属性,关系
一般与特殊关系(is a)
组成(has a)
关联及其多重性1:1 1:n m:n
双向关联与单向关联
对象关系映射(Object Relational Mapping,简称ORM)
ORM是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。什么是ORM?
面向对象的开发方法是当今企业级应用开发环境中的主流开发方法
关系数据库是企业级应用环境中永久存放数据的主流数据存储系统
字母O起源于“对象”(Object)而R则来自于“关系”(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和表现层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息等等。而这些代码写起来总是重复的。
对象-关系映射模式
属性映射
类映射
关联映射
一对一一对多多对多
Hibernate
对象/关系映射一直都是数据库技术中的难点,尽管人们提出了许多方案解决这个问题,但都不能完全做到即便利又高效。
EJB的推出让人们看到了希望,但实践证明实体Bean的效率并不高,并且还十分难于被初学者理解。由Gavin King创建的Hibernate框架,从某种程序上正在朝着正确的方向迈进,并且得到越来越多IT从业人员的认可。就像当年的Struts框架一样,Hibernate也已经在许多项目中得到广泛应用。
Hibernate由于投注了更多的精力在提升效率上,使用起来又十分方便,新版的EJB规范正在向Hibernate方向靠拢。正是由于得到广泛的认可,Hibernate已经成为程序员必须掌握的技术之一。
Hibernate能帮助我们利用面向对象的思想,开发基于关系型数据库的应用程序
第一:将对象数据保存到数据库
第二:将数据库数据读入对象中
Hibernate的基本组件
实体类
实体类映射文件
重点学习的部分Hibernate配置文件
辅助工具
Hibernate核心接口
Hibernate
运行的底层信息:数据库的URL用户名密码JDBC驱动类,数据库Dialect,
数据库连接池等(
hibernate.cfg.xml
)
Hibernate映射文件(*.hbm.xml)。
Hibernate配置的两种方法:
属性文件(hibernate.properties)
调用代码:
Configuration cfg = new Configuration();
XML文件(hibernate.cfg.xml)
调用代码:
Configuration cfg = new Configuration().configure();
SessionFactory(会话工厂)
概述:
它与数据库绑定,一个数据库对应一个SessionFactory,关于数据库中的所有东西(例如:表之间的关联)都放在SessionFactory中了,二级缓存与SessionFactory中的二级缓存就是进程级的缓存,就相当于Web中的Application对象,因此它是重量级的,它的创建时间比较耗时,所以该对象只创建一个,不要频繁创建。应用程序从SessionFactory(会话工厂)里获得Session(会话)实例。它在多个应用线程间进行共享。通常情况下,整个应用只有唯一的一个会话工厂——例如在应用初始化时被创建。然而,如果你使用Hibernate访问多个数据库,你需要对每一个数据库使用不同的会话工厂(即:需要为每个数据库都创建一个会话工厂)会话工厂缓存生成的SQL语句和Hibernate在运行时使用的映射元数据。
调用代码SessionFactory sessionFactory = cfg.buildSessionFactory();
说明:
SessionFactory由Configuration对象创建,所以每个Hibernate配置文件,实际
上是对SessionFactory的配置。
Session(会话)概述:
它是操纵Hibernate进行CRUD(增、删、改、查)操作的,即Hibernate在进行CRUD操作时必须使用Session,Session不同于JDBC中的Connection。
也可以这样理解Session对Connection又进行了一层包装,打开一个Session
并不等于打开一个Connection,因为Session的功能要比Connection强,Session不仅具有Connection的功能,还具有管理一级缓存的功能,例如在Hibernate中保存一个对象,Hibernate要完成两个功能,一是发出相应的SQL语句将数据存储到数据库表中,另一个功能是把当前这个对象放入到缓存中Session不是线程安全的,它代表与数据库之间的一次操作,它的概念介于Connection和Transaction之间,由于不是线程安全的,所以不能多线程共享使用,否则会产生莫明其妙的问题,Session通常是一个业务请求过来,就open出一个Session,业务请求完毕后Session随之关闭,通常是Session关闭后,与该Session对应的事务就关闭了,即一个业务请求对应一个事务。
Session也称为持久化管理器,因为它是与持久化有关的操作接口。
Session通过SessionFactory打开,在所有的工作完成后,需要关闭。它与Web的
HttpSession没有任何关系。
调用代码Session session = sessionFactory.openSession();
持久化对象的状态
瞬时对象
(Transient Objects)
使用new 操作符初始化的对象不是立刻就持久的。它们的状态是瞬时的,也就是说它们没有任何跟数据库表相关联的行为,只要应用不再引用这些对象
(不再被任何其它对象所引用),它们的状态将会丢失,并由垃圾回收机制回收。
特点是:数据库中没有与之对应的记录。
持久化对象
(Persist Objects)
持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,
持久实例是在事务中进行操作的——它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。特点是:数据库中有与之对应的记录,同时session
中也有该记录对应的对象存在,即受session管理。
离线对象
(Detached Objects)
Session
关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,它们不再受Hibernate管理。特点是:数据库中有与之对应的记录,同时session中没有该记录对应的对象存在,即不受session管理。
持久化对象的生命周期(lifecycle)
Transaction(事务)
概述:
它将应用代码从底层的事务实现中抽象出来——这可能是一个JDBC事务,一个JTA用户事务或者甚至是一个公共对象请求代理结构(CORBA)——允许应用通过一组一致的API控制事务边界。这有助于保持Hibernate应用在不同类型的执行环境或容器中的可移植性。
调用代码:
Transaction tx = session.beginTransaction();
注:使用Hibernate进行操作时必须显式的调用Transaction(默认:autoCommit=false)。
Query接口
概述:
Query(查询)接口允许你在数据库上执行查询并控制查询如何执行。查询语句使用HQL或者本地数据库的SQL方言编写。
调用代码:
Query query = session.createQuery("from User");
注意: 在Hibernate 3.x中不建议使用如下包中的类: org.hibernate.classsic.*
这个包下面的类,是Hibernate 3.x为了向下兼容Hibernate 2.x才保留下来的。
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="com.domain.User">
<id name="id">
Hibernate 3.x 基础
Hibernate出现的原因:
直接使用JDBC操作数据库的步骤很繁琐;
JDBC操作的是关系型数据库;
我们用JAVA开发程序,则使用面向对象的思想;
Hibernate正是在这两种不同的模型之间建立关联,Hibernate给我们提供了利用面向对象的思想来操作关系型数据的接口。
关系模型(Relational Model)
关系模型把世界看作是由实体(Entity)和联系(Relationship)构成的。
所谓实体就是指现实世界中具有区分与其它事物的特征或属性并与其它实体有联系的对象。在关系模型中实体通常是以表的形式来表现的。表的每一行描述实体的一实例,表的每一列描述实体的一个特征或属性。
所谓联系就是指实体之间的关系,即实体之间的对应关系。
1:1 1:n m:n 关系数据库
表(字段,主键,外键)
面向对象三大特征:封装、继承(一般与特殊)、多态(覆盖与重载)
类对象属性关系
一般与特殊关系(is a)
组成(has a) 关联及其多重性 1:1 1:n m:n 双向关联与单向关联
对象关系映射(Object Relational Mapping,简称ORM)
ORM是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。
什么是 ORM?
面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统
字母O起源于“对象”(Object),而R则来自于“关系”(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和表现层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。 当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息等等。而这些代码写起来总是重复的。
对象-关系映射模式
属性映射
类映射
关联映射 一对一 一对多 多对多
Hibernate 对象/
关系映射一直都是数据库技术中的难点,尽管人们提出了许多方案解决这个问题,
但都不能完全做到即便利又高效。EJB的推出让人们看到了希望,但实践证明实体Bean的效率并不高,并且还十分难于被初学者理解。由Gavin King创建的Hibernate框架,从某种程序上正在朝着正确的方向迈进,并且得到越来越多IT从业人员的认可。就像当年的Struts框架一样,Hibernate也已经在许多项目中得到广泛应用。Hibernate由于投注了更多的精力在提升效率上,使用起来又十分方便,新版的EJB规范正在向Hibernate方向靠拢。正是由于得到广泛的认可,Hibernate已经成为程序员必须掌握的技术之一。
Hibernate能帮助我们利用面向对象的思想,开发基于关系型数据库的应用程序
第一:将对象数据保存到数据库
第二:将数据库数据读入对象中
基于B/S的典型三层架构
Hibernate配置的两种方法:属性文件(hibernate.properties)
调用代码:Configuration cfg = new Configuration();
XML文件(hibernate.cfg.xml)
调用代码:Configuration cfg = new Configuration().configure();
SessionFactory(会话工厂)
概述:
它与数据库绑定,一个数据库对应一个SessionFactory,关于数据库中的所有东西(例如:表之间的关联)都放在SessionFactory中了,二级缓存与SessionFactory相关,二级缓存就是进程级的缓存,就相当于Web中的Application对象,因此它是重量级的它的创建时间比较耗时,所以该对象只创建一个,不要频繁创建。应用程序从SessionFactory(会话工厂)里获得Session(会话)实例。它在多个应用线程间进行共享。通常情况下,整个应用只有唯一的一个会话工厂——例如在应用初始化时被创建。然而,如果你使用Hibernate访问多个数据库,你需要对每一个数据库使用不同的会话工厂(即:需要为每个数据库都创建一个会话工厂)。
会话工厂缓存了生成的SQL语句和Hibernate在运行时使用的映射元数据。
调用代码: SessionFactory sessionFactory = cfg.buildSessionFactory(); 说明:SessionFactory由Configuration对象创建,所以每个Hibernate配置文件,实际上是对SessionFactory的配置。
Session(会话)概述:
它是操纵Hibernate进行CRUD(增、删、改、查)操作的,即Hibernate在进行
CRUD操作时必须使用Session,Session不同于JDBC中的Connection。也可以这样理解Session对Connection又进行了一层包装,打开一个Session并不等于打开一个Connection因为Session的功能要比Connection强,Session不仅具有Connection的功能,还具有管理一级缓存的功能,例如在Hibernate中保存一个对象,Hibernate要完成两个功能,一是发出相应的SQL语句将数据存储到数据库表中,另一个功能是把当前这个对象放入到缓存中。 Session不是线程安全的,它代表与数据库之间的一次操作,它的概念介于Connection和Transaction之间,由于不是线程安全的,所以不能多线程共享使用,否则会产生莫明其妙的问题,Session通常是一个业务请求过来就open出一个Session,业务请求完毕后Session随之关闭,通常是Session关闭后,与该Session对应的事务就关闭了,即一个业务请求对应一个事务。Session也称为持久化管理器,因为它是与持久化有关的操作接口。
Session通过SessionFactory打开,在所有的工作完成后,需要关闭。它与Web层的HttpSession没有任何关系。
调用代码
Session session = sessionFactory.openSession();
持久化对象的状态
瞬时对象(Transient Objects) 使用new 操作符初始化的对象不是立刻就持久的。它们的状态是瞬时的,也就是说它们没有任何跟数据库表相关联的行为,只要应用不再引用这些对象(不再被任何其它对象所引用),它们的状态将会丢失,并由垃圾回收机制回收。 特点是:数据库中没有与之对应的记录。
持久化对象(Persist Objects)
持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,持久实例是在事务中进行操作的——它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。特点是:数据库中有与之对应的记录,同时session中也有该记录对应的对象存在,即受session管理。
离线对象(Detached Objects) Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,它们不再受Hibernate管理。特点是:数据库中有与之对应的记录,同时session中没有该记录对应的对象存在,即不受session管理。
持久化对象的生命周期(lifecycle)
Transaction(事务)
概述:
它将应用代码从底层的事务实现中抽象出来——这可能是一个JDBC事务,一个JTA用户事务或者甚至是一个公共对象请求代理结构(CORBA)——允许应用通过一组一致的API控制事务边界。这有助于保持Hibernate应用在不同类型的执行环境或容器中的可移植性。
调用代码:
Transaction tx = session.beginTransaction();
注:使用Hibernate进行操作时必须显式的调用Transaction(默认autoCommit=false)。
Query
接口
概述:Query(查询)接口允许你在数据库上执行查询并控制查询如何执行。查询语句HQL或者本地数据库的SQL方言编写。
调用代码: Query query = session.createQuery("from User"); 注意:
在Hibernate 3.x中不建议使用如下包中的类:org.hibernate.classsic.*
这个包下面的类,是Hibernate 3.x为了向下兼容Hibernate 2.x才保留下来的。
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="com.domain.User">
<id name="id">
</id> <property name="name"/>„ </class> </hibernate-mapping>
hibernate-mapping元素: 可以包含的子元素 class –描述被映射的类subclass/joined-subclass –在继承关系的映射中会用到
query – 将查询语句定义在配置文件class元素: 常用属性 name – 实体类的类名 table –被映射到数据库表的名称可以包含的常见子元素 id –
主键定义 property – 属性定义 关系映射定义(一对多、多对一等)主键- id: 被映射的类必须要有一个id定义 通常使用逻辑主键 逻辑主键:没有意义的唯一标识符 业务主键:有意义的唯一标识符 Hibernate使用generator类来生成主键Hibernate自带了很多generator(不同的主键生成策略int/long – native String - uuid 我们也可以定义自己的generator实现IdentifierGenerator接口,一般情况下不需要实现自己的generator。
主键生成策略
generator 主键生成器,每个主键都必须定义相应的主键生成策略。它用来为持久化实
例生成唯一的标识。Hibernate内置的主键生成策略
数据库提供的主键生成机制。identity、sequence(序列)。
外部程序提供的主键生成机制。increment (递增)(这种策略在做集群时,不应该使用,因为该策略是在同一个JVM中是唯一的) ,hilo(高低位) ,seqhilo(使用序列的高低位 ),uuid.hex(使用了IP地址+JVM的启动时间(精确到1/4秒+系统时间+一个计数器值(在JVM中唯一) ),uuid.string。 其它:native(本地),assigned(手工指定),foreign(外部引用)。
普通属性映射 - property:
<property name="property_name"/>
可使用的常见属性如下:
name – 对应类的属性名称type –
指定属性的类型,一般情况下可以不用指定,由hibernate自动匹配(可参考文档中的有关说明)length –指定长度 column – 指定属性所对应的数据库字段的名称,如果不指定,就是属性的名称实体类的设计原则: 实现一个默认的(即无参数)构造方法。 提供一个标识属性(即id,当然也可以用别的名称,它一般和业务无关),这个属性就是与数据库中的主键字段对应的。
使用非final类型的类,即实体类最好不定义成
final类型的,否则在使用时,有时Hibernate会抛出异常。为实体类的中属性提供setter/getter方法,属性的访问控制符可以是private但对应的setter/getter方法的访问控制符一般为public。
Hibernate 3.x 关联映射
many-to-one(多对一关联映射)
User-Dept 多个用户属于某个组 从代码上体现为:
public class Dept {
privte String id;
private String name;
}
public class User{
private String id;
privte String name;
private Dept dept;
public Dept getDept (){return dept;}
public void setDept (Dept dept){
this.dept = dept;
}
}
many-to-one
的映射最常用,也是最容易理解和编写的
<many-to-one name="dept" column="deptid"/>
生成的
DDL
语句如下
create table T_Dept (
id varchar(255) not null,
name varchar(255),
primary key (id))
create table User (
id varchar(255) not null,
name varchar(255),
password varchar(255),
createTime datetime,
expireTime datetime,
deptid varchar(255),
primary key (id))
alter table User add index
FK285FEBC3D18669 (deptid),
add constraint
FK285FEBC3D18669 foreign key (deptid) references T_Dept (id)
从生成的DDL语句,我们可以知道,实际上是在User表上建立了一个指向Group表的外键关联。
<many-to-one name="dept" column="deptid"/>
的含义:
该标签是
加在多的一端
,它会在多的一端加入一个字段,这个字段的名字就叫做
deptid,这个字段加上后它就会作为外键参照name属性指定的dept(即对应的实体类是Dept,即叫做一的一端)的主键字段。也就是说它会在多的一端对应的表中加入一个字段,这个字做作为外键字段参照一的一端对应表的主键字段,这个外键字段将来存储的就是一的一端的主键。
重要属性-cascade(级联)级联的意思是指定两个对象之间的操作联动关系,
对一个对象执行了操作之后,对其指定的级联对象也需要执行相同的操作 总共可以取值为:all、none、save-update、delete all:代表在所有的情况下都执行级联操作none:在所有情况下都不执行级联操作save-update:在保存和更新的时候执行级联操作 delete:在删除的时候执行级联操作
如:<many-to-one name="group"column="groupid“ cascade="all"/>
级联只对添加、修改、删除起作用,对加载(查询)不起作用(1)无cascade配置的User-Group执行代码: <many-to-one name="dept" column="deptid"/>
java代码:
Dept dept = new Dept();
dept.setName("jkjk");
User user = new User();
user.setName("管理员");
user.setDept(dept);
session.save(user);
执行结果:
抛出org.hibernate.TransientObjectException异常,以上代码中,dept对象是一个瞬时对象,user对象引用了一个瞬时对象,所以在保存的时候出现异常。(2)无cascade
配置时正确的java执行代码:
为避免异常,我们可以需要将dept对象保存
Dept dept = new Dept();
dept.setName("jkjk");
执行save操作之后,dept对象变成持久化对象的状态
悲观锁:
锁的含义--锁具有排它性,也就是说当一上线程将数据锁住后,其它线程不能进行读取或修改被锁定的数据。
悲观锁的实现,通常依赖于数据库机制,在整个过程中将数据锁定,其它任何用户都不能读取或修改。 悲观锁适用场合:
数据库自增字段(序列)的实现
一般适用于短事务,因为长事务执行时间较长,在此过程中数据会被一直锁定,其它用户不能进行访问和修改,因此并发性不好,但是如果此项业务的确需要进行串行访问,针对此现象只能使用悲观锁,因为此时需要的数据安全性较高。即在要求数据准确性和安全性较高的情况下,可以采用悲观锁来实现,但是同时还需要考虑访问速度性能下降的现实问题。
Session接口的load()方法有两种重载形式:
一种是正常支持延迟加载数据;
另一种是使用带锁的方式来加载数据。 LockMode(锁模式)常用的参数如下:
LockMode.UPGRADE
LockMode.UPGRADE_NOWAIT:专门为Oracle数据库提供的。
public void testLoad(){
Inventory inv = null; try{
inv = (Inventory)session.load(Inventory.class, 1, LockMode.UPGRADE_NOWAIT); } catch(Exception e){ } finally{ } }
乐观锁:
乐观锁实质上不是锁,它是一种冲突检测手段,也就是数据读取上来后,可以随意修改,但是在更新或保存的时候它进行检测,如果检测到的数据版本低于数据库中该记录的版本号时,提示不能进行更新。
乐观锁的并发性较好,因为当一个线程修改数据时,其它线程是可以继续访问数据。
例如:SVN版本控制器就是基于乐观锁的思想实现的,而微软公司的VSS版本控制器则是基于悲观锁的思想实现的。
Hibernate对乐观锁的支持主要体现在实体映射文件上,如下所示: <!-- 配置optimistic-lock属性指定当前实体类使用乐观锁,其中version表示乐观锁的实现采用版本检测方式 -->
<class name="Inventory" table="t_inventory" optimistic-lock="version"> <id name="id" column="i_id" length="32"> <generator class="sequence">
<param name="sequence">myseq</param> </generator> </id>
<!-- 加入<version.../>标签,用于映射实体类中用于保存乐观锁标记的属性,它必须在<id.../>标签的后面出现-->
<version name="version" column="i_version"/> <property name="itemName" column="v_itemName"/> <property name="counts" column="i_counts"/> </class>
实体类的设计:
public class Inventory { private int id;
private String itemName; private int counts;
//用于存储表中版本检测字段的值,类型必须为int private int version; public Inventory(){
} /*
* 提供所有属性的setXXX和getXXX方法 */ }
大多数基于数据版本记录机制(version)实现,一般是在数据库表中加入一个version字段,读取数据时将版本号一同读出,之后更新数据时版本号自动加1(Hibernate会自动完成),如果提交数据时版本号小于数据表中的版本号,则认为数据是过期的,否则给予更新。
Hibernate 3.x——HQL查询语言
示例:Hibernate3_10_HQL Hibernate查询:
数据查询与检索是Hibernate中的一个亮点。相对其他ORM实现而言,Hibernate提供了灵活多样的查询机制。
标准化对象查询(Criteria Query):以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)
Hibernate语言查询(Hibernate Query Language,HQL):它是完全面向对象的查询语句,查询功能非常强大,具备多态、关联等特性 。Hibernate官方推荐使用HQL进行查询。 Native SQL Queries(原生SQL查询):直接使用标准SQL语言或跟特定数据库相关的SQL进行查询。
Hibernate语言查询(Hibernate Query Language,HQL): HQL用面向对象的方式生成SQL语句:
以类和属性来代替表和数据列; 支持多态; 支持各种关联; 减少了SQL的冗余;
HQL支持所有的关系数据库操作:
连接(joins,包括Inner/outer/full joins),笛卡尔积(cartesian products); 投影(projection)--查询一到多个属性;
聚合(Aggregation,max, avg)和分组(group); 排序(Ordering); 子查询(Subqueries);
SQL函数(SQL function calls)
简单示例:查询用户名以“王”开头的所有用户。 Query query = session.createQuery(
"from User user where user.name like '王%'"); List users = query.list();
复杂示例:从User和Group中查找属于“admin”组的所有用户。 Query query = session.createQuery(
"from User user where user.group.name='admin'"); 如果用传统的SQL则查询语句如下:
select user.userId as userId, user.name as name, user.groupId as groupId, user.idCardId as idCardId from TBL_USER user, TBL_GROUP group where (group.groupName='admin' and user.groupId=group.groupId)
HQL语言-简单属性查询: (1)单一属性查询:
List users = session.createQuery("select name from User").list(); 说明:
List集合中此时存储的是name属性集合,其类型为实体类(即User)的name属性的类型(本例为String类型),所以List集合中对象的类型取决于实体类中属性的类型。
(2)多属性查询: List users =
session.createQuery("select id, name from User ").list(); 说明:
List集合中此时存储的对象数组(即Hibernate查询多个属性时返回的是对象数组,一条记录为一个对象数组,最后将所有对象数组存储到集合中),其中对象数组的长度与查询的属性个数是相同的(即select关键字后面有多少个属性,将来对象数组的长度就是多少);
为什么会是Object类型的数组?这是因为实体类中属性的类型是不同的,所以用Object类型表示更为通用。
数组下标元素存储的对象类型与实体类中属性的类型相同。
(3)采用HQL动态实例化对象方式返回所要查询的属性: List users =
session.createQuery("select new User(id, name) from User").list(); 说明:
List集合中此时存储的是User对象,但是该User对象并不是实体对象,而是我们自己new出来的(即不是Hibernate自动封装的),因此不受Session统一管理,此时的User对象中只有id和name属性有值,其它属性均为null。 User对象必须提供一个构造函数,形式为: public User(int id, String name) {„}
否则将不能创建User对象,因为创建对象时调用的是带参数的构造方法。
(4)采用别名的方式查询属性值:
采用别名方式查询属性值,在Hibernate中可以使用as关键字指定别名,也可以不使用as关键字指定别名,两者都可以指定别名,如下所示: 不采用as关键指定别名 List users =
session.createQuery("select u.id, u.name from User u").list(); 采用as关键字指定别名 List users =
session.createQuery("select u.id, u.name from User as u").list();
说明:当指定别名后,在查询的属性名需要使用前缀时必须使用别名,而不能再使用原实体对象的名称,即: u.id, u.name。
【小结】
单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致; 多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致;
数组的长度取决与select中属性的个数;
如果认为返回数组不够对象化,可以采用HQL动态实例化对象的方式: select new User(id, name) from User
String key = "'%张%'";
hql = hql + key;
List user = session.createQuery(hql).list();
(2)采用占位符的形式查询 如:
Query query = session.createQuery("select u.id, u.name from User u where u.name like ?");
query.setParameter(0, “%王%"); List users = query.list(); 或使用方法链编程:
List users = session.createQuery("select u.id, u.name from User u where u.name like ?")
.setParameter(0, “%王%") .list();
注:在Hibernate中第1个占位符的下标位置从0开始
(3)采用参数命名的方式查询 单一条件
List users = session.createQuery("select u.id, u.name from User u where u.name like :myname")
.setParameter("myname", "%张%") .list(); 多条件
List users = session.createQuery("select u.id, u.name from User u where u.name like :myname and u.id=:myid")
.setParameter("myname", "%张%") .setParameter("myid", 1) .list();
说明:定义命名参数固定格式::+参数名称(即:myid ) 赋值时,直接写参数名即可: setParameter("myid", 1) setParameter方法用于为参数赋单一值
(4)采用in关键字(即包含)查询
List users = session.createQuery("select u.id, u.name from User u where u.id in(:myids)")
.setParameterList("myids", new Object[]{1, 2, 3, 4, 5}) .list();
说明:由于in中的条件是集合的形式,所以赋值是需要采用setParameterList这种方法,赋的值则采用对象数组的形式。
(5)采用数据库函数查询——不建议使用
在Hibernate的HQL语言中可以直接使用数据库中的函数,但是这就使Hibernate与特定的数据库进行了高耦合,不便于日后的移植。
如:
List students = session.createQuery("select u.id, u.name from User u where CONVERT(varchar(10), DATEPART(mm,u.create_time))=?") .setParameter(0, "06") .list();
(6) 查询某一时间段的数据
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //查询2010-06-10到2010-07-31入职的员工
List users = session.createQuery("select u.id, u.name from User u where u.create_time between ? and ?")
.setParameter(0, sdf.parse("2010-06-10 00:00:00")) .setParameter(1, sdf.parse("2010-07-31 23:59:59")) .list();
说明:在实际项目开发中,关于日期段的查询一般都是从起始日期的00:00:00时间开始,结束于结束日期的23:59:59,这样对于日期类型的数据库表的字段更为精确。
【小结】
可以采用拼字符串的方式传递参数
可以采用 ?来传递参数(索引从0开始) 可以采用 :参数名 来传递参数
如果传递多个参数,可以采用setParamterList方法
在HQL语言中可以使用数据库的函数,如:CONVERT、DATEPART
HQL语言-原生SQL语句查询: 采用原生SQL语句查询
List users = session.createSQLQuery("select * from t_user").list(); 说明:
使用原生SQL语句,需要使用:SQLQuery接口; 使用原生SQL语句时,不会往Session(一级)缓存中放入数据,即SQLQuery接口中的list()方法不会往Session(一级)缓存中放入数据;
SQLQuery接口的list()方法返回的List集合中存储的是数组对象
HQL语言-外置命名查询:
外置命名查询就是指把HQL语句放到一个配置文件中,而不是把HQL语句直接写到程序代码中,采用这种方式的目的是为了让HQL语句与程序代码更好的进行解耦。
在实际开发中这并不是决对的,因为当把所有的HQL语句放入到一个配置文件中在调试时,会增加一定的工作量,有时显得会不太方便,当项目追求低耦合度时,可以考虑采用这种方式。
实体映射文件:
在</class>的标签后面加入如下代码: <!-- 外置命名查询定义 --> <query name="searchUsers">
<![CDATA[
SELECT u FROM User u where u.id<? ]]> </query>
说明: 配置文件中之所以使用<![CDATA[„ ]]>是因为在HQL语句中含有”<”号,该符合是XML语言的组成部分,如果文本中含有XML语言特定的符号时可以将文本放在<![CDATA[„ ]]> 中,如果不使用<![CDATA[„ ]]>,则这些特定符号需要进行转义,否则会引起一些问题。
使用:
List users = session.getNamedQuery("searchUsers")
.setParameter(0, 6) .list(); 说明:
searchUsers:为配置文件中外置命名查询的名称
setParameter(0, 6):为外置命名查询的HQL语句的占位符赋值 在映射文件中采用<query>标签来定义HQL语句
在程序中采用session.getNamedQuery()方法得到HQL查询串
HQL语言-查询过滤器:
启用查询过滤器后,当Session没有关闭的时候,只要在Session中执行的所有HQL语句都会自动加上查询过滤器中的条件(即实现过滤功能)。
(1)定义查询过滤器
在</class>的标签后面加入如下代码:
<filter-def name="filtertest">
<filter-param name="myid" type="integer"/>
</filter-def> 说明
filtertest:查询过滤器名称
myid:过滤条件中的参数名称
type:指定参数的类型,本例为小写的integer表示这是Hibernate中的类型,也可以写成Java中的java.lang.Integer
(2)为实体映射文件指定使用查询过滤器在<class>标签中的最后加入如下代码:
<!-- 指定使用查询过滤器,i_id数据库字段名 -->
<filter name="filtertest" condition="i_id < :myid"/>
说明:
name:指定要使用的查询过滤器名称condition:指定查询过滤器的条件,将来过滤器将使用该条件进行过滤操作。其中“< ”表示的是“<”号,因为该符号是XML语言的特殊符号,所以在这里需要进行转义。
(3)在程序中启动查询过滤器
//启用查询过滤器,只有启动以后查询过滤器才起作用
session.enableFilter("filtertest") .setParameter("myid", 6);
【小结】
在映射文件中定义过滤器参数在类的映射中使用这些参数在程序中启用过滤器查询过滤器中的条件在Hibernate生成具体的SQL语句时会自动加上该条件,所以条件中必须使用表中的字段名HQL语言-分页查询:
Hibernate分页查询
List users = session.createQuery("from User")
//指定从第几条记录开始,下标从0开始,本例为从第2条记录开始查询
.setFirstResult(1) //每页显示多少条记录
.setMaxResults(2) .list();
HQL语言-对象导航查询:
所谓的对象导航查询是指查询某一个实体时,条件是这个实体所关联的另外一个实体对象,例如:查询员工的姓名,条件是根据部门名称进行查询。
对象导航查询:
List users = session.createQuery("select u.name from User u where U.dept.name like '%人力%'").list(); HQL语言的含义是:查找部门名称中含有“人力”字样的部门中的员工姓名字段。
说明:
查询的条件是根据User类中的关联的实体对象dept的名称来进行查询的,其中dept是User类中对Dept的引用,那么这种导航的工作原理是根据配置文件中的关联映射完成的。
在HQL语句中采用
进行导航HQL语言-连接查询:
Hibernate中支持3种连接查询:
内连接
左外连接
右外连接
说明:HQL中的连接必须采用对象导航进行连接,否则将抛出org.hibernate.hql.ast.QuerySyntaxException异常。
(1)内连接
示例:查询员工的姓名及所在部门的名称
内连接有两种方式:
不使用inner关键字
List users = session.createQuery("select d.name, u.name from User u join u.dept d").list();
使用inner关键字
List users = session.createQuery("select d.name, u.name from User u inner join u.dept d").list();
说明: 在Hibernate中上述 的HQL语句就能够将User实体对象和Dept实体对象的关联关系建立起来了,因为这都是通过实体映射文件来完成的,体现在表上则是通过两张表的连接完成的。 (2)左外连接
示例:查询部门的名称(包含没有员工的部门)及员工的姓名
List users = session.createQuery("select d.name, u.name from Dept d left join d.users u").list();
说明: Hibernate会根据Dept实体类的映射文件中的关联映射关系自动发出左外连接的SQL语句(即t_dept表与t_user表之间采用左外连接的SQL语句)。 (3)右外连接
示例:查询员工姓名(包含未分配部门的员工)及员工所在部门的名称。
List users = session.createQuery("select d.name, u.name from Dept d right join d.users u").list(); 说明:Hibernate会根据Dept实体类的映射文件中的关联映射关系自动发出右外连接的SQL语句(即t_dept表与t_user表之间采用右外连接的SQL语句)。
HQL语言-统计查询:
Hibernate 3.x支持统计查询,但是使用Hibernate做纯粹的统计应用比较少,因为涉及到的数据量非常大,在实际项目开发中,一般采用第三方的报表工具,比较常用的有:iReport、如意报表、JfreeChart等。
示例:查询员工人数
统计总数,一般有编程两种方式:
(1)采用获取返回的List集合长度的方式
List users = session.createQuery("select count(id) from User").list(); //Hibernate的统计返回值一般都是long型,防止统计数据过大造成越界异常 Long count = (Long)users.get(0);
说明:这种方式效率不高,因为返回的是一个List,而该List中就一个元素(即统计的数量值),然后还需要使用get方法将该元素取出。 (2)采用从返回的Long对象中获取统计数值 Long count =
(Long)session.createQuery("select count(id) from User").uniqueResult(); 说明:这种方式效率高,因为返回的不是一个List集合,而是一个长整型的对象,该对象即是统计出来的数据,因为Hibernate使用长整型来封装统计出来的数值,目的是防止统计出来的数值过大,造成数据溢出异常。数据类型一定要切记,是Long(即封装类)。
HQL语言-DML查询: Hibernate 3.x支持DML风格的操作,所谓DML风格就是指批量的执行insert、update、delete等操作这里的这些操作指的都是批量执行Hibernate中对应的save、update、delete方法。
但是这些批量操作一般不建议使用,因为当批量执行一批语句后,Hibernate是不会修
改缓存的,这就造成了数据库中的数据与缓存中的数据不同步的问题,假设一次性批量插入1万条数据,这时Hibernate不可能将这1万条数据都放入缓存,因为这非常容易导致内存溢出异常。
在实际项目开发中对于那些变化不大数据( 即不是经常访问的数据)可以采用DML来操作。 示例:
session.createQuery("update User u set u.name=? where u.id < ?")
.setParameter(0, "XXX") .setParameter(1, 5) .executeUpdate();
说明:执行完批量的update操作后数据库中的值已经发生了变化,但是缓存中的值并没有发生变化还是原来的值,但是当我们访问时,其实访问的是缓存中的值,这就造成了数据的不准确性。
注:
session.get()和session.load()区别: get()方法:
1. 不支持延迟加载,会马上发出查询sql语句,加载User对象;
2. 使用get()方法加载数据,如果数据库中不存在相应的数据,则返回null。 load()方法:
1. 支持延迟加载,只有真正使用这个对象的时候,才会发出查询sql语句
2. 使用load加载数据,如果数据库中没有相应的数据,那么抛出ObjectNotFoundException,即不会返回null。
get(),load()方法会把查询上来的数据放入缓存,也会使用缓存
get(),load()方法会将查询上来的数据放入一级缓存中一份,然后再将该数据放入二级缓存中一份;先查找一级缓存再查找二级缓存,如果在任何一级缓存中能够查到该数据,则直接进行使用,否则发出SQL加载数据
结论:session是共享二级缓存(SessionFactory缓存)的
Query query = session.createQuery("") query.iterate()
iterate() 查询实体对象: iterate() 使用缓存(但不包括查询缓存,oid是存储在查询缓存中的),发出查询id的SQL语句,不会发出查询实体对象的SQL语句。
原理同load()方法,缓存中如果存在该实体对象,则直接使用,如果不存在该实体对象,则发出SQL语句去查询。
iterate() 方法查询普通属性:
一级缓存、二级缓存只存储实体对象
发出查询姓名属性的SQL语句;不会发出查询ID属性列表的SQL语句(因为此时的HQL语句查询的是普通属性而不是实体对象)
* 一(二)级缓存是缓存实体对象的,即如果查询出来的是实体对象,则才向一(二)级缓存中存放,如果查询出来的是普通属性,则不向一(二)级缓存中存放。
重点!!!
* 思考:执行如下语句,是否将查询出来的数据存放到一级缓存中?
Iterator iter = session.createQuery("select new User(u.name) from User u where u.id=1").iterate();
答案:不会向一级缓存中存放,因为此时查询出来的User对象是我们自己new出来的,而不是Hibernate自动为我们new出来的,所以不受Session管理。
Query.list()方法:
1、当关闭查询缓存(默认)时,list()方法不使用一级缓存和二级缓存中的数据
2、当开启查询缓存时,list()方法就会使用一级缓存和二级缓存中的数据,顺序是先找一级缓存再找二级缓存
Session 、SessionFactory:
clear() 方法清除缓存中的全部实体对象
evict() 方法可以指定要从缓存中清除哪个实体对象