Hibernate框架深入学习

ORM思想:

首先我们来了解一下ORM思想:ORM的英文是Object Relation Mapping 对象关系映射。

对象-关系映射(OBJECT/RELATIONALMAPPING,简称ORM),是随着面向对象的软件开发方法发展而产生的。用来把对象模型表示的对象映射到基于S Q L 的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQ L 语句打交道,只需简单的操作实体对象的属性和方法就可以了,O R M 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化


简单的说就是把我们程序中的实体类和数据库表建立起来对应关系。

  所谓的ORM就是利用描述对象和数据库表之间映射的元数据,自动把Java应用程序中的对象,持久化到关系型数据库的表中。通过操作Java对象,就可以完成对数据库表的操作。可以把ORM理解为关系型数据和对象的一个纽带,开发人员只需要关注纽带一端映射的对象即可。




与其它操作数据库的技术相比,Hibernate具有以下几点优势:

  1. HibernateJDBC访问数据库的代码做了轻量级封装,大大简化了数据访问层繁琐的重复性代码,并且减少了内存消耗,加快了运行效率。
  2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化了DAOData Access Object,数据访问对象)层编码工作。
  3. Hibernate的性能非常好,映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系,可扩展性强,由于源代码的开源以及API的开放,当本身功能不够用时,可以自行编码进行扩展。

明确关注:

Hibernate框架操作实体类就相当于操作数据库表


Hibernate中get和load方法的区别

区别:

1、查询的时机不一样

get方法任何时候都是立即加载的,即只要一调用get马上发起数据库查询

load方法默认情况下是延迟加载,即真正用到对象的非OID字段的数据才发起查询

load方法可以通过配置的方式改为立即加载。

配置的方式如下:

由于load方法是hibernate的方法所以只有XML的配置方式:(关闭懒加载的方式)

<class name="Customer" table="cst_customer" lazy="false">

2、返回的结果不一样

get方法永远返回查询的实体类对象。

load方法当是延迟加载时,返回的是实体类的代理对象

涉及的概念:

立即加载:

是不管用不用马上查询。

延迟加载:

等到用的时候才真正发起查询。


Hibernate的持久化类的概念:

Hibernate是持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。那么知道了什么是持久化,什么又是持久化类呢?其实所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为是持久化类。其实你可以简单的理解为持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系


1.1.1 持久化类的编写规则:

我们在编写持久化类的时候需要有以下几点需要注意:

  1. 持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。
  2. 持久化类的属性需要私有,对私有的属性提供公有的getset方法。因为在Hibernate底层会将查询到的数据进行封装。
  3. 持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。举个例子:

假设表中有一员工工资的列

,如果使用double类型,如果这个员工工资忘记录入到系统中,系统会将默认值0存入到数据库,如果这个员工工资被扣完了,也会向系统中存入0.那么这个0就有了多重含义,而如果使用包装类类型就会避免以上情况,如果使用Double类型,忘记录入工资就会存入null而不是0,而这个员工工资被扣完了,就会存入0,不会产生歧义。

    持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象是一样的意思,在关系型数据库的表中是通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个OID相同的持久化对象的。

    持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么就不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略就会失效。

持久化类我们已经可以正常编写了,但是在持久化类中需要有一个唯一标识OID与表的主键去建立映射关系。而且主键一般我们是不会让客户手动录入的,一般我们是由程序生成主键。那么Hibernate中也提供了相应的主键生成的方式,那么我们来看下Hibernate的主键生成策略。

简单的说:

我们的实体类都需要遵从JavaBean的编写规范。

什么是JavaBean:

Bean:在软件开发领域,Bean表示可重用组件。

JavaBean就是用java语言开发的可重用组件。

JavaBean的编写规范是什么:

1.类都是public的(公有的,拥有最大的访问权限,方便其他程序来调用)

2.都有默认无参构造函数(方便通过动态代理的方式来产生代理对象)

3.成员变量都是私有的(因为这些属性,我们并不希望外面来改变它,它只是服务于本程序的内部)

4.都有公有的get/set方法(方便外界使用)

5.一般都实现Serializable接口(方便对象的持久化操作,比如持久化到硬盘)

基本类型和包装类的选择问题:

由于包装类可以有null值。所以实际开发中都是用包装类。

1.1 hibernate中对象标识符(OID)Object Identify介绍

OID全称是Object Identifier,又叫做对象标识符。

它是hibernate用于区分两个对象是否是同一个对象的标识。

我们都知道,虚拟机内存区分两个对象看的是内存的地址是否一致。数据库区分两个对象,靠的是表的主键。hibernate负责把内存中的对象持久化到数据库表中,靠的就是对象标识符来区分两个对象是否是同一个。实体类中映射主键的字段就是OID,如下图所示:



1.1 hibernate的主键生成策略

