当前位置:文档之家› hiberbate学习笔记

hiberbate学习笔记

23:02 2013/1/17
hibernamte_day01
1.2Hibernate的工作原理--ORM***
对象-关系映射(Object-Relationship Mapping)
在我们的应用程序(App)中,数据用对象体现,在在数据库中,数据是使用表放入形式保存。Hibernate用于程序中的对象(Object)与表中的数据关系(Relationship)之间的映射(Mapping),即把对象把对象保存到关系表中或者把关系表中表中数据取出来映射为对象。
可以这样理解,当我们使用Hibernate框架技术,就可以直接从数据库取出java对象,或者把java对象直接保存到数据库中,中间写sql语句等繁琐的步骤被hibernate封装,对我们透明。

Hibernate自动化的程度很高的组件,因此比较难以驾驭,在对Hibernate理解不够透彻的情况下使用,稍不留神就可能会影响性能。
业界还有一些自动化程度稍低的数据访问层组件,比如Ibatis,相当于半自动的仪器,可控性较Hibernate强一些

1.3

3)hibernate映射文件(可以有n个)
用来指明类和表之间的对应关系,Hibernate根据该文件生成sql语句比如POJO类名为Emp.java,对应的映射文件为Emp.hbm.xml

**我们一般不使用业务数据作为主键,因为逻辑的改变有可能会改变主键。
生成主键的方式有很多中
assigned、foreign、guid、hilo、identity、increment、native、select、seqhilo、sequence、uuid
其中、sequence是使用序列生成主键(Oracle数据库经常使用)
Mysql数据库一般使用identity,自动生成主键(注意需要在建表时主键生成为auto_increment)。

"form User" 是Hibernate提供面向对象的查询语言:
HQL(Hibernate Query Language)语言
表示从数据库中将所有的User对象的数据都取出来(一个User对象对应一条数据)
Query是结果集对象

1.3.2主键生成方式有以下几种
1)identity 用于自动生成主键的方式,除了Oracle不支持,其他数据库一般都支持(较常用)
2)sequence Oracle中使用,用序列生成ID主键
3)native 主键生成方式如果是native,那就看配置文件hibernate.cfg.xml中方言是什么,
如果方言是MySql,相当于Identity,如果是方言Oracle,相当于sequence
4)increment 不常用
5)assigned 不常用,手动生成id


increment 生成主键生成方式是先"select max(t_id) from t_foo ",从t_foo中找到最大id,之后将max(t_id)加1,这样就保证了主键唯一。但是这样有危险,并发访问时会有风险。不建议使用。

assigned
没有任何自动方式,需要自己自己指定,使用也较少。

设置主键为assigned

1.3.3 Hibernate 映射类型
案例描述
常用Hibernate映射类型有如下几种:
string
integer
double
date 日期,指表示年月日
datetime 日期,指表示年月日
timestamp 时间戳,存放年月日时分秒
yes_no 将在数

据库中存放一个字符Y或者N
true_false 将在数据库中存放一个字符T或者F,功能yes_no是相同的



=========================================================================
9:45 2013/1/21
hibernate_day02
1.持久对象和以及缓存机制
1.1对象得的三种状态
有以下三种
1)暂时态
当对象刚创建,和Session没有发生任何关系时,当程序运行完就立刻结束消失,被称为暂时态。
2)持久态
当执行如下代码时,对象变为持久态
Emp e = new Emp();
session.save();
持久态对象和Session发生了关系,如执行了save、get、query等方法
session中会缓存给对象(Session的缓存叫做以及缓存)
Session再获取对象时,首先去查找一级缓存,如果没有才去查询数据库
Session要负责将持久态对象的变化更新到数据库
(在是flush的时候更新,tx提交的时候会自动调用session的flush())
3)游离态
调用了session.evict(Object obj)方法,和Session解除了关系
1.2一级缓存机制
其一,如果session被查询,session将先到缓存中查询是否有被查询的对象,找到直接取出,否则才查询数据库;
其二,session需要负责实时维护缓存中的数据,保证缓存中的数据数据数据的一致性。一旦用户对缓存数据做了修改,session立刻将更新到数据库中。

session.save(foo);语句执行后,Hibernate自动执行了insert操作
foo.setValue("foo100");语句执行后,Hibernate又会自动执行update操作。

提问:是否foo.setValue("foo200");或触发session的更新操作?
所以注意:
当食物提交时,session会把持久对象的改变更新到数据库。
更准确的说:
当执行session.flash()操作时,session会把持久对象的变更更新都数据库,而当执行https://www.doczj.com/doc/447459519.html,mit()操作时,会自动调用session.flash();


