Hibernate 学习笔记
hibernate的功能是为了满足在数据库和面向对象的软件之间交换数据的需要。
Hibernate的特点:
1,ORM(对象关系映射)功能的框架实现,能用对象的概念来处理关系数据库。
2,功能强大,高效。提供了对象关系映射,对象查询语言HQL,和一套Criteria(标准) API等功能。
3,有丰富的成功案例和文档资料。
Hibernate的核心优点是封装了jdbc。
Hibernate的开发步骤:-à搭建开发环境(eclipse)
1.设计持久类(po):先写持久类(包:三部分:域名。项目名称。模块)如:com.tarena.ebank.biz.entity
2.设计映射文件(hbm)
3.构造表结构(scheme)
4.调用hibernate
持久类(pojo 普通java对象):
1. 有空的构造方法(hibernate要new无参数的构造),否则hibernate无法实例化yield对象
2. 所有的属性必须有setter/getter方法
Why use OId?
3.持久类最好有一个中性的对象唯一标识符(主键).将对象存到数据库相当于存到表里,存到表里就要有主键。
持久类必须有一个具有唯一标识符的属性,因为数据库中表的id唯一(类中定义一个private Long oid(中性的,后加的,不需要提供set,get方法),对应表中的id)
Hbm.xml编码规范:
映射文件中写的是,账户类中信息存储在那个表中,并且是怎样和表对应的:对应关系
Account类 DB中的表
oid OID
acctNO ACCTNO
bal BALANCE
1.一个映射文件映射一个持久类。
2.映射文件名字与他所映射的持久类的名字保持一致。如Account和Account.hbm.xml
3.映射文件与他所映射的持久类放在同一个包路径下。
类对表,属性对字段,对象对记录
类对应表用class来说明,属性对应字段用column来说明。主键对象唯一标识符用id来说明,在id中hibernate要指定一个算法,这里的算法很多。
hilo算法特殊:要给一个永久提供唯一id的表,我们必须给表的名和字段名(列名),
<param name="hilo_t"></param>
<param name="column"></param>
实质是传给算法的两个常数
如:hilo(hight-low高低为算法)他的原理是:产生很多值去对应oid,而且这些数据都不能重复,用计数器来分配这些数字,因为当拿走一个数字的时候计数器就会加1。如果计数器私有的话,计数器就会归零这样不能保证计数器的不重复性,要解决这个问题需要将计数器放在数据库中公用(在数据库中放一个一行一列的表)。但这样产生的问题是对数据库的访问频率增多,对数据库压力增大。那么怎么做能够降低数据库的压力呢,也就是减少访问次数,提高取回来数字的利用率?Q:在应用程序端也加入一个计数器,从数据库中去一个数字比如说1,应用程序端的计数器就会变为01000000这样的话在应用程序端取一个数字的话计数器机会加1,如去第一次变为01000000,去第二次变为010000001这叫高低位算法,在数据库中建立一个持久计数器(作用:在软件的生命周期中保证全部的数字不重复),在应用程序端建立一个临时计数器(作用:在一个时间段中保证数字不重复)。他们的关系就像名字和姓的关系,持久计数器相当于姓,临时计数器相当于名字
Hilo算法很常用如:电话号码86-020-23878748237,身份证号码等。
Dialect:方言。
SessionFactory sf = new Configuration()
.configure()
.buildSessionFactory();
sf.openSession();
Hibernate的核心接口一共有5个,分别为:Session、SessionFactory、Transaction、Query和Configuration。
这5个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。
下面对这五的核心接口分别加以介绍。
(1)Session接口:Session接口负责执行被持久化对象的CRUD操作(CRUD(Create/Read/Update/Delete,CRUD)的任务是完成与数据库的交流,包含了很多常见的SQL语句)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。
这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSesion对象称为用户session。
(2)SessionFactory接口:SessionFactroy接口负责初始化Hibernate。并负责创建Session对象。
这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,
一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
多线程中一个线程用一个session,一个session一个事务。
(3)Configuration接口:Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。
在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。程序中只需调用一次Configuration
(4)Transaction接口:Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。
(5)Query和Criteria接口:Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。
Configuration().configurate()使用名为hibernate.cfg.xml中映射和应用程序资源指定的属性。
Configuration: configure();读配置文件,他返回的是所调用的对象的引用。
Configuration:读配置文件和映射文件,这个函数在一个类中只读一次。
ID生成策略
increment identity sequence hilo seqhilo uuid guid native assigned select foreign
Assigned如果主键值方式没有指定的话就会默认assigned
Sequence:oracle指定的计数器(seqhilo),默认的计数器为hibernate_sequence。
如果像在mysql中像要产生oracle的主键值就要用到identity。用native可以让hibernate来判断用mysql还是用oracle。
UUID中有128位二进制数,这个数字全世界唯一经过处理后可以变为32位字符串。
重点掌握:native和sequence
String对varchar是字符串在hibernate中的映射
属性对字段---属性的值对字段的类型。值无所谓关键是要说明属性的类型。
Property-type=”java.lang.String” column-type=”varchar” 简写:type=”string” 说明这个会加快hibernate的运行速度,可读性较高。
总结:映射类型是java类型和数据库类型的配对关系
单一实体映射(基础)
配置文件:
头信息
<property name="show_sql">true</property>在第一行,一执行就打印sql
四个连接数据库的基本信息
Connection.isolation 2 代表防止涨读,可写可不写
name="dialect" :方言(—DB类型—)
resource的值就是用到的映射文件的位置
注意在hibernate中的异常都是非捕捉异常
Hibernate中的对象:
对象状态:(一组属性的特定取值)
1. 持久对象的状态-->PO外在的状态 (OOAD中 对象的状态-->PO内在的状态)
(1)Transient 暂时状态: po 和Session无关,数据库中也无该对象的记录
不受session 管理的,在对象中可表现为持久化对象的主键值没有 save()
Persistent 持久状态: po 和Session有关,数据库中有该对象的记录
进入了session 的管理,session会自动进行同步。
Detached 游离/脱管态:po和Session无关,数据库中有该对象的记录
脱离了session 的管理,但数据库有与其对应的记录,所以还可以进入持久化状态。他的主键
注:和Session关系,即Session中有该对象的副本和该对象的引用
持久化对象: 即就是在数据库中存有其相对应数据的对象,并且在内存中也有这个对象,内部状态和外部状态同步, 这个对象在Session的管理范围内,也就是调用Session.save()方法同步到数据库的对象。对持久态的对象做修改,不调用update方法在提交的时候也会自动更新将数据同步到数据库中。
临时对象: 即在内存中刚刚创建(new)的对象,还没有同步到数据库,或者是调用Session.delete(),数据库中信息被删除了的对象。
游离对象: 也就是在数据库中有和该对象向对应的纪录,并且在内存中的也存在该对象,但是不在Session的管理范围之内,例如:在使用load()、get()方法查询到数据并封装为对象之后,将Session实例关闭,则对象由Persistent状态变为Detached状态。就不会在将其改变同步到数据库
在这里强调一下暂态和游离态的区别:暂态与数据库中的记录无关,而游离态与数据库中的记录是相关。持久态的对象调用了delete()方法后尽管数据库中的主键还是存在的,但是它已经跟对象无关了,对象在数据库中的记录已经不存在了。所以持久态对象调用了delete()方法不是游离态,而是暂态
2. 状态转换图:
Session的get()和load()的区别在于:当要查找的对象数据不存在时,load()方法就是直接抛出异常,而get()方法则返回null值 ,(常考的面试题)
批量操作:相当于JDBC里面的批量更新,调用session.flush(),会刷新缓存,强制执行数据库执行sql语句,将session缓存清空。在做commit之前数据库也会隐藏的执行一次flush(),相当于批量更新
1. 持久对象程序再启动的时候还可以再读出来,持久态的对象是自动同步的。
Transient(暂态):对象在内存中已经被创建但还未存入DB。对象刚new出来,在数据库中什么都没有的状态。这个时候很危险,如果掉线或断电数据就会丢失,这是很可怕的。
Persistent(持久态):对象在内存中存在并且存在了数据库中。Session的缓存中有当前对象的克隆体(副本),所以可以实现自动同步。
Detached(游离态):对象已经存在数据库中但是和section无关。Session的缓存中没有当前对象,不能实现同步,这就需要手工同步。持久态变游离态叫脱钩,一般不会用section.close来关闭section,这里仅仅是关闭section而不是删除了section。Section.clear留了section但留了section。Evict()经常用到只删除一个对象。
2. 非持久对象:关机既消失
Get() 和load()方法工作原理的不同:get如果查到对象会返回给你,查不到的话返回null,load如果查到对象返回值如果查不到会返回异常。
过程:load不去数据库,造代理对象(看起来像你要的值实际上是空值)
Get会去数据库但是他自己不会产生代理,get方法自身不会产生延迟加载(hibernate的特性),load会产生。
load--出缓存找->yes->return object
->no->return proxy(代理对象,不去数据库找,因为要是查到而不去用,就什么也不会发生,要是使用,就去数据库找,找不到就抛异常)
proxy-使用DB中查找->yes-->proxy 填充
->no -->异常
load延迟加载,可以提高效率 是Hibernate提高效率的手段,延迟加载通过代理对象来完成的。
get是立即加载
如何选择load和get?
我们在迫不得已时(延迟加载不安全时)用get,但延迟加载安全时我们都用load
确定对象已经存在用load,不确定对象是否存在用get
hibernate 中持久状态改变时,不会立即被更新到数据库中。这样我们就可以将其优化后更新到数据库中
调用flush:代表同步的时刻
when a transaction is committed(隐含的) 当一事务提交
sometimes before a query is executed(隐含的) 有时在执行查询之前
when the application calls session.flush() explicitly(自己调flush) 当应用程序调用Session.flush()的明确
Flush的执行:
在事务提交前的一瞬间可能会有某些隐含的flush,
当某些查询之前可能会有某些隐含的flush。
Session.flush()。
对现存的不满,我们可以改:
设置flushMode 三个常量
:flushMode.AUTO(用)
flushmode.COMMIT(将查询前的覆盖掉)
flushMOde.NEVER:显示调用flush是才会被调用,将flush的权利收回
Flush mode:FlushMode.AUTO(默认,自动化程度最高,代码的使用对flush没有什么控制) FlushMode.COMMIT(查询前调用),FlushMode.NEVER(除非你调用否则不会被调用,如果像非常精确的控制flush可以考虑用他)。
我们和持久对象打交道的session,中有哪些函数可以调用?
总结:
1、 transient,persistent,detached 的概念:
transient 暂态:db中无记录入session无关
persistent db 有记录,与session有关
detached 有 无关
对象状态:持久(怎样持久,持久没有),同步
2、持久对象的管理(状态转换,when,how持久,同步)及工具(session)
3、get、load(延迟加载 proxy)
4、游离态的使用(分布)
5、flush 同步控制和优化
Batch:批量更新,如果做一个十万次的循环,每存一个对象都会在缓存中存入一个对象,也就说缓存要存放十万个副本这样就会导致缓存溢出。解决这个问题的方法是:每存一批就清空缓存中的前一批。 所有的数据库操作都是在提交之前做的。所以要用flush函数在提交前还没有提交的操作存入数据库中。
? 一些复杂的映射(重点):
实体:指账户,订单等。
实体关系:是指这些实体之间的关系。
1.关联关系:
一个类的一个对象A用到了另外一个对象B的某些功能 A use B,B use A
关联关系的内涵是:use,继承关系的内涵是is。A use B,B use A这两种方式是不一样的,他们关系的方向不同
1)如果是A 到B的关系表示为A---àB,如果是B到A的关系表示为A<-----------B,如果是A和B的双向关系A-----B。
2)基数关系(基于数量的关系):一对多,一对一,多对一,多对多。这中映射方式主要考虑数量和方向。基数关系=1:1/1:M/M:1/N:M+方向,
#1:1(单向,双向)
外键的约束是在外键所在的表中定义,在主键所在的表中没有外键信息。
<property name="hbm2ddl.auto">create</property>表示从映射文件自动生成新表,而且会删除旧表
在一对一中关系的维护方在外键方,如果外键方看不到关系则关系会被忽略但不会出异常,主键可以看到他们的关系但他不维护关系;
cascade:指明哪些操作会从父对象级联到关联的对象(可选)
property-ref:指定到关联类的一个属性,这个属性将会和表外键相对应。如果没有指定,会使用关联类的主键(可选).
many-to-one:外键是天生的多对一的体现
name:属性名。指出many一方的类用哪个属性和one一方的类关联
column:字段名(可选).指出many一方的类对应的数据表用哪个列和one一方的类对应的数据表关联(两表之间存在外键关联);
unique:允许产生外键列唯一约束的数据库定义语言(DDL)(可选)
constrained="true"给hibernate说明这里的one-to-one不是主键是外键
3)组件关系:单一组件,组件集合。组件关系主要考虑强度
2.继承关系(有三种方案)