在了解Hibernate的主键生成策略之前,先来了解两个概念,即自然主键和代理主键,具体如下:

    自然主键:把具有业务含义的字段作为主键,称之为自然主键。例如在customer表中,如果把name字段作为主键,其前提条件必须是:每一个客户的姓名不允许为null,不允许客户重名,并且不允许修改客户姓名。尽管这也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许客户重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。

    代理主键:把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为“ID”,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间。在上面例子中,显然更合理的方式是使用代理主键。

第1章 Hibernate的一级缓存和对象状态

1.1 hibernate的一级缓存

1.1.1 hibernate中的一级缓存:

    Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数,从而提升效率。

    在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存。只要 Session 实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为是Session基本的缓存。

Hibernate的一级缓存有如下特点:

1.当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息复制一份加入到一级缓存中去。

2.当调用Session接口的load()、get()方法,以及Query接口的iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。

3.当调用Session的close()方法时,Session缓存会被清空。

下面我们来提供一个测试Hibernate一级缓存的方法
  1. @Test  
  2.     // 证明Hibernate的一级缓存的存在:  
  3.     public void demo1(){  
  4.         Session session = HibernateUtils.openSession();  
  5.         Transaction tx = session.beginTransaction();          
  6.         Customer customer1 = session.get(Customer.class, 1l);// 马上发生一条sql查询1号客户.并将数据存入了一级缓存  
  7.         System.out.println(customer1);  
  8.           
  9.         Customer customer2 = session.get(Customer.class, 1l);// 没有发生SQL语句从一级缓存中获取数据.  
  10.         System.out.println(customer2);  
  11.           
  12.         System.out.println(customer1 == customer2);// true 一级缓存缓存的是对象的地址.  
  13.         tx.commit();  
  14.         session.close();  
  15.     }  

2.1.3 Hibernate的快照机制详解:

Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。

1.1 Hibernate中持久化类的三种状态说明:

了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划

分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态的详细介绍如下。

1、 瞬时态(transient)

瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。

2、 持久态(persistent)

 持久态的对象存在持久化标识OID 加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。

3、 脱管态(detached)

脱管态也称离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。

 学习对象状态我们要明确的:

a、是为了更好的掌握hibernate中操作的方法。

b、区分状态只有两个标识

一是否有OID

二是否和Session建立的关系

临时(瞬时)状态:

没有OID,和Session没有关系。

持久化状态:

有OID,和Session有关系。

脱管状态:

有OID,和Session没有关系。

持久化类三种对象状态的互相转化

持久化对象的三种状态可以通过调用Session中的一系列方法实现状态间的转换,具体如下: 

1、瞬时态转换到其他状态

通过前面学习可知,瞬时态的对象由new关键字创建,瞬时态对象转换到其他状态总结如下:

1. 瞬时态转换为持久态:执行Session的save()或saveOrUpdate()方法。

2.瞬时态转换为脱管态:为瞬时态对象设置持久化标识OID。

由于持久化对象状态演化图中没有涉及到瞬时态转换到脱管态的情况,这里做下简要的说明,在前面学习中可知,脱管态对象存在OID,但是没有Session的关联,也就是说脱管态和瞬时态的区别就是OID有没有值,所以可以通过为瞬时态对象设置OID,使其变成脱管态对象。

简言之就是给瞬时态的对象手动赋予一个OID就转变为了脱管态

Customer customer = new Customer(); // 瞬时态

customer.setCust_id(1); // 脱管态

2、持久态对象转换到其他状态

持久化对象可以直接通过Hibernate 中Session的get()、load()方法,或者Query查询从数据库中获得,持久态对象转换到其他状态总结如下:

1.持久态转换为瞬时态:执行Session的delete()方法,需要注意的是被删除的持久化对象,不建议再次使用。

2.持久态转换为脱管态:执行Session的evict()、close()或clear()方法。evict ()方法用于清除一级缓存中某一个对象;close()方法用于关闭Session,清除一级缓存;clear()方法用于清除一级缓存的所有对象。

3、脱管态对象转换到其他状态

 脱管态对象无法直接获得,是由其他状态对象转换而来的,脱管态对象转换到其他状态总结如下:

1.脱管态转换为持久态:执行Session的update ()、saveOrUpdate()或lock()方法。

2.脱管态转换为瞬时态:将脱管态对象的持久化标识OID设置为null。

由于持久化对象状态演化图中没有涉及到脱管态转换到瞬时态的情况,这里做下简要的说明,跟瞬时态转换到脱管态的情况相似,脱管态和瞬时态的区别就是OID有没有值,所以可以通过将脱管态对象的OID设置为null,使其变成瞬时态对象。例如在session.close()操作后,加入代码customer.setCust_id(null);,customer对象将由脱管态转换为瞬时态。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值