Hibernate的一些关键知识

================一些hibernate资料============================
org.hibernate.cfg.Configuration类的作用: 读取hibernate配置文件(hibernate.cfg.xml或hiberante.properties)的.
 new Configuration()默认是读取hibernate.properties,所以使用new Configuration().configure();来读取hibernate.cfg.xml配置文件,ibernate.cfg.xml也可以是别的文件名字.SessionFactory是线线程安全的。
session用完后,必须关闭。
session是非线程安全,一般是一个请求一个session.
hibernate操作对象的好处:我们不用写那么多的JDBC代码,只要利用session操作对象,至于hibernat如何存在对象,这不需要我们去关心它,
======================
Annotation版本的HellWorld
 在hibernate.cfg.xml中建立映射<maping class=…/><mapping class="com.wjt276.hibernate.model.Teacher"/>
不是使用org.hibernate.cfg.Configuration来创建Configuration,而使用org.hibernate.cfg.AnnotationConfiguration来创建Configuration,这样就可以使用Annotation功能。
Configuration cfg = new AnnotationConfiguration();
SessionFactory sf = cfg.configure().buildSessionFactory();
ORM是通过使用描述对象和数据库之间映射的元数据,将Java程序中的对象自动持久化到关系数据中。本质上就是将数据从一种形式转换到另外一种形式。


三、 Hibernate做什么:
1、 就是将对象模型(实体类)的东西存入关系模型中,
2、 实体中类对应关系型库中的一个表,
3、 实体类中的一个属性会对应关系型数据库表中的一个列
4、 实体类的一个实例会对应关系型数据库表中的一条记录。
%%将对象数据保存到数据库、将数据库数据读入到对象中%%
阻抗不匹配---例JAVA类中有继承关系,但关系型数据库中不存在这个概念这就是阻抗不匹配。Hibernate可以解决这个问题。
四、 Hibernate存在的原因:
1、 解决阻抗不匹配的问题;
2、 目前不存在完整的面向对象的数据库(目前都是关系型数据库);
3、 JDBC操作数据库很繁琐
4、 SQL语句编写并不是面向对象
5、 可以在对象和关系表之间建立关联来简化编程
6、 O/R Mapping简化编程
7、 O/R Mapping跨越数据库平台
五、 Hibernate的优缺点:
1、 不需要编写的SQL语句(不需要编辑JDBC),只需要操作相应的对象就可以了,就可以能够存储、更新、删除、加载对象,可以提高生产效;
2、 因为使用Hibernate只需要操作对象就可以了,所以我们的开发更对象化了;
3、 使用Hibernate,移植性好(只要使用Hibernate标准开发,更换数据库时,只需要配置相应的配置文件就可以了,不需要做其它任务的操作);
4、 Hibernate实现了透明持久化:当保存一个对象时,这个对象不需要继承Hibernate中的任何类、实现任何接口,只是个纯粹的单纯对象—称为POJO对象(最纯粹的对象—这个对象没有继承第三方框架的任何类和实现它的任何接口)
5、 Hibernate是一个没有侵入性的框架,没有侵入性的框架我们一般称为轻量级框架
6、 Hibernate代码测试方便。
六、 Hibernate使用范围:
1. 针对某一个对象,简单的将它加载、编辑、修改,且修改只是对单个对象(而不是批量的进行修改),这种情况比较适用;
2. 对象之间有着很清晰的关系(例:多个用户属于一个组(多对一)、一个组有多个用户(一对多));
3. 聚集性操作:批量性添加、修改时,不适合使用Hibernate(O/映射框架都不适合使用);
4. 要求使用数据库中特定的功能时不适合使用,因为Hibernate不使用SQL语句;
=========================================
三、 Hibernate.cfg.xml:hbm2ddl.auto
在SessionFactory创建时,自动检查数据库结构,或者将数据库schema的DDL导出到数据库. 使用 create-drop时,在显式关闭SessionFactory时,将drop掉数据库schema. 取值 validate | update | create | create-drop
validate  加载hibernate时,验证创建数据库表结构
create 每次加载hibernate,重新创建数据库表结构,这就是导致数据库表数据丢失的原因。
create-drop 加载hibernate时创建,退出是删除表结构
update 加载hibernate自动更新数据库结构
在本机开发调试初始化数据的时候可以选择create、update等。
但是网站发布正式版本的时候,对数据库现有的数据或表结构进行自动的更新是很危险的。此时此刻应该由DBA同志通过手工的方式进行后台的数据库操作。
hibernate.hbm2ddl.auto的值建议是“none”或“validate”。“validate”应该是最好的选择:这样 spring在加载之初,如果model层和数据库表结构不同,就会报错,这样有助于技术运维预先发现问题。
九、 字段名和属性相同
Annotation:默认为@Basic
注意:如果在成员属性没有加入任何注解,则默认在前面加入了@Basic
Xml中不用写column
十一、 不需要(持久化)psersistence的字段:就是不实体类的某个成员属性不需要存入数据库中Annotation:使用@Transient 进行注解就可以了。
十二、 映射日期与时间类型,指定时间精度
Annotation:使用@Temporal(value=TemporalType)来注解表示日期和时间的注解
其中TemporalType有三个值:TemporalType.TIMESTAMP 表示yyyy-MM-dd HH:mm:ss TemporalType.DATE 表示yyyy-MM-dd;TemporalType.TIME 表示HH:mm:ss
十三、 映射枚举类型
Annotation:使用@Enumerated(value=EnumType)来注解表示此成员属性为枚举映射到数据库
其中EnumType有二个值:①EnumType.STRING 表示直接将枚举名称存入数据库;②EnumType.ORDINAL 表示将枚举所对应的数值存入数据库
Xml:映射非常的麻烦,先要定义自定义类型,然后再使用这个定义的类型……