查看控制台,先执行了一次session.flash(),之后https://www.doczj.com/doc/447459519.html,mit()操作又自动执行一次session.flash();
所以,执行了2次update操作

凡是被session处理过的对象,都是持久态。id=1的Foo对象在之前已经被持久化了数据库中,所以通过get方法查询的foo1和foo2对象是同一个对象。

如果被session查询,foo1指向的对象是持久态,该对象将缓存于session中。
Foo foo1 = (Foo)session.get(Foo.class, 1);
当session再查询一次
Foo foo2 = (Foo)session.get(Foo.class, 1);
session会首先在一级缓存中查询id=1的foo对象,
如果找到,就从一级缓存中取,如果找不到,才查询数据库

此时,如果执行

当数据被缓存到session中时,session就要负责维护缓存中的数据,这是Hibernate中的一个重要机制:以及缓存机制

以及缓存机制:其一如果session被查询,session将先到缓存中查找是否有被查询的对象,找到则取出,找不到才在数据库查询;
其二,session需要负责实时维护在缓存中

的数据,保证缓存中的数据与数据库中的数据的一致性,一旦用户对缓存中的数据做看修改,session立刻将数据更新都数据库中。
session.evivt()方法
session.evict()方法会将对象从session中踢出。
b 加入session.evice()方法

当对象被清除出session后,即刻变为游离态,此时对象中的修改将不起左右,session不会把游离的对象更新懂啊数据库中

2.Hibernate延迟加载机制

2.1基本原理
延迟加载机制的基本原理
当访问实体对象时,并不是立即到数据库中查找。而是在真正要使用实体对象的时候,才去数据库查询数据。

具备这样的功能的方法
session.load()
query.iterator()
注意:这些方法返回的对象,里面没有数据,数据在使用的时候(调用getXXX()方法时)才取

2.2 实现原理
1)load方法、iterator方法返回的对象不是实体类,而是该实体类动态子类对象,该子类重写了getXXX方法,在该方法触发了数据库的访问。
2)当调用load方法或者itertor方法时,具体Hibernate调用了GBLIB的功能实现了动态生成子类。

2.3 OpenSessionInView和ThreadLocal*
1)OpenSessionInView技术把Session的关闭延迟到View组件运行完之后
2)如果使用延迟加载必须使用OpenSessionInView技术,否则在取数据时,session已经关闭了
3)实现OpensessionInView可以采用很多技术:
Servlet---过滤器
Struts2---拦截器
Spring---AOP
4)使用OpenSessionInView必须满足Session的线程单例
一个线程分配一个Session,在该线程的方法中可以获得该Session,
具体使用ThreadLocal--其实一个线程为Key的Map,
5)Hibernate的支持
配置文件中:
thread
然后调用:
sessionFactory.getCurrentSession();
自动实现线程单例

通过对比,我们发现load()方法并没有导致select语句立即执行。
load方法并没有导致select语句立即执行。
load方法真正将数据取出,而返回的对象Foo只是一个代理对象,其中没有数据。load方法只是做好了取出数据的准备。
而当调用foo.getId()时,才真正从数据库取出数据来。这叫做延迟加载(懒加载)。

延迟加载
即对象在load的时候,数据并没有取出来,当填充表单时才会带数据库中取数据。
如果在ProjectDao中调用session.close()关闭session,
所以projectupdateform.jsp页面就没有取到数据。
如果ProjectDao 中不调用session.close方法
那么projectupdateform.jsp页面可以从session中驱动数据。

这样就造成一个矛盾:
一方面,session必须关闭,否则浪费数据连接资源;
另一方面,如果使用延迟加载,就不能在Dao中调用session.close()方法来关闭session。

如何在项目中使用延迟加载
使用OpenSessionInView技术
(即在页面的时候Sess

ion还是开启的,当页面显示完成后session才关闭)

如何让session在页面显示完成后才关闭?
a 将关闭session的操作写在拦截器中,等页面显示完成后才关闭session。
b 关闭session时,要找到对应的session(要一直持有该session)

ThreadLocal类
ThreadLocal类能够帮助我们实现项目中的延迟加载,在企业开发总共使用广泛。

5)测试ThreadLocal

然而,我们不希望从f()方法中拿到的s2对象和main()方法中创造的s1是同一个对象首先注意一点,f()方法和main()方法同样是在一个线程
有相同的线程号

Hibernate为啥要给我们延迟加载的机制?
可以再某些时候提高效率,降低并发访问数据的压力。
综上,如果使用get()方法,那么不是延迟加载,如果使用load()方法,那么就是延迟加载。load方法相当于做好取消数据的准备,等到了使用的时候才从数据库中取出数据。

以当线程id为key,创建一个sone对象,在同一个线程中,再利用SomeFactory得到的Some对象是同一个。
只有字不同的线程中,提到的对象才是不同的

