框架Hibernate笔记系列 基础Session

时间:2023-11-10 15:58:38

标题:框架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缓存的概念。