=======================
ID主键生成策略
<id>标签必须配置在<class>标签内第一个位置。由一个字段构成主键,如果是复杂主键<composite-id>标签
被映射的类必须定义对应数据库表主键字段。大多数类有一个JavaBeans风格的属性, 为每一个实例包含唯一的标识。<id> 元素定义了该属性到数据库表主键字段的映射。 
<generator>元素(主键生成策略)
主键生成策略是必须配置
用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数, 用<param>元素来传递。 
所有的生成器都实现org.hibernate.id.IdentifierGenerator接口。 这是一个非常简单的接口;某些应用程序可以选择提供他们自己特定的实现。当然, Hibernate提供了很多内置的实现。下面是一些内置生成器的快捷名字: 
increment 
用于为long, short或者int类型生成 唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。 
identity 
对DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置标识字段提供支持。 返回的标识符是long, short 或者int类型的。 (数据库自增)
sequence 
在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的标识符是long, short或者 int类型的。(数据库自增) 
hilo 
使用一个高/低位算法高效的生成long, short 或者 int类型的标识符。给定一个表和字段(默认分别是 hibernate_unique_key 和next_hi)作为高位值的来源。 高/低位算法生成的标识符只在一个特定的数据库中是唯一的。 
seqhilo 
使用一个高/低位算法来高效的生成long, short 或者 int类型的标识符,给定一个数据库序列(sequence)的名字。 
uuid 
用一个128-bit的UUID算法生成字符串类型的标识符, 这在一个网络中是唯一的(使用了IP地址)。UUID被编码为一个32位16进制数字的字符串,它的生成是由hibernate生成,一般不会重复。 
UUID包含:IP地址,JVM的启动时间(精确到1/4秒),系统时间和一个计数器值(在JVM中唯一)。 在Java代码中不可能获得MAC地址或者内存地址,所以这已经是我们在不使用JNI的前提下的能做的最好实现了
guid 
在MS SQL Server 和 MySQL 中使用数据库生成的GUID字符串。 
native 
根据底层数据库的能力选择identity, sequence 或者hilo中的一个。(数据库自增) 
assigned
让应用程序在save()之前为对象分配一个标示符。这是 <generator>元素没有指定时的默认生成策略。(如果是手动分配,则需要设置此配置)
select 
通过数据库触发器选择一些唯一主键的行并返回主键值来分配一个主键。 
foreign 
使用另外一个相关联的对象的标识符。通常和<one-to-one>联合起来使用。 
二:联合主键
复合主键(联合主键):多个字段构成唯一性。
复合主键的映射,一般情况把主键相关的属性抽取出来单独放入一个类中。而这个类是有要求的:必需实现序列化接口(java.io.Serializable)(可以保存到磁盘上),为了确定这个复合主键类所对应对象的唯一性就会产生比较,对象比较就需要复写对象的hashCode()、equals()方法
=====================================
第14课 Hibernate核心开发接口(重点)


