Java相关课程系列笔记之十四Hibernate学习笔记
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Hibernate学习笔记
Java相关课程系列笔记之十四
笔记内容说明
Hibernate(梁建全老师主讲,占笔记内容100%);
目录
一、Hibernate的概述 (1)
1.1 Hibernate框架的作用 (1)
1.2 Hibernate访问数据库的优点 (1)
1.3 JDBC访问数据库的缺点 (1)
1.4 Hibernate的设计思想 (1)
二、Hibernate的基本使用 (2)
2.1 Hibernate的主要结构 (2)
2.2 Hibernate主要的API (2)
2.3 Hibernate使用步骤 (2)
2.4 HQL语句(简要介绍) (6)
三、数据映射类型 (7)
3.1映射类型的作用 (7)
3.2 type映射类型的两种写法 (7)
四、Hibernate主键生成方式 (8)
4.1五种生成方式 (8)
五、Hibernate基本特性 (9)
5.1对象持久性 (9)
5.2处于持久状态的对象具有的特点 (9)
5.3三种状态下的对象的转换 (9)
5.4批量操作:注意及时清除缓存 (9)
5.5案例:三种状态下的对象使用 (10)
5.6一级缓存机制(默认开启) (10)
5.7一级缓存的好处 (10)
5.8管理一级缓存的方法 (10)
5.9延迟加载机制 (11)
5.10具有延迟加载机制的操作 (11)
5.11常犯的错误 (12)
5.12延迟加载的原理 (12)
5.13 Session的get和load方法的区别 (12)
5.14延迟加载的好处 (12)
5.15案例:测试延迟加载 (12)
5.16案例:重构NetCTOSS资费管理模块 (13)
5.17 Java Web程序中如何用延迟加载操作(OpenSessionInView) (15)
六、关联映射 (18)
6.1一对多关系one-to-many (18)
6.2多对一关系many-to-one (19)
6.3多对多关联映射many-to-many (19)
6.4关联操作(查询join fetch/级联cascade) (21)
6.5继承关系映射 (24)
七、Hibernate查询方法 (27)
7.1 HQL查询 (27)
7.2 HQL和SQL的相同点 (27)
1
7.3 HQL和SQL的不同点 (27)
7.4 HQL典型案例 (27)
7.5 Criteria查询 (30)
7.6 Native SQL原生SQL查询 (31)
八、Hibernate高级特性 (32)
8.1二级缓存 (32)
8.2二级缓存开启方法及测试 (32)
8.3二级缓存管理方法 (33)
8.4二级缓存的使用环境 (33)
8.5查询缓存 (33)
8.6查询缓存开启方法及测试 (33)
8.7查询缓存的使用环境 (33)
九、Hibernate锁机制 (34)
9.1悲观锁 (34)
9.2悲观锁的实现原理 (34)
9.3悲观锁使用步骤及测试 (34)
9.4乐观锁 (35)
9.5乐观锁的实现原理 (35)
9.6乐观锁使用步骤及测试 (35)
十、其他注意事项 (36)
10.1源码服务器管理工具 (36)
10.2利用MyEclipse根据数据表自动生成实体类、hbm.xml (36)
10.3根据实体类和hbm.xml生成数据表 (37)
10.4 Hibernate中分页查询使用join fatch的缺点 (37)
10.5 Hibernate的子查询映射 (38)
2
一、Hibernate的概述
1.1 Hibernate框架的作用
Hibernate框架是一个数据访问框架(也叫持久层框架,可将实体对象变成持久对象,详见第5章)。
通过Hibernate框架可以对数据库进行增删改查操作,为业务层构建一个持久层。
可以使用它替代以前的JDBC访问数据。
1.2 Hibernate访问数据库的优点
1)简单,可以简化数据库操作代码。
2)Hibernate可以自动生成SQL,可以将ResultSet中的记录和实体类自动的映射(转化)。
3)Hibernate不和数据库关联,是一种通用的数据库框架(支持30多种数据库),可以方便数据库移植。
任何数据库都可以执行它的API。
因为Hibernate的API中是不涉及SQL 语句的,它会根据Hibernate的配置文件,自动生成相应数据库的SQL语句。
1.3 JDBC访问数据库的缺点
1)需要编写大量的复杂的SQL语句、表字段多时SQL也繁琐、设置各个问号值。
2)需要编写实体对象和记录之间的代码,较为繁琐。
3)数据库移植时需要修改大量的SQL语句。
1.4 Hibernate的设计思想
Hibernate是基于ORM(Object Relation Mapping)思想设计的,称为对象关系映射。
负责Java对象和数据库表数据之间的映射。
Hibernate是一款主流的ORM工具,还有其他很多ORM工具,如:MyBatis(以前叫iBatis)、JPA。
Hibernate功能比MyBatis强大些,属于全自动类型,MyBatis属于半自动。
但全自动会有些不可控因素,因此有些公司会用MyBatis。
ORM工具在完成Java对象和数据库之间的映射后:
1)在查询时,直接利用工具取出“对象”(不论是查询一条记录还是多条记录,取出的都是一个个对象,我们不用再去转化实体了)。
2)在增删改操作时,直接利用工具将“对象”更新到数据库表中(我们不用再去把对象转成数据了)。
3)中间的SQL+JDBC细节,都被封装在了工具底层,不需要程序员参与。
◆注意事项:
❖Java程序想访问数据库,只能通过JDBC的方式,而Hibernate框架也就是基于ORM思想对JDBC的封装。
❖Hibernate是以“对象”为单位进行数据库的操作。
1
二、Hibernate的基本使用
2.1 Hibernate的主要结构
1)hibernate.cfg.xml(仅1个):Hibernate的主配置文件,主要定义数据连接参数和框架设置参数。
◆注意事项:就是个xml文件,只是名字比较奇葩!
2)Entity实体类(n个,一个表一个):主要用于封装数据库数据。
3)hbm.xml映射文件(n个):主要描述实体类和数据表之间的映射信息。
描述表与类,字段与属性的对应关系。
2
<property name="connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
<!-- 框架参数,将hibernate底层执行的SQL语句从控制台显示-->
<property name="show_sql">true</property>
<!-- 格式化显示的SQL -->
<property name="format_sql">true</property>
<!-- 指定映射描述文件-->
<mapping resource="org/tarena/entity/Cost.hbm.xml" />
</session-factory>
</hibernate-configuration>
◆注意事项:应该放在源文件的src目录下,默认为hibernate.cfg.xml。
文件内容是
Hibernate工作时必须用到的基础信息。
private Integer id; //资费ID private String feeName; //资费名称
private Integer baseDuration; //基本时长private Float baseCost; //基本定费
private Float unitCost; //单位费用private String status; //0:开通;1:暂停;
private String descr; //资费信息说明private Date createTime; //创建日期
private Date startTime; //启用日期private String costType; //资费类型
……getter/setter方法
属性和对应的getter/setter方法,而没有任何业务逻辑方法的类。
这种类最多再加入
equals()、hashCode()、toString()等重写父类Object的方法。
不承担任何实现业务逻
辑的责任。
3
step5:编写hbm.xml映射(文件)描述信息:映射文件用于指明POJO类和表之间的映射关系(xx属性对应xx字段),一个类对应一个映射文件。
例如:Cost.hbm.xml内容如下:
❖映射文件默认与POJO类放在一起;命名规则为:类名.hbm.xml。
❖hbm.xml中已写出的属性与字段的映射要一一对应,若表中没有某个字段,却写了映射关系,则报错:找不到实体类。
step6:利用Hibernate API实现DAO
1)新建HibernateUtil类,用于封装创建Session的方法。
如下:每个用户会对应一个Session,但是SessionFactory是共享的。
4
5
2.4 HQL语句(简要介绍)
简要介绍见2.3节中step6中的3)特殊查询(本页最上)。
详细介绍见第七章。
6
三、数据映射类型
hbm.xml在描述字段和属性映射时,采用type属性来指定映射类型。
3.1映射类型的作用
主要负责实现属性和字段值之间的相互转化。
3.2 type映射类型的两种写法
1)指定Java类型,例如:ng.String、ng.Integer …,不能写成String …
2)指定Hibernate类型,例如:
①整数:byte、short、integer、long;
②浮点数:float、double;③字符串:string;
④日期和时间:date(只处理年月日),time(只处理时分秒),timestamp(处理年月日时分秒);
⑤布尔值:true/false<-yes_no->char(1)(数据库存Y/N,显示时自动转为true/false)、true/false<-true_false->char(1)(数据库存T/F,显示时自动转为true/false)、
true/false<-boolean->bit(数据库存1/0,显示时自动转为true/false);
⑥其他:blob(以字节为单位存储大数据)、clob(以字符为单位存储大数据)、
big_decimal、big_integer;
◆注意事项:Hibernate类型都是小写!建议使用Hibernate类型。
3)所以2.3节中step5的Cost.hbm.xml内的type也可这样写:
<property name="name" type="string"><column name="NAME" /></property>
<property name="baseCost" type="float"><column name="BASE_COST" /></property>
<property name="startTime" type="date"><column name="STARTIME" /></property>
◆注意事项:
❖java.util.Date有年月日时分秒毫秒,但如果用date映射,则只把年月日存进数据库;java.sql.Date只有年月日。
java.sql.Timestamp有年月日时分秒毫秒。
❖若在页面显示按特定格式显示则用Struts2标签:
<s:date name="属性名" format="yyyy-MM-dd HH:mm:ss"/>
四、Hibernate主键生成方式
Hibernate负责管理主键值。
它提供了多种主键生成方式。
4.1五种生成方式
1)sequence:可以按指定序列生成主键值。
只适用于Oracle数据库。
不担心并发量!
2)identity:按数据库自动增长机制生成主键值。
一般适用于MySql、SQLServer数据库。
五、Hibernate基本特性
5.1对象持久性
在Hibernate使用过程中,实体对象可以具有以下三种状态:
1)临时状态:采用new关键字创建的对象,该对象未与Session发生关联(未调用Session的API)。
也叫临时对象。
临时状态的对象会被Java的垃圾回收机制回收。
2)持久状态:实体对象与Session发生关联(调用了Session的get、load、save、update 等API)。
也叫持久对象。
3)游离状态:原来是持久状态,后来脱离了Session的管理。
如:Session被关闭,对象将从持久状态变为游离状态,同时垃圾回收机制可以回收掉,不再占用缓存空间了。
5.2处于持久状态的对象具有的特点
1)对象生命期持久,垃圾回收机制不能回收。
2)对象的数据可以与数据库同步(即对象中的数据发生改变,则数据库中的数据自动同步)。
由Session对象负责管理和同步。
3)对象在Session的一级缓存中存放(或者说在Session缓存中的对象都是持久对象)。
注意事项:Session.close();有两个作用:①关闭连接、释放资源②使对象变为游离状态,当对象的引用不存在时,对象才被回收。
没被回收时,对象中的数据还在!
5.3三种状态下的对象的转换
5.4批量操作:注意及时清除缓存
5.5案例:三种状态下的对象使用
当持久对象数据改变后,调用session.flush()方法,会与数据库同步更新。
commit()方法,内部也调用了flush()方法,因此使用commit()方法可以省略flush()方法的调用。
该缓存区可以存储当前Session关联的持久对象。
只有在缓存区中,Session才管该对象。
5.7一级缓存的好处
Hibernate在查询时,先去缓存当中查找,如果缓存中没有,才去数据库查询。
如果利用Session对同一个对象查询多次,第一次去数据库查,后续的会从缓存查询,从而减少了与数据库的交互次数。
5.8管理一级缓存的方法
1)session.evict()方法:将对象清除。
2)session.clear()方法:清除所有对象。
3)session.close()方法:清除所有对象,并关闭与数据库的连接。
◆注意事项:不同的session的缓存区不能交叉访问。
4)案例:测试一级缓存
5.10具有延迟加载机制的操作
1)session.load();//延迟加载查询,session.get();是立即加载查询
2)query.iterator();//查询
3)获取关联对象的属性信息
◆注意事项:这些方法返回的对象,只有id属性(主键)有值,其他属性数据在使用
的时候(调用getXXX()方法时,除主键外,调主键get方法不会发SQL)才去获取。
5.11常犯的错误
1)报错:LazyInitializationException:could not initialize proxy - no Session,原因:代码中使用了延迟加载操作,但是session在加载数据前关闭了。
只要看到这个类名:LazyInitializationException就都是session过早关闭,后面的描述可能不同。
2)报错:NonUniqueObjectException: a different object with the same identifier value was already associated with the session,原因:有两个不同对象,但是主键,即id却相同。
例如:
就完全相同了。
5.14延迟加载的好处
1)提高了内存的使用效率。
2)可以使数据访问降低并发量。
5.15案例:测试延迟加载
5.16案例:重构NetCTOSS资费管理模块
◆注意事项:
❖实体类和hbm.xml必须保持一致!列名写错则会报:不能读取实体类。
❖junit测试右键点Copy Trace查看错误列。
step3:借用2.3节中step6的HibernateUtil类
step4:按CostDAO接口重构一个DAO实现组件:HibernateCostDAOImpl
step4:将5.16案例中step4中的HibernateCostDAOImpl类里的所有方法中的开启事务、提交事务、关闭Session全部删除。
②利用Filter过滤器。
③利用Spring的AOP机制。
17
六、关联映射
关联映射主要是在对象之间建立关系。
开发者可以通过关系进行信息查询、添加、删除和更新操作。
如果不使用Hibernate关联关系映射,我们也可以取到用户对应的服务。
而Hibernate提供的关联映射,更方便一些。
step5:新建TestOneToMany.java类用于测试
18
6.2多对一关系many-to-one
step1:新建项目,导入Hibernate开发包,借用NetCTOSS项目中的实体:Account和Service,配置hibernate.cfg.xml和两个实体的hbm.xml映射文件。
step2:可多个Service服务对应一个Account帐号,所以为多对一关系。
因此为N方Service 实体类添加Account属性,以及对应的get/set方法。
数据库设计是采用3张数据表,有一张是关系表。
例如:
ADMIN_INFO-->ADMIN_ROLE<--ROLE
中间的关系表不用映射,只映射两端的表。
但如何对关系表进行操作呢?
答:Hibernate会自动的通过已经映射的表,进行关系表操作。
◆注意事项:
❖多对多关系是默认的级联操作,可加cascade属性,但是加了之后相当于进入一个循环,若删除A数据,则所有表中有A的数据则都被删除!因此一般都是
采用一对多或多对一,从而破坏这个循环。
详情可看6.4节。
❖多对多关系一定有第三张表,间接实现多对多关系,两表之间不可能有多对多
19
关系!
案例1:step1:在Admin实体类中添加一个Set集合属性
step2:在Admin.hbm.xml中定义属性的映射描述
简单说明:
20
step3:在TestManyToMany类中添加方法,用于测试
21
step2:新建TestCascade类,用于测试级联操作
22
Hibernate级联删除的缺点:delete是按id(主键)一条一条删的,不是按关系字段删的,当数据量小时可用Hibernate的级联删除,简单方便些。
但是,但当数据量大时,Hibernate的级联删除效率低,则不建议使用Hibernate的级联删除,建议采用HQL语句的方式,例如:
❖级联删除,不写inverse="true",且数据库中SERVICE_CHANG表中的ACCOUNT_ID为NOT NULL约束,那么程序最后会执行update,会设置
ACCOUNT_ID=null,那么将与数据库冲突!报错!所以,应当加上
23
inverse="true"。
6.5继承关系映射
可以将数据表映射成具有继承关系的实体类。
Hibernate提供了3方式的继承映射。
1)将一个表映射成父类和子类:采用<subclass>描述子类。
2)将子类表映射成父类和子类(无父类表):采用<union-subclass>描述。
step3:添加映射文件:Product.hbm.xml、Book.hbm.xml、Car.hbm.xml
1)Product.hbm.xml
24
25
26
七、Hibernate查询方法
7.1 HQL查询
Hibernate Query Language简称HQL。
HQL语句是面向对象的一种查询语言。
HQL针对Hibernate映射之后的实体类型和属性进行查询(若出现表名和字段名则为错误的!)。
7.2 HQL和SQL的相同点
1)都支持select、from、where、group by、order by、having子句……。
2)创建PersonKey实体,用作Person实体的联合主键
step3:添加Person.hbm.xml映射文件
7.6 Native SQL原生SQL查询
1)基本使用:
操作也可用原生SQL。
2)案例:创建TestSQLQuery类,用于测试原生SQL查询(同样借助以前的实体和映射)
八、Hibernate高级特性
8.1二级缓存
二级缓存是SessionFactory级别的。
由SessionFactory对象负责管理。
通过Factory创建的Session都可以访问二级缓存。
二级缓存默认是关闭的。
如果二级缓存打开,则先去一级缓存找对象,找不到则去二级缓存,还找不到则访问数据库。
◆注意事项:
❖一级缓存和二级缓存都是存“单个对象”的!不能存集合、数组!
8.3二级缓存管理方法
1)SessionFactory.evict(class);//清除某一类型的对象
2)SessionFactory.evict(class,id);//清除某一个对象
3)SessionFactory.evict(list);//清除指定的集合
8.7查询缓存的使用环境
1)共享数据的结果集,结果集数据量不应太大(几十到几百条即可)。
2)查询的结果集数据变化非常小(最好不要变化,几天变一次还可接受,先清原来的缓存再重新载入)。
九、Hibernate锁机制
Hibernate提供了乐观锁和悲观锁机制,主要用于解决事务并发问题。
9.1悲观锁
Hibernate认为任何操作都可能发生并发,因此在第一个线程查询数据时,就把该条记录锁住。
此时其他线程对该记录不能做任何操作(即增删改查四种操作都不能)。
必须等当前线程事务结束才可以进行操作。
9.2悲观锁的实现原理
step5:创建TestTrain类,用于模拟并发操作
step4:将9.3节step4中的ThreadClient类,改为不加锁的load方法
step5:再次执行9.3节step5中的TestTrain类
执行结果:先提交的事务执行成功,后提交的事务执行失败,报错信息为:
十、其他注意事项
10.1源码服务器管理工具
今后工作中会使用到的源码服务器(或叫版本服务器)管理工具:
1)CVS 常用乐观锁机制2)SVN 常用乐观锁机制
3)VSS 用的少悲观锁机制
10.2利用MyEclipse根据数据表自动生成实体类、hbm.xml step1:进入DB Browser,建立一个与数据库的连接
step2:新建工程,右键工程-->MyEclipse-->Add Hibernate Capbilities
step3:弹出添加Hibernate开发环境界面:
第一个界面选择Hibernate版本,然后点Next;
◆注意事项:第一个界面中Select the libraries to add to the buildpath 是选择要导入的
Hibernate的Jar包,有两种选择:MyEclipse自带的“MyEcipse Libraries”,还有自己导入的“User Libraries”,如果已经手动将所有Jar包拷贝到了lib下,不用再导入了。
那么我们都不选,去掉“MyEcipse Libraries”前面的checkbox的对勾。
第二个界面添加hibernate.cfg.xml主配置文件(可默认),然后点Next;
第三个界面在DB Driver下拉菜单中选择第一步建立的连接,然后点Next;
第四个界面创建HibernateSessionFactory类,其实就是我们写的HibernateUtil,然后点击Finish。
◆注意事项:主配置文件中:
❖对于MySql连接,连接字符串中如若有&符号,需要写成&
❖<property name="myeclipse.connection.profile">
oracle.jdbc.driver.OracleDriver</property>为链接名,可以删掉。
step4:在工程中,新建一个package,用于存放实体和hbm.xml
step5:进入DB Browser选中需要生成的数据表,右键选择Hibernate Reverse Engineering...
第一个界面选择生成hbm.xml和POJO(实体类),及其存放路径,然后点Next;
第二个界面选择Type Mapping为Hibernat types(其他设置可默认),然后点Next;
第三个界面选择数据表,可以设定映射的POJO类名(包名.类名)和主键生成方式,选择字段可以设置属性名和映射类型(要改,否则不符合Java命名规范),最后点击Finish。
第四个界面,改名为HibernateUtil
◆注意事项:第一个界面有个Create abstract class,创建抽象类。
意思为:
public class Foo extends AbstractFoo implements java.io.Serializable{
//里面为构造方法,没有属性和getter/setter方法} public abstract class AbstractFoo implements java.io.Serializable{
//里面为属性和getter/setter方法}
第一个界面:用以前的实体类方式,所
以不选择Create abstract class
第三个界面:类名为:包名.类名
第三个界面:每个字段建议都去改名字,
否则生成的属性名不符合Java命名规范
step6:完成后,实体类、hbm.xml和hibernate.cfg.xml都已生成、修改好了。
◆注意事项:
❖Hibernate生成的映射文件会有一些问题,最好再修改一下。
❖可以按“Ctrl”键选择多个表,之后步骤相同,可以同时创建多个表的POJO类和映射文件。
10.3根据实体类和hbm.xml生成数据表
利用Hibernate框架自带的hbm2ddl工具,根据实体类和hbm.xml生成数据表。
step1:在hibernate.cfg.xml中添加开启工具的设置
<!-- 根据实体类和hbm.xml生成数据表-->
<property name="hbm2ddl.auto">update</property>
10.4 Hibernate中分页查询使用join fatch的缺点
使用Hibernate分页查询时,若语句中使用了join fatch语句,则由“假分页”机制实现。
实际采用可滚动ResultSet游标实现的数据抓取。
最后的显示结果一样,但是SQL语句不同。
37
假分页SQL语句:select * from bill,返回大量数据,然后利用游标result.absolute(begin);即跳着查询,而result.next();为下一条记录。
真分页SQL语句:MySql:select * from bill limit ?,?
Oracle:select ... (select ... rownum<?) where rownum>?
那么假分页的结果就是,一次性把数据全部取出来放在缓存中,比较适合小数据量,如果数据量大,对内存压力比较大。
所以,建议join fatch语句不要和分页查询一起用!
“假分页”详情可看JDBC笔记9.5节。
10.5 Hibernate的子查询映射
38。