使用load方法返回的的值并不是Foo对象

Hibernate如何延迟加载
当调用load()方法时
Foo foo = (Foo)session.load(Foo.class,1);

Hibernate返回的是Foo动态生成的子类对象
该子类重写了getValue()方法,在这个方法中实现了延迟加载的工作。
Foo$$....extend Foo{
public String getValue(){
//触发数据库操作
return value;
}
}

什么是动态生成一个类?
一般情况下,我们像创建并使用一个类的流程如下:
a 编译java源文件-->在硬盘上生成一个二进制.class文件
b JVM加载.class文件,将类读入一块内存(方法区)中
c 应用程序调用方法区中的类及其放方法

而动态生成技术,是应用程序直接在内存中创建一个类。就像当我们调用load方法,我们并没有创建Foo$$这个类,
该类是有Hibernate动态生成的。

严格来讲,动态生成类技术也不是由Hibernate完成的,是由其他组件生成的,
asm.jar的作用就是在内存中生成类;
clib-2.1.3.jar是在调用asm.jar的基础上动态生成子类。因为asm.jsr非常底层,cglib.jar对其做封装,生成某个类的子类。于是。Hibernate调用看cglib.jar实现延迟加载

如下显示,Foo$$这个类时由cglib实现的。

思考如何让延迟加载失效?

因此,注意:
自定义的类不要做成final的,因为在很多框架中会有这样的动态生成机制

打印四条查询语句,表名当query.iterate()语句执行结束后,因为延迟加载机制,先执行select t_id from t_foo; 语句,从数据库中仅仅将id取出;在迭代的过程中,每次执行fooIt.next()语句,需要对象数据时,才根据id到数据库中取出全部数据。


线程单例
回到服务器中,只

要是服务器,每一个浏览器访问服务器时,服务器会为每一个浏览器创建一个线程。假设Some就是Session,如果使用这种机制获取Session,当一个用户浏览器不论怎么调用session都是同一个(只要在相同的线程中)。
这种机制就叫做线程单例。
线程单例的实现原理就是
ThreadLocal就相当于Map,只不过Key是固定的,就相当于当前的线程号。

ThreadLocal就相当于Map,只不过key是固定的,就是当前线程号。

我们可以再Dao中调用getSession()方法获取一个Session,当页面显示数据结束后,在拦截器中调用closeSession()关闭Session。
以后使用这个HibernateUtil即可。

3 many-to-one 关联映射
3.1 数据表关联
数据表单的关联(只有一种情况)和Hibernate关联映射。
Hibernate关联映射在数据表关联的基础上,根据业务需求,为了存取数据的方便高效而设计的。
数据表的关联不一定到时Hibernate关联映射

3.2many-to-one
基础表
需求在取出Emp(many)的时候Dept(one)关联的取出

3.3 many-to-one需求下的常见操作
1)保存Emp
Emp中有已经存在的Dept
2)取出Emp(带着Dept)
Emp emp = (Emp)session.get(Emp.class,empId);
关联属性默认延迟加载

但是:取Dept还是用的单独的SQL,可以设置凤fetch方式

此时用join的方式生成SQL
3)查询Emp(带着Dept)
HQL:from Emp
关联属性默认是延迟加载

但是:取Dept还是用单独的SQL
HQL:from Emp e left outer join fetch e.dept
4)根据特定的条件Emp(带着Dept)
HQL:from Emp e where https://www.doczj.com/doc/447459519.html, =''
HQL:from Emp e left outer join fetch e.dept where https://www.doczj.com/doc/447459519.html,='..'
5)根据Dept的属性查询Emp(带着Dept)
HQL:from Emp e where https://www.doczj.com/doc/447459519.html,=''
HQL:from Emp e left oouter join fetch e.dept where https://www.doczj.com/doc/447459519.html,=''

新建t_emp 和 t_dept
注意:这里不用写主键关联约束(现在的约束使用的越来越少)

当我们写员工表Emp和部门表Dept时
Emp Dept many-to-one
取出员工信息的同时取到关联的部门信息,这样的业务可以叫做one-to-many此时部门的Dept与员工Emps的关系是一对多

再比如当我们写购物车订单Order和订单项Item时
Item Order many-to-one
当我们看到一个条目Item时,我们也要看该条目属于哪个订单Order
Order Item one-to-many
当取出一个订单时,我们要看该订单中所有条目

一般情况下,many-to-one的需求较多
注意:
在如上这样的需求中,我们使用关联关系映射;并且,并不是所有地方都需要关联关系映射。
如果没有类似的需求,就不需要做如下操作。


相关主题
文本预览
相关文档 最新文档