一、 Configuration(AnnotationConfiguration)
进行配置信息的管理;目标:用来产生SessionFactory;可以在configure方法中指定hibernate配置文件,默认(不指定)时在classpath下加载hibernate.cfg.xml文件
二、 SessionFactory
作用:主要用于产生Session的工厂(数据库连接池)
当它产生一个Session时,会从数据库连接池取出一个连接,交给这个Session
getCurrentSession():表示当前环境没有Session时,则创建一个,否则不用创建。
openSession(): 表示创建一个Session(3.0以后不常用),使用后需要关闭这个Session。
两方法的区别:
①、openSession永远是每次都打开一个新的Session,而getCurrentSession不是,是从上下文找、只有当前没有Session时,才创建一个新的Session
②、OpenSession需要手动close,getCurrentSession不需要手动close,事务提交自动close
③、getCurrentSession界定事务边界
上下文: 
所指的上下文是指hibernate配置文件(hibernate.cfg.xml)中的“current_session_context_class”所指的值:(可取值:jta|thread|managed|custom.Class)
<property name="current_session_context_class">thread</property>
常用的是:①、thread:是从上下文找、只有当前没有Session时,才创建一个新的Session,主要从数据界定事务
              ②、jta:主要从分布式界定事务,运行时需要Application Server来支持(Tomcat不支持)
③、managed:不常用
④、custom.Class:不常用
三、 Session
1、 管理一个数据库的任务单元
2、 save();
session.save(Object)
session的save方法是向数据库中保存一个对象,这个方法产生对象的三种状态
3、 delete()  
session.delete(Object)
Object对象需要有ID
对象删除后,对象状态为Transistent状态
4、 load()
格式: Session.load(Class arg0, Serializable arg1) throws HibernateException
* arg0:需要加载对象的类,例如:User.class
* arg1:查询条件(实现了序列化接口的对象):例"4028818a245fdd0301245fdd06380001"字符串已经实现了序列化接口。如果是数值类类型,则hibernate会自动使用包装类,例如 1
* 此方法返回类型为Object,但返回的是代理对象。
* 执行此方法时不会立即发出查询SQL语句。只有在使用对象时,它才发出查询SQL语句,加载对象。
* 因为load方法实现了lazy(称为延迟加载、赖加载)
* 延迟加载:只有真正使用这个对象的时候,才加载(才发出SQL语句)
* hibernate延迟加载实现原理是代理方式。
* 采用load()方法加载数据,如果数据库中没有相应的记录,则会抛出异常对象不找到(org.hibernate.ObjectNotFoundException)
5、 Get()
格式:Session.get(Class arg0, Serializable arg1)方法
* arg0:需要加载对象的类,例如:User.class
* arg1:查询条件(实现了序列化接口的对象):
例"4028818a245fdd0301245fdd06380001"字符串已经实现了序列化接口。如果是基数类型,则hibernate会自动转换成包装类,如 1
返回值: 此方法返回类型为Object,也就是对象,然后我们再强行转换为需要加载的对象就可以了。
如果数据不存在,则返回null;
注:执行此方法时立即发出查询SQL语句。加载User对象
6、 load()与get()区别
① 、 不存在对应记录时表现不一样;
② 、 load返回的是代理对象,等到真正使用对象的内容时才发出sql语句,这样就要求在第一次使用对象时,要求session处于open状态,否则出错
③ 、 get直接从数据库加载,不会延迟加载
get()和load()只根据主键查询,不能根据其它字段查询,如果想根据非主键查询,可以使用HQL
7、 update()
、用来更新detached对象,更新完成后转为为persistent状态(默认更新全部字段)
更新transient对象会报错(没有ID)
更新自己设定ID的transient对象可以(默认更新全部字段)
persistent状态的对象,只要设定字段不同的值,在session提交时,会自动更新(默认更新全部字段)
更新部分更新的字段(更改了哪个字段就更新哪个字段的内容)
a) 方法1:update/updatable属性
 xml:设定<property>标签的update属性,设置在更新时是否参数更新
<property name="name" update="false"/>
注意:update可取值为true(默认):参与更新;false:更新时不参与更新
annotateon:设定@Column的updatable属性值,true参与更新,false:不参与更新
@Column(updatable=false)
public String getTitle() {return title;}
注意:此种方法很少用,因为它不灵活
b) 方法二:dynamic-update属性
注意:此方法目前只适合xml方式,JAP1.0 annotation没有对应的
在实体类的映射文件中的<class>标签中,使用dynamic-update属性,true:表示修改了哪个字段就更新哪个字段,其它字段不更新,但要求是同一个session(不能跨session),如果跨了session同样会更新所有的字段内容。
<class name="com.bjsxt.Student" dynamic-update="true">
   如果需要跨session实现更新修改的部分字段,需要使用session.merget()方法,合并字段内容
