标题:框架Hibernate笔记
资料地址:
1. www.icoolxue.com 孔浩
1.背景简介
Hibenate是JBoss公司的产品。它是数据持久化的框架。Usually,我们使用JDBC来access DB,我们要先连接数据库,然后与数据库交互,然后关闭连接。使用了Hibernate之后,它实现了对JDBC的封装。跟数据库建立连接和关闭连接都由Hibernate来管理,我们只要写交互代码就可以了。
使用Hibernate,甚至不需要写SQL语句,它会自动帮你生成表 生成SQL语句,自动执行SQL语句。
目前,使用mybatis的比较多。原因在于使用Hibernate效率不高。(In the same way,使用springMVC代替Struts2)
2.环境搭建
2.1
STS,springsource toolsuit。用于项目开发。
安装JBoss,因为里面有支持Hibernate的模块。可以用于代码提示。
2.2
导入包(安装目录下的lib库中的required包+log4j包+数据库驱动包)
创建hibernate的配置文件,ibernate.cfg.xml,在这个文件中加入相应的数据库基本信息(dialect方言用来确定连接的数据库;connection.driver_class连接驱动;connection.url数据库地址;conncction.username&&conncection.password;show_sql打印sql语句;hbm2dll自动完成类到数据库表的转换)
创建实体类User.java;映射关系User.hbm.xml。
创建sessionFactory,它是线程安全的。所以,整个sessionFactory应该基于单例的模式来创建。
创建session,session=sessionFactory.openSession();session.beginTranscation(); session.getTransaction().commit();
数据库中的类型timestamp表示时间戳,年月日时分秒。
3. 使用Hibernate实现我们的增删改查。
session.save();session.load();session.update();session.delete();
session.createQuery().list();获得list列表。
session.createQuery().setFirstResult();直接搞定分页,不用SQL了。
4. Hibernate状态
Transient临时状态;Persistent持久化状态;Detached游离状态。
临时状态(Transient):用new创建的对象,它没有持久化,没有处于Session中,处于此状态的对象叫临时对象;持久化状态(Persistent):已经持久化,加入到了Session缓存中。如通过hibernate语句保存的对象。处于此状态的对象叫持久对象(已存入数据库);游离状态(Detached):持久化对象脱离了Session的对象,如Session缓存被清空的对象。特点:已经持久化,但不在Session缓存中。处于此状态的对象叫游离对象;游离对象是数据库中有,但是没有被管理的对象。
当session未提交时,再次使用session.set(U)将会进行更新操作。这样可以防止重复提交。在第一次save和commit之间,写的所有东西,如果属性相同被hibernate忽略,如果不同,hibernate在commit的时候,会进行update操作。同样的,当session.load()的时候也是将数据从sql中取出来,再次的改动,也同样是update语句。。(因为同样都是session管理,所以都是persist状态)load()的时候,会创建一个代理对象。
session.save()每次执行一次,就会在SQL中存一个数据。
5. Hibernate与SQL的关系。
在操作了hibernate的方法如save()等后,并没有直接生成sql语句,去操作数据库,而是把这些更新存入Session中,只有Session缓存要被更新时,底层的sql语句才能执行,数据存入数据库;
如:Session.save(user)运行机理。
1,把User对象加入缓存中,使它变成持久化对象;
2,选用映射文件指定的标识生成ID;
3,在Session清理缓存时候执行:在底层生成一个insert sql语句,把对象存入数据库。
”在你执行Session.save(user)后,在Session清理缓存前,如果你修改user对象属性值,那么最终存入数据库的值将是最后修改的值;此过程中ID不能被修改;“
如:Session.delete(user)运行过程。
如果user是持久化对象,则执行删除操作,同样底层数据库的执行条件是:在Session清理缓存时候;
如果user是游离对象:
1,将user对象和Session关联,使之成为持久化对象;
2,然后按照user 是持久化对象的过程执行。
6. Hibernat延迟加载
首先,session.load()和session.get()来加载一个对象区别;在load()对象之后,并不会马上执行sql,只有使用对象的时候,才执行sql语句。当完成load(对象)之后,这个对象其实仅仅是一个代理对象,这个代理对象中仅仅有一个id值。这里的代理对象问题,会在程序中引起很多问题。(代理对象只有一个ID,取其他值会报ObjectNotFoundException异常)
get()一执行,就会发送sql语句。get没有延迟加载。load()则相反。使用load()是有延迟加载的,返回的代理对象只有一个id.返回的session已经被关闭了。当需要数据库中的其他属性时,就需要到其它数据库中去取。
一对多种,要通过外键取另一方的数据时,因为默认是延迟加载的,所以会执行两个SQL语句来执行查询。
怎么解决关闭session后,延迟加载的问题?当取到主表的集合后,会自动关闭session。查询结果被清空。如果这个时候去查询主表关联表的数据内容,由于session已关闭,无法取到数据。解决办法:用spring的OpenSessionInViewFilter把session的周期交给servlet filter来管理,每当有request进来,就打开一个session,response结束之后才关闭它,这样可以让session存在于整个servlet request
请求周期中。
7. Hibernate的ID生成策略
<id name="id"> <generator class="assigned"> </id>
<property name="usename"></property>
assigned需由程序员手动指定,否则第二次保存的时候,则会抛异常。
native,自动+1。检索起来方便。但是,每次添加的时候,得从SQL中查找最多的id号,然后加1。
uuid,会自动生成一个字符串;此时,定义主键时必须为String类型。uuid自动生成,不重复。它直接生成主键,不用查询数据库。
8. Hibernate中的Many to One多对一单向关联(关系映射)
<Many-to-one name="classroom" column="cid" cascade="add/delete/all...">用来映射多对一,name表示对象中的属性名称。一对多,在多的一方,增加外键。那么你想,在保存的时候,如果要保存多的一方要存一的一方的外键值。。所以在add的时候,要先add一的一方,再add多的一方。
最佳实践:一定要先添加一的一方,再添加多的一方。不然的话,反过来,会多执行若干条update()的语句。影响Hibernate的效率。关系永远由多的一方来维护,而不要由一的一方来维护。
Many-to-one中也涉及到了延迟加载,当加载外键中的内容时,由于延迟加载,会再执行一条SQL语句来查询外键中的内容。
当不使用cascade级联的时候,默认地我们要先存一的一方(会在DB中有一个主键ID),这样我们在存多的一方的时候,才能确保存成功。但是,假如有这么一种情况,我没有save一的一方,那么在保存多的一方的时候,是会抛异常的。因为一的一方没有相对应的id,我们没法保存。(这是孔浩视频中讲的)那么,当我们设置了cascade的时候,会自动完成关联,如果添加的时候没有关联对象,会自动创建一个关联对象。这正好解决了上述问题(没有保存一的一方,但是会自动关联,所以就会存了,不给你抛异常了)。但是。同样的。当我们要删除多的一方的其中一个数据的时候,因为设置的是级联关联的。所以我们删除多的一方的一个数据,外键其实还被多的一方的其他数据所关联的,在这个时候,运行测试类,就会抛出异常了。最佳实践:如果没有特殊情况,尽量不要使用cascade。可能使用cascade的地方,一般都是一的一方进行删除时使用。特殊需求,才会使用cascade的add,正常情况下,add方法应该都是由程序员完成添加的。
更多问题,咨询视频第七讲。孔浩讲Hibernate
9. Hibernate的One-to-many一对多单向关联
还记得,在Many-to-one单向关联中,我们在多的一方引用了对象。而我们在One-to-many中,我们在一的一方引用了Set(使用Set,而不使用List的原因:)。
<set name="comments">
<!--key用来指定在对方的外键的名称-->
<key column="cid"/>
<!--class用来设置列表中的对象类型-->
<One-to-many class="comments"/>
</set>
在使用set的时候,我么可以在bean实体类中,创建HashSet方法,然后每次add一个对象到Set集合中。具体操作可以参考视频。特别注意,One-to-many在添加和维护关系时,比较麻烦。所以在开发中不建议使用One-to-many的单向。
10. Hibernate的One-to-many的双向关联
顾名思义,双向关联。我们需要在一的一方写Set关联,在多的一方写对象管理。
同样的道理,先添加一的那一方;再添加多的那一方。这样可以提高系统的效率。(因为可以减少Hibernate将会执行的SQL语句)所以给我们的最佳实践就是:不要使用一的一方来维护关系。
<!--inverse为true表示不在自己的这一方维护关系(双向关联中)-->
<!--lazy为extra表示Hibernate会根据参数来执行SQL语句,效率高-->
<set name="comments" lazy="extra" inverse="true">
<!--key用来指定在对方的外键的名称-->
<key column="cid"/>
<!--class用来设置列表中的对象类型-->
<One-to-many class="comments"/>
</set>
在配置文件的<set>标签中,可以通过inverse来明确不使用一的这一端,来维护关系。
11. Hibernate的One-to-one的单向关联
<!--一对一也使用Many-to-one,但在后面指明unique为true,只对应一个对象-->
<Many-to-one name="person" column="personId" unique="true"/>
<!--你这么一看,One-to-one就是Many-to-one的特例-->
12. Hibernate的One-to-one的双向关联
一端为<Many-to-one name="">,另外一端为<One-to-many>.关系由设置了外键的一端<Many-to-one name="">来维护。只要取出的是没有关系的这一方,会自动将关联对象取出。最佳实践:尽量不要使用这种双向的一对一的关联。
13. Hibernate的Many-to-many双向关联
在实体类的两方都要写<set>.在两边的任何一方都可以维护关系。同样地,我们这部分的知识知道怎么配置,知道每次的执行要执行几条SQL语句就可以了。
<!--set中存放的是对方的实体类名称-->
<!--table设置关联的中间表-->
<set name="admin" table="t_admin_role" lazy="extra">
<!--key中存放的是自己在对方中的外键-->
<key column="aid">
<Many-to-many class="Role" column="rid">
</set>
14. 基于Annotation的Hibernate
15.
总结:
1. Hibernate中的好多现象是由于缓存造成的。之所以使用缓存,是Hibernate为了提高效率的优化。但是在目前的分层体系架构中,不明显。
2.牢记这点:数据库中没有的就是游离状态;数据库中有的,被session管理的就是持久化状态。持久化状态在提交的时候,会根据具体情况来update()。
3. 学好Hibernate的关键之处就是,每次执行一条Hibernate语句,都要知道将要执行几条SQL语句。
4. java中命名习惯用驼峰规则;数据库中命名习惯用下划线。
5.在学习和使用Hibernate的过程中,始终要有一个session缓存的概念。