前言:
hibernate是一个ORM框架,也就是对象关系映射框架。Hibernate能够将JAVA类和关系数据库表进行映射,同时提供面向对象的查询机制,配合JDBC,能够帮助程序开发人员从繁琐的数据持久层编程中解放出来。
核心技术: 持久类,对象状态,Hibernate属性配置,映射文件基础,HQL语言,
数据库表设计策略(基于性能和设计),关联映射(一对一,一对多,多对一),继承映射(TPS,TPH,TPC),Hibernate查询性能优化
Chapter 01
1.1 hibernate 概述
hibernate的主要作用是简化应用的数据持久层编程。hibernate位于应用层和数据库之间,解决数据持久层编程。
Hibernate框架包括:
(1) 持久化对象(Persistent Object): 简称为PO,PO用来映射数据库中的记录,可以通过修改PO来修改数据库记录。
(2) Hibernate属性文件(hibernate.properties):配置数据库访问信息,如数据库驱动,连接串,用户名,密码等。也可以用hibernate.cfg.xml的xml文件配置属性。
(3) Hibernate映射文件(XML Mapping):配置持久化对象映射数据库中的记录。映射文件是xml文件,往往使用 .hbm.xml形式命名,其中 表示持久化对象的类名。
示例:
(1) 创建持久化类,映射customer表。
通过持久化对象映射数据库中的记录,通过持久化类得到持久化对象。持久化类与数据库表对应,类的属性与表的字段对应,并提供无参构造方法和符合命名规范的getXXX和setXXX方法。
(2) 在hibernate.cfg.xml中配置数据库连接信息。
hibernate使用连接池获得数据库连接,其发布包中提供了多个第三方开源连接池,也可以使用hibernate内置的连接池。
(3) 在映射文件中配置映射信息。
持久化类和数据库表的映射,其对应关系需要在映射文件中配置,配置持久化类和类的属性的映射。名字与持久化类相同,后缀为.hbm.xml。
通过class节点配置类与表的映射关系,class元素主要有两种子元素,即id和property。id与表的主键对应,其他字段与property元素里的属性对应。
所有的hbm.xml文件都必须在hibernate.cfg.xml里面配置才能使用,如下:
1.2 重用API
作用:解析配置文件,操作持久化对象,从而操作数据库。
(1) Configuration 类。(这个方法已经无法实现)
Configuration类的configure方法,可以用来读取指定的Hibernate属性文件。代码如下:
Configuration conf = new Configuration();
conf.configure(“hibernate.cfg.xml”); //如果是hibernat.cfg.xml文件,可以用con.configure();
(2) SessionFactory类
SessionFactory是session的工厂类。一个应用有唯一的SessionFactory对象,SessionFactory是不可变的。通过conf获取工厂对象。代码如下:
SessionFactory factory = conf.buildSessionFactory();
(3) Session接口。
Session接口是java应用和Hibernate之间的一个主要的运行期接口,是提供持久化服务的核心API。一个Session对象类似于一个数据库连接对象,其生命周期贯穿整个逻辑事务的始末。它的主要功能是操作持久化对象,如创建,读取,删除等,从而操作数据库记录。Session对象可以通过SessionFactory对象获得。Session接口中主要有如下方法:
① save(Object object)
②update(Object object)
③delete(Object object)
④Object get(Class clazz, Serializable id) 查询
如果进行增,删,改操作,必须使用session对象开始一个事务,并使用session对象的commit方法提交事务才能生效。如果发生错误,可以用session对象的rollback方法回滚事务。Hibernate框架的事务接口为Transaction。代码如下:
Session session = factory.openSession();
Transaction tran = session.beginTransaction();
。。。
session.save(xxx);
tran.commit();
session.close();
Chapter 02
2.1 持久化类
(1) 必须提供public的无参构造方法。
(2) 必须提供一个标识属性(Identifier Property)。
持久化类中必须提供一个标识属性,与表的主键对应。
2.2 对象状态
Hibernate开发人员需要一直关注对象的状态。对象有三种状态:
(1) 瞬时状态(transient state)
当通过new操作符实例化了一个对象,而这个对象并没有被Session对象操作,也就是说该对象没有与一个Session对象关联时,那么这个对象就称为瞬时状态对象。瞬时状态的对象不能被持久化到数据库中,也不会被赋予持久化标识(Identifier)。如下面代码的cust对象:
Customer cust = new Customer(“SHETC”,”123”);
(2) 持久状态(persistent state)
如果一个对象与Session对象关联,那么该对象就被称为持久状态对象。持久状态的对象与数据库中的一条记录对应,并拥有持久化标识(Identifier)。当持久化对象有改变时,当前事物提交后,Hibernate会自动检测到对象的变化,并持久化到数据库中。如下面代码的cust对象:
Session session = sessionFactory.getSession();
Transaction tran = session.beginTransaction();
Customer cust = (Customer) session.get(Customer.class,”ETC”);
c.setPwd(“password”);
tran.commit();
session.close();
(3) 托管状态(detached state)。
当与持久状态对象关联的session关闭后,该对象就变成脱管状态(detachedd state)。脱管状态的对象引用依然有效,可以继续使用。
2.3 Hibernate 属性配置
hibernate应用的数据库连接信息都通过Hibernate属性进行配置,可以在hibernate.properties或者hibernate.cfg.xml文件中配置,二者是一样的。当二者同时存在时,后者将覆盖前者。Hibernate框架使用连接池维护数据库连接,hibernate框架支持C3P0和DBCP,以及它自带的连接池。配置文件的基本结构如下:
<hibernate-configuration>
<session-factory>
<property name=""></property>
<mapping resource=""/>
</session-factory>
</hibernate-configuration>
2.4 ORM映射基础
对象和数据库之间的映射关系都在映射文件中配置。映射文件与其对应的类名相同,后缀是.hbm.xml。映射文件的基本结构如下:
<hibernate-mapping>
<class>
<id>
<generator class=""/>
</id>
<property name=""/>
<component name=""/>
<subclass></subclass>
</class>
</hibernate-mapping>
映射文件所有元素都存在根元素hibernate-mapping下面,使用最多的元素是class。class元素下常用的子元素有id,property,component,subclass,joined-class,union-subclass。
(1) class元素
class元素用来定义一个持久化类。class元素的主要属性有:
① name:持久化类的完整名字
② table:与持久化类对应的表名字。
③lazy:延迟加载,可以指定为true或者false。
④abstract:抽象类,指定该类为抽象父类。
(2) id元素
class元素下存在id属性,用来定义与表的主键对应的属性。
(3) generator元素
id元素下必须存在generator元素,用来指定标识属性的生成类。这些生成类都实现了IdentifierGenerator接口。例如:
<id name="custname" type="java.lang.String">
<column name = "custname" length="20">
<generator class = "assigned"/>
</id>
上述配置中的assigned对应org.hibernate.id.Assigned类,该类要求标识属性必须通过代码方式赋值。主要的generator类型有如下几个:
① increment:自动增加,用于为long/int/short类型生产唯一标识。
② identity:对DB2,Mysql等数据库的内置标识字段进行支持。
③ native:根据数据库底层能力,选择合适的生产方式。
④ assigned:通过应用程序指定标识属性,是默认的生成策略。
⑤ foreign:使用另一个关联对象的标识作为标识属性。通过外键生成主键。
(4) property元素
class元素下往往存在大量的property元素,property元素影射了持久类中非标识属性与表字段的映射关系。
(5) component 元素
如果某个持久化类的属性非常多,可以将某些属性封装到新的类中。在映射文件中,即可使用component元素映射新类的属性和表字段。
2.5 HQL语言
Hibernate框架定义了HQL语言,可以完成复杂的数据库操作。HQL语言和SQL语言在语法上很类似,主要区别如下:
(1) HQL语句中出现的是类名,属性名;SQL语句中出现的是表名,字段名。
(2) HQL语言严格区分大小写;SQL不区分
(3) HQL 语言理解继承,多态等面向对象的概念。
使用Hibernate API执行HQL语句的具体步骤。
(1) 获得Query对象。
hibernate框架使用Query对象执行HQL语句,Query对象可以使用Session对象获得。代码如下:
Query query = session.createQuery(“from Customer”);
Query query2 = session.createQuery(“from Customer where age>?”;
使用?指定参数。
(2) 使用Query对象执行查询HQL。
获得Query对象后,就可以使用Query中的方法来执行HQL语句,Query中的执行查询功能的方法如下:
public List list();
list方法可以执行查询功能的HQL语句,并能直接返回持久化对象的List集合。如果HQL语句中有?,可以使用Query的setXXX 方法指定参数的具体值,例如:
setDate(int position, Date date): 在指定位置设置Date类型的参数。
setInteger(int position,Integer i): 在指定位置设置Integer类型的参数。
setString(int position,String s):在指定位置设置String类型的参数。
代码如下:
query2.setInteger(0,20);
List list = query.list();
List list2 = query2.list();
(3) 使用Query对象执行更新HQL。
HQL语句也可以实现更新和删除功能。Query接口中提供了执行更新HQL语句的方法:
public int executeUpdate();
executeUpdate方法可以执行更新HQL,包括update和delete操作。 代码如下:
Query query = session.createQuery(”update Customer set aget = 30 where custname=’ETC’);
query.executeUpdate();
Chapter 04
在实际开发过程中,往往一张表可能被映射到多个类,进行粒度的细分。由于目的不同,细分的方式也被分为两种,即基于设计的粒度设计和基于性能的粒度设计。
4.1 基于设计的粒度设计
如果表中的某些字段可以放在一起作为类的某个属性,例如用户信息中的多种联系方式,那么我们就可以将这些信息设计为一个类。
将表跟多个类映射,类和类之间使用关联关系,在映射文件中,使用component元素进行映射。
假设我们有一张表contactee,表中存储了联系人信息,我们将表中的address,tel,email, msn ,fax字段设计为类contactInfo。 ContactInfo 实例可以作为contactee的属性存在,表示联系方式信息。那么该表将与两个类映射,即Contactee以及ContactInfo类,其中ContactInfo是Contactee的属性。
4.2 基于性能的粒度设计。
如果表中的某些字段不常被使用,而且所占的空间较大,例如Student表中的字段image, image表示Student的照片,使用Blob类型,所占空间较大。如果在应用中,image字段不常被使用,那么可以将表Student映射为两个类,其中一个类是StudentDetail,映射表中所有字段;另一个是StudentBasic,映射表中除了image之外的字段。在一定程度上能保证性能和效率。
Chapter 05
数据库的关系表之间,最常见的是通过主键或者外键实现关联关系。
5.1 关联的方向与数量。
关联的方向可以分为单向关联和双向关联。如果表A,表B,我们可以通过表A的实例查询到对应的表B的实例,也可以通过表B的实例查询到对应的表A的实例,那就称为双向关联。
关联的数量分为一对一,一对多,多对一,多对多。如果表A实例可以有多个表B实例,表B实例也可以有多个表A实例,说明表A和表B存在多对多关系。
5.2 一对多/多对一
5.2.1 基于主外键的一对多/多对一关联
主表的hbm.xml中,使用 < one-to-many>
从表的hbm.xml中,使用 < many-to-one>
public class Address implements Serializable{
private Integer id;
private Person person; //声明了与Address实例关联的Person对象,其关联关系靠外键person_id 维护。
private String detail;
private String zipcode;
private String tel;
private String type;
}
public class Person implements java.io.Serializable{
private Integer id;
private String name ;
private Integer age;
private Set addresses = new HashSet(0); //用来存储与Person关联的多个Address实例
}
其中person是主表,address是从表,address表中的person_id是外键,参考主表的主键。person与address是一对多的关系。
Person.hbm.xml
<set name="addresses" inverse="true">
<key>
<column name="person_id"></column>
</key>
<one-to-many class="com.jonah.hbm.mapping.code.Address"></one-to-many>
</set>
Address.hbm.xml
<many-to-one name="person" class="com.jonah.hbm.mapping.code.Person" fetch="select" cascade="all">
<column name="person_id"/>
</many-to-one>
关联标记属性
简单介绍下面几个,除了name是必须,其余都是可选的。更多的我们参考官文档。
name为持久化对象的集合的属性名称
column外键的名称
class持久化类
关于inverse属性
控制反转,主要用在一对多,多对对双向关联上,inverse可以设置到集合上, 默认inverse为false。为true表示反转,由对方负责;反之,不反转,自己负责;如果不设,one和many两方都要负责控制,因此,会引发重复的sql语句以及重复添加数据。
关于cascade(级联)属性
级联的意思是指定两个对象之间的操作联动关系,对一个对象执行了操作之后,对其指定的级联对象也需要执行相同的操作
总共可以取值为:all、none、save-update、delete
all-代表在所有的情况下都执行级联操作
none-在所有情况下都不执行级联操作
save-update-在保存和更新的时候执行级联操作
delete-在删除的时候执行级联操作
5.2.2 基于连接表的一对多/多对一关联
Person.hbm.xml
<set name="addresses" table ="personaddress">
<key column="personid" />
Address.hbm.xml
<join table="personaddress">
<key column = "addressid" />
<many-to-one name="person" cascade ="all">
<column name="personid“ />
</many-to-one>
</join>
5.3 一对一关联
基于主键的一对一关联
基于唯一外键的一对一关联
5.4 多对多关联
通过连接表实现
5.5 关联映射配置文件
1. one-to-one
2. one-to-many
3. many-to-one
4. many-to-many
5 集合映射
5.6 连接查询
HQL中的连接查询分为隐式(implicit)和显式(explicit)两种。
隐式查询不使用join关键字,默认是 inner join的规则,即 内连接
显式查询 有inner join , left join ,right join.
左外连接查询左边实例的记录
右外连接查询右边实例的记录。
chapter 06 继承关系映射
6.1 实例准备
、某网店专门经营图书和DVD,由于它们之间存在共性,因此抽象出一个父类,为Product产品类,Book和DVD类基于Product类进行扩展。不管表如何设计,类的结构是基于面向对象得到的,可以保持不变。
6.2 TPS (Table Per SubClass)
当设计三张表,当主表与父类对应,从表分别对应子类时,可以使用TPS策略进行映射。只需要有一个父类的hbm.xml文件配置子类就可以完成映射。
采用属性属性进行配置,代码如下:
<joined-subclass name="Book" table="book">
<key column="id" />
<property name="book的属性”, column="book属性“ />
</joined-subclass>
<joined-subclass name="DVD" table="dvd">
<key column="id" />
<property name="DVD的属性”, column="DVD属性“ />
</joined-subclass>
使用key指定了子类的主键。用property指定了子类的属性。
6.3 TPH(Table Per Class Hierarchy)
当只设计一张表时,使用标识字段区分不同对象时,可以使用TPH策略进行映射。
使用discriminator字段,将category字段指定为区分值字段。
<discriminator column="category" type="java.lang.String" />
<subclass name="Book" table="book">
<property name="book的属性”, column="book属性“ />
</subclass>
6.4 TPC(Table Per Concrete Class)
如果设计两张表book和dvd,,那么在Product.hbm.xml可以这样配置。
<union-subclass name="Book" table="book">
<property name="book的属性”, column="book属性“ />
</union-subclass>
chapter 7
7.1 批量操作
批量操作时防止内存溢出的方法:
1. 使用Session的flush/clear方法
2. 使用StatelessSession接口
使用 Session 对象生成的session实例,有可能发生内存溢出异常。这是因为Hibernate将需要插入到数据库的数据存到了session级别的缓存区,当缓存区满了,也就发生了 OutOfMemoryError,也就是内存溢出异常。
使用方法1.2 可以避免。
7.2 延迟加载
当某实例有关联实例时,hibernate中默认使用延迟加载。即默认情况下,不查询关联实例,可以配置
lazy=”false” ,取消延迟加载。实际开发中,往往都使用延迟加载,以保证性能。
7.3 batch-size 属性
N+1 查询问题中,当N值较大时,将降低性能。 batch-size属性可以定义批处理的实体个数,建议取值在5-30之间,能减少查询语句条数,从而提高性能。