这样虽然可以实现部分字段更新,但这样会多出一条select语句,因为在字段数据合并时,需要比较字段内容是否已变化,就需要从数据库中取出这条记录进行比较。
9、 clear()
清除session缓存
无论是load还是get,都会首先查找缓存(一级缓存,也叫session级缓存),如果没有,才会去数据库查找,调用clear()方法可以强制清除session缓存
10、 flush()
在hibernate中也存在flush这个功能,在默认的情况下session.commit()之前时,其实执行了一个flush命令。
Session.flush功能:
? 清理缓存;
? 执行sql(确定是执行SQL语句(确定生成update、insert、delete语句等),然后执行SQL语句。)
Session在什么情况下执行flush:
默认在事务提交时执行;
注意:flush时,可以自己设定,使用session.setFlushMode(FlushMode)来指定。
session.setFlushMode(FlushMode);
FlushMode的枚举值:
? FlushMode.ALWAYS:任务一条SQL语句,都会flush一次
? FlushMode.AUTO :自动flush(默认)
? FlushMode.COMMIT: 只有在commit时才flush
? FlushMode.MANUAL:手动flush。
? FlushMode.NEVER :永远不flush  此选项在性能优化时可能用,比如session取数据为只读时用,这样就   
不需要与数据库同步了
注意:设置flush模式时,需要在session开启事务之前设置。
可以显示的调用flush;
在执行查询前,如:iterate.
注:如果主键生成策略是uuid等不是由数据库生成的,则session.save()时并不会发出SQL语句,只有flush时才会发出SQL语句,但如果主键生成策略是native由数据库生成的,则session.save的同时就发出SQL语句。
11、 evict()
例如:session.evict(user)
作用:从session缓存(EntityEntries属性)中逐出该对象
利用Hibernate将实体类对象保存到数据库中,因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理,不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false。


session.evict(user);//从session缓存(EntityEntries属性)中逐出该对象
//无法成功提交,因为hibernate在清理缓存时,在session的临时集合(insertions)中取出user对象进行insert操作后需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session中逐出了,所以找不到相关数据,无法更新,抛出异常。
 解决在逐出session缓存中的对象不抛出异常的方法:
在session.evict()之前进行显示的调用session.flush()方法就可以了。
=================================
第15课 持久化对象的三种状态
一、 瞬时对象(Transient Object):
使用new操作符初始化的对象不是立刻就持久的。它们的状态是瞬时的,也就是说它们没有任何跟数据库表相关联的行为,只要应用不再引用这些对象(不再被任何其它对象所引用),它们的状态将会丢失,并由垃圾回收机制回收
二、 持久化对象(Persistent Object):
持久实例是任何具有数据库标识的实例,它有持久化管理器Session统一管理,持久实例是在事务中进行操作的----它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。
三、 离线对象(Detached Object):
Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,它们不再受hibernate管理。
四、 三种状态的区分:
1、 有没有ID,(如果没有则是Transient状态)
2、 ID在数据库中有没有
3、 在内存里有没有(session缓存)
五、 总结:
Transient对象:随时可能被垃圾回收器回收(在数据库中没有于之对应的记录,应为是new初始化),而执行save()方法后,就变为Persistent对象(持久性对象),没有纳入session的管理
内存中一个对象,没有ID,缓存中也没有
Persistent对象:在数据库有存在的对应的记录,纳入session管理。在清理缓存(脏数据检查)的时候,会和数据库同步。
内存中有、缓存中有、数据库有(ID)
Detached对象:也可能被垃圾回收器回收掉(数据库中存在对应的记录,只是没有任何对象引用它是指session引用),注引状态经过Persistent状态,没有纳入session的管理
内存有、缓存没有、数据库有(ID)
=============================
第16课 关系映射(重点)
一、 一对一 关联映射
? 两个对象之间是一对一的关系,如Person-IdCard(人—身份证号)
? 有两种策略可以实现一对一的关联映射
? 主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。
? 唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以用来表示一对一关联关系。
(一) 唯一外键关联-单向(unilateralism)
1、 说明:
人—-> 身份证号(Person?IdCard),从IdCard看不到Person对象
2、 对象模型
需要在Person类中持有IdCard的一个引用idCard,则IdCard中没有Person的引用
3、 关系模型
关系模型目的:是实体类映射到关系模型(数据库中),是要求persion中添加一个外键指向idcard
多对一场景下面的
(八) 重要属性-cascade(级联):
级联的意思是指定两个对象之间的操作联运关系,对一个 对象执行了操作之后,对其指定的级联对象也需要执行相同的操作,取值:all、none、save_update、delete
1、 all:代码在所有的情况下都执行级联操作
2、 none:在所有情况下都不执行级联操作
3、 save-update:在保存和更新的时候执行级联操作
4、 delete:在删除的时候执行级联操作。
(三) 多对一、一对多的区别:
多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一的。
一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多的。
两者使用的策略是一样的,只是各自所站的角度不同。
(十) 一对多,在一的一端维护关系的缺点:
因为是在一的一端维护关系,这样会发出多余的更新语句,这样在批量数据时,效率不高。
还有一个,当在多的一端的那个外键设置为非空时,则在添加多的一端数据时会发生错误,数据存储不成功。
如果在一对多的映射关系中采用一的一端来维护关系的话会存在以下两个缺点:①如果多的一端那个外键设置为非空时,则多的一端就存不进数据;②会发出多于的Update语句,这样会影响效率。所以常用对于一对多的映射关系我们在多的一端维护关系,并让多的一端维护关系失效(见下面属性)。
(四) 关于inverse属性:
inverse主要用在一对多和多对多双向关联上,inverse可以被设置到集合标签<set>上,默认inverse为false,所以我们可以从一的一端和多的一端维护关联关系,如果设置inverse为true,则我们只能从多的一端维护关联关系。
注意:inverse属性,只影响数据的存储,也就是持久化
(五) Inverse和cascade区别:
Inverse是关联关系的控制方向
Casecade操作上的连锁反应
(六) 一对多双向关联映射总结:
在一的一端的集合上使用<key>,在对方表中加入一个外键指向一的一端
在多的一端采用<many-to-one>
注意:<key>标签指定的外键字段必须和<many-to-one>指定的外键字段一致,否则引用字段的错误
如果在一的一端维护一对多的关系,hibernate会发出多余的update语句,所以我们一般在多的一端来维护关系。
八、 关联关系中的CRUD_Cascade_Fetch
1、 设定cascade可以设定在持久化时对于关联对象的操作(CUD,R归Fetch管)
2、 cascade仅仅是帮助我们省了编程的麻烦而已,不要把它的作用看的太大
a) cascade的属性指明做什么操作的时候关系对象是绑在一起的
b) refresh=A里面需要读B改过之后的数据
3、 铁律:双向关系在程序中要设定双向关联
4、 铁律:双向一定需要设置mappedBy
5、 fetch
a) 铁律:双向不要两边设置Eager(会有多余的查询语句发出)
b) 对多方设置fetch的时候要谨慎,结合具体应用,一般用Lazy不用eager,特殊情况(多方数量不多的可以考虑,提高效率的时候可以考虑)
6、 O/RMapping编程模型
a) 映射模型
i. jpa annotation
ii. hibernate annotation extension
b) 编程接口
i. jpa
ii. hibernate
c) 数据查询语言
i. HQL
ii. EJBQL(JPQL)
7、 要想删除或者更新,先做load,除了精确知道ID之外
8、 如果想消除关联关系,先设定关系为null,再删除对应记录,如果不删记录,该记录就变成垃圾数据
=====================
(五) 类表继承TABLE_PER_CLASS
理解如何映射
这种策略是使用union-subclass标签来定义子类的。每个子类对应一张表,而且这个表的信息是完备的,
即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处,
joined-subclass定义的子类的表,只包含子类特有属性映射的字段)。实现这种策略的时候,有如下步骤:
父类用普通<class>标签定义即可
子类用<union-subclass>标签定义,在定义union-subclass的时候,需要注意如下几点:
Union-subclass标签不再需要包含key标签(与joined-subclass不同)
Union-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),
也可以与class标签平行。 当Union-subclass标签的定义与class标签平行的时候,需要在Union-subclass
标签中,添加extends属性,里面的值是父类的全路径名称。子类的其它属性,像普通类一样,
定义在Union-subclass标签的内部。这个时候,虽然在union-subclass里面定义的只有子类的属性,
但是因为它继承了父类,所以,不需要定义其它的属性,在映射到数据库表的时候,依然包含了父类的所
有属性的映射字段。
注意:在保存对象的时候id是不能重复的(不能使用自增生成主键)
====================================
第17课 hibernate树形结构(重点)
树形结构:也就是目录结构,有父目录、子目录、文件等信息,而在程序中树形结构只是称为节点。
一棵树有一个根节点,而根节点也有一个或多个子节点,而一个子节点有且仅有一个父节点(当前除根节点外),而且也存在一个或多个子节点。
也就是说树形结构,重点就是节点,也就是我们需要关心的节点对象。
节点:一个节点有一个ID、一个名称、它所属的父节点(根节点无父节点或为null),有一个或多的子节点等其它信息。
Hibernate将节点抽取出成实体类,节点相对于父节点是“多对一”映射关系,节点相对于子节点是“一对多”映射关系。
===========================================================================
第19课 Hibernate查询(Query Language)


一、 Hibernate可以使用的查询语言
1、 NativeSQL:本地语言(数据库自己的SQL语句)
2、 HQL :Hibernate自带的查询语句,可以使用HQL语言,转换成具体的方言
3、 EJBQL:JPQL 1.0,可以认为是HQL的一个子节(重点)
4、 QBC:Query By Cretira
5、 QBE:Query By Example
注意:上面的功能是从1至5的比较,1的功能最大,5的功能最小


第20课 Query by Criteria(QBC)
QBC(Query By Criteria)查询方式是Hibernate提供的“更加面向对象”的一种检索方式。QBC在条件查询上比HQL查询更为灵活,而且支持运行时动态生成查询语句。 
在Hibernate应用中使用QBC查询通常经过3个步骤 
  (1)使用Session实例的createCriteria()方法创建Criteria对象 
  (2)使用工具类Restrictions的相关方法为Criteria对象设置查询对象 
  (3)使用Criteria对象的list()方法执行查询,返回查询结果
二、 Restrictions用法
Hibernate中Restrictions的方法 说明  
Restrictions.eq =  
Restrictions.allEq   利用Map来进行多个等于的限制  
Restrictions.gt   >  
Restrictions.ge   >=  
Restrictions.lt   < 
Restrictions.le   <=  
Restrictions.between   BETWEEN 
Restrictions.like   LIKE 
Restrictions.in   in 
Restrictions.and   and 
Restrictions.or   or 
Restrictions.sqlRestriction   用SQL限定查询 
五、 QBC分页查询 
   Criteria为我们提供了两个有用的方法:setFirstResult(int firstResult)和setMaxResults(int maxResults). 
setFirstResult(int firstResult)方法用于指定从哪一个对象开始检索(序号从0开始),默认为第一个对象(序号为0);setMaxResults(int maxResults)方法用于指定一次最多检索出的对象数目,默认为所有对象。 
第21课 Query By Example(QBE)
QBE查询就是检索与指定样本对象具有相同属性值的对象。因此QBE查询的关键就是样本对象的创建,样本对象中的所有非空属性均将作为查询条件。QBE查询的功能子集,虽然QBE没有QBC功能大,但是有些场合QBE使用起来更为方便。 
二、 query.list()和query.iterate()的区别
先执行query.list(),再执行query.iterate,这样不会出现N+1问题,
* 因为list操作已经将Student对象放到了一级缓存中,所以再次使用iterate操作的时候
* 它首先发出一条查询id列表的sql,再根据id到缓存中取数据,只有在缓存中找不到相应的
* 数据时,才会发出sql到数据库中查询
三、 两次query.list()
* 会再次发出查询sql
* 在默认情况下list每次都会向数据库发出查询对象的sql,除非配置了查询缓存
* 所以:虽然list操作已经将数据放到一级缓存,但list默认情况下不会利用缓存,而再次发出sql
* 默认情况下,list会向缓存中放入数据,但不会使用数据。
===============================================
第23课 性能优化策略
1、 注意session.clear()的动用,尤其在不断分页循环的时候
a) 在一个大集合中进行遍历,遍历msg,取出其中的含有敏感字样的对象
b) 另外一种形式的内存泄露 //面试是:Java有内存泄漏吗?
2、 1 + N问题 //典型的面试题
a) Lazy
b) BatchSize 设置在实体类的前面
c) join fetch
3、 list 和 iterate不同之处
a) list取所有
b) Iterate先取ID,等用到的时候再根据ID来取对象
c) session中list第二次发出,仍会到数据库查询
d) iterate第二次,首先找session级缓存
===============================
第24课 hibernate缓存
一、 Session级缓存(一级缓存)
一级缓存很短和session的生命周期一致,因此也叫session级缓存或事务级缓存
hibernate一级缓存

那些方法支持一级缓存:
* get()
* load()
* iterate(查询实体对象)

如何管理一级缓存:
* session.clear(),session.evict()

如何避免一次性大量的实体数据入库导致内存溢出
* 先flush,再clear

如果数据量特别大,考虑采用jdbc实现,如果jdbc也不能满足要求可以考虑采用数据本身的特定导入工具
二、 二级缓存
Hibernate默认的二级缓存是开启的。
二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存),它与session级缓存不一样,一级缓存只要session关闭缓存就不存在了。而二级缓存则只要进程在二级缓存就可用。
二级缓存可以被所有的session共享
二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存
二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存
二级缓存一般使用第三方的产品,如EHCache
1、 二级缓存的配置和使用:
配置二级缓存的配置文件:模板文件位于hibernate\etc目录下(如ehcache.xml),将模板存放在ClassPath目录中,一般放在根目录下(src目录下)
2、 二级缓存的开启:
Hibernate中二级缓存默认就是开启的,也可以显示的开启
二级缓存是hibernate的配置文件设置如下:
<!-- 开启二级缓存,hibernate默认的二级缓存就是开启的 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
3、 指定二级缓存产品提供商:
修改hibernate的 配置文件,指定二级缓存提供商,如下:
<!-- 指定二级缓存提供商 -->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
4、 使用二级缓存
a) xml方式:指定哪些实体类使用二级缓存:
方法一:在实体类映射文件中,使用<cache>来指定那个实体类使用二级缓存,如下:
<cache 
    usage="transactional|read-write|nonstrict-read-write|read-only"  (1)
    region="RegionName"                                              (2)
    include="all|non-lazy"                                           (3)
/>
(1) usage(必须)说明了缓存的策略: transactional、 read-write、 nonstrict-read-write或 read-only。 
(2) region (可选, 默认为类或者集合的名字(class or collection role name)) 指定第二级缓存的区域名(name of the second level cache region) 
(3) include (可选,默认为 all) non-lazy 当属性级延迟抓取打开时, 标记为lazy="true"的实体的属性可能无法被缓存 
另外(首选?), 你可以在hibernate.cfg.xml中指定<class-cache>和 <collection-cache> 元素。 
这里的usage 属性指明了缓存并发策略(cache concurrency strategy)。 
 策略:只读缓存(Strategy: read only) 
如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。甚至在集群中,它也能完美地运作。 
<class name="eg.Immutable" mutable="false">
    <cache usage="read-only"/>
    ....
</class>
策略:读/写缓存(Strategy: read/write) 
如果应用程序需要更新数据,那么使用读/写缓存 比较合适。 如果应用程序要求“序列化事务”的隔离级别(serializable transaction isolation level),那么就决不能使用这种缓存策略。 如果在JTA环境中使用缓存,你必须指定hibernate.transaction.manager_lookup_class属性的值, 通过它,Hibernate才能知道该应用程序中JTA的TransactionManager的具体策略。 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。 如果你想在集群环境中使用此策略,你必须保证底层的缓存实现支持锁定(locking)。Hibernate内置的缓存策略并不支持锁定功能。 
<class name="eg.Cat" .... >
    <cache usage="read-write"/>
    ....
    <set name="kittens" ... >
        <cache usage="read-write"/>
        ....
    </set>
</class>
策略:非严格读/写缓存(Strategy: nonstrict read/write) 
如果应用程序只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离, 那么比较适合使用非严格读/写缓存策略。如果在JTA环境中使用该策略, 你必须为其指定hibernate.transaction.manager_lookup_class属性的值, 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。 
策略:事务缓存(transactional) 
Hibernate的事务缓存策略提供了全事务的缓存支持, 例如对JBoss TreeCache的支持。这样的缓存只能用于JTA环境中,你必须指定 为其hibernate.transaction.manager_lookup_class属性。 
没有一种缓存提供商能够支持上列的所有缓存并发策略。下表中列出了各种提供器、及其各自适用的并发策略。 
方法二:在hibernate配置文件(hibernate.cfg.xml)使用<class-cache>标签中指定
要求:<class-cache>标签必须放在<maping>标签之后。
<class-cache class="com.wjt276.hibernate.Student" usage="read-only"/>
6、 二级缓存的管理:
1、 清除指定实体类的所有数据
SessionFactory.evict(Student.class);
2、 清除指定实体类的指定对象
SessionFactory.evict(Student.class, 1);//第二个参数是指定对象的ID,就可以清除指定ID的对象
CacheMode参数用于控制具体的Session如何与二级缓存进行交互。 
? CacheMode.NORMAL - 从二级缓存中读、写数据。 
? CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。 
? CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。 
? CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过 hibernate.cache.use_minimal_puts的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。 
如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics) API。 
Map cacheEntries = sessionFactory.getStatistics()
        .getSecondLevelCacheStatistics(regionName)
        .getEntries();
此时,你必须手工打开统计选项。可选的,你可以让Hibernate更人工可读的方式维护缓存内容。 
hibernate.generate_statistics true
hibernate.cache.use_structured_entries true
8、 总结
load默认使用二级缓存,iterate默认使用二级缓存
list默认向二级缓存中加数据,但是查询时候不使用
三、 查询缓存
查询缓存,是用于缓存普通属性查询的,当查询实体时缓存实体ID。
默认情况下关闭,需要打开。查询缓存,对list/iterator这样的操作会起作用。
可以使用<property name=”hibernate.cache.use_query_cache”>true</property>来打开查询缓存,默认为关闭。
所谓查询缓存:即让hibernate缓存list、iterator、createQuery等方法的查询结果集。如果没有打开查询缓存,hibernate将只缓存load方法获得的单个持久化对象。
在打开了查询缓存之后,需要注意,调用query.list()操作之前,必须显式调用query.setCachable(true)来标识某个查询使用缓存。
查询缓存的生命周期:当前关联的表发生修改,那么查询缓存生命周期结束
注意查询缓存依赖于二级缓存,因为使用查询缓存需要打开二级缓存
查询缓存的配置和使用:
* 在hibernate.cfg.xml文件中启用查询缓存,如:
<property name="hibernate.cache.use_query_cache">true</property>
* 在程序中必须手动启用查询缓存,
<property name="hibernate.cache.use_query_cache">true</property>
* 在程序中必须手动启用查询缓存,如:
query.setCacheable(true);
注:查询缓存的生命周期与session无关。
查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
四、 缓存算法
1、 LRU、LFU、FIFO
===========================================
第25课 事务并发处理
一、 数据库的隔离级别:并发性作用。
1、 Read Uncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到。)很少用
2、 Read Commited(提交读):只有提交后才可以读,常用,
3、 Repeatable Read(可重复读):mysql默认级别, 必需提交才能见到,读取数据时数据被锁住。
4、 Serialiazble(序列化读):最高隔离级别,串型的,你操作完了,我才可以操作,并发性特别不好,
隔离级别 是否存在脏读 是否存在不可重复读 是否存在幻读
Read Uncommitted(未提交读) Y Y Y
Read Commited(提交读) N Y(可采用悲观锁解决) Y
Repeatable Read(可重复读) N N Y
Serialiazble(序列化读)
脏读:没有提交就可以读取到数据称为脏读
不可重复读:再重复读一次,数据与你上的不一样。称不可重复读。
幻读:在查询某一条件的数据,开始查询的后,别人又加入或删除些数据,再读取时与原来的数据不一样了。


1、 Mysql查看数据库隔离级别:
方法:select @@tx_isolation;
2、 Mysql数据库修改隔离级别:
方法:set transaction isolation level 隔离级别名称;
例如:修改为未提交读:set transaction isolation level read uncommitted;
二、 事务概念(ACID)
ACID即:事务的原子性、一致性、独立性及持久性 
事务的原子性:是指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成. 
事务的一致性:是指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变. 
事务的独立性:是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致. 
事务的持久性:是指事务运行成功以后,就系统的更新是永久的.不会无缘无故的回滚.
=================================
第26课 hibernate悲观锁、乐观锁
Hibernate谈到悲观锁、乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差
并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了。称为并发性不好
数据库隔离级别:见前面章级
一、 悲观锁
悲观锁:具有排他性(我锁住当前数据后,别人看到不此数据)
悲观锁一般由数据机制来做到的。
1、 悲观锁的实现
通常依赖于数据库机制,在整修过程中将数据锁定,其它任何用户都不能读取或修改(如:必需我修改完之后,别人才可以修改)
2、 悲观锁的适用场景:
悲观锁一般适合短事务比较多(如某一数据取出后加1,立即释放)
长事务占有时间(如果占有1个小时,那么这个1小时别人就不可以使用这些数据),不常用。
3、 实例:
用户1、用户2 同时读取到数据,但是用户2先 -200,这时数据库里的是800,现在用户1也开始-200,可是用户1刚才读取到的数据是1000,现在用户用刚刚一开始读取的数据1000-200为800,而用户1在更新时数据库里的是用房更新的数据800,按理说用户1应该是800-200=600,而现在是800,这样就造成的更新丢失。这种情况该如何处理呢,可采用两种方法:悲观锁、乐观锁。先看看悲观锁:用户1读取数据后,用锁将其读取的数据锁上,这时用户2是读取不到数据的,只有用户1释放锁后用户2才可以读取,同样用户2读取数据也锁上。这样就可以解决更新丢失的问题了。
4、 悲观锁的使用
如果需要使用悲观锁,肯定在加载数据时就要锁住,通常采用数据库的for update语句。
Hibernate使用Load进行悲观锁加载。
Session.load(Class arg0, Serializable arg1, LockMode arg2) throws HibernateException
LockMode:悲观锁模式(一般使用LockMode.UPGRADE);注:只有用户释放锁后,别的用户才可以读取
二、 乐观锁
乐观锁:不是锁,是一种冲突检测机制。
乐观锁的并发性较好,因为我改的时候,别人随边修改。
乐观锁的实现方式:常用的是版本的方式(每个数据表中有一个版本字段version,某一个用户更新数据后,版本号+1,另一个用户修改后再+1,当用户更新发现数据库当前版本号与读取数据时版本号不一致(等于小于数据库当前版本号),则更新不了。
Hibernate使用乐观锁需要在映射文件中配置项才可生效。
乐观锁在存储数据时不用关心 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值