SlideShare a Scribd company logo
对象 / 关系映射— Hibernate 学员要求 :熟悉 Java 、 SQL 、 JDBC ,掌握面向对象的 开发 方法,并有实际项目开发经验 课程目标: 理解 O/R Mapping 原理,掌握 Hibernate 开发的相关知识,并能使用 Hibernate 进行实际项目开发 作者:赵青
目  录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
数据的持久化 持久化  持久层 持久化如何演变为持久层? 是不是只要在应用中用了数据库就天然具备了“持久层”了呢?未必!
只有持久化而没有持久层 网上商城购物结算的例子
 
没有持久层的特征 业务逻辑和数据库访问逻辑混杂在一起,没有清晰的界限,干扰了我们的视线,难于理解。 业务规则的变动必然影响到数据库的访问逻辑,反之亦然,笨重,难于维护。 好处是:简单方便、开发迅速,不需要复杂的设计,比较适合于业务简单的应用。
引入持久层后的系统架构
改良后的设计
引入 DAO 模式 DAO = Data Accessor Object  数据访问对象 数据库访问的实现细节被隐藏到 DAO 里面。 Domain Object 则提供了面向领域的对象,封装了具体的业务规则。
引入 DAO 模式的优点 业务层无需关心具体的 select 、 insert 等操作,使得业务业务逻辑实现更加清晰,也使得开发人员的专业划分成为可能,业务人员专注于业务逻辑编码。 业务层和持久层可以彼此独立的变化,比如:仅仅替换数据访问层的实现,可以将系统部署在不同的数据库平台上。
改良后的代码
观察 DAO 的实现细节
问题的症状 - 用 JDBC 实现持久层 为域中的每个类手工编写持续性代码的工作量繁重。 这些代码基本上都是“支撑性”代码,单调、机械、乏味、不优雅。 特别是需要支持多种 SQL 方言时,对于持久层的开发者是个大难题。
新需求的产生 - 通用的持久层框架 将编写支撑性代码的工作量降到最低。编码是有趣的工作,但是敲键盘决不有趣。凡是无趣的工作都交给机器去完成。 对象模型和关系模型的映射( ORM ),编码时只需要关心对象,而无需再纠缠于 JDBC ResultSet 中的字段。 更好的移植性,只需要简单的修改配置参数,即可实现底层数据库的切换。
目  录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
Hibernate 在应用中的位置 基于 B/S 的典型三层架构 开发如何分层? 业务逻辑层和持久化层绝不要依赖于展现层。 持久层对于业务层是透明的,持久层和业务层的变化是彼此独立的。
Hibernate 核心架构
Configuration Configuration  类负责管理 Hibernate  的配置信息。它包括如下内容: Hibernate 运行的底层信息:数据库的 URL 、用户名、密码、 JDBC 驱动类,数据库 Dialect, 数据库连接池等。 Hibernate 映射文件( *.hbm.xml )。 Configuration cfg = new Configuration();
SessionFactory 会话工厂缓存了生成的 SQL 语句和 Hibernate 在运行时使用的映射元数据。 会话工厂在应用初始化时被创建,是一个重量级的类,它在多个应用线程间进行被共享,通常情况下,整个应用只有唯一的一个会话工厂,然而,如果你使用 Hibernate 访问多个数据库,你需要对每一个数据库使用一个会话工厂。 应用程序从会话工厂里获得 Session( 会话 ) 实例。 SessionFactory sessionFactory = cfg.buildSessionFactory();
Session Session 也称为持久化管理器,因为它是与持久化有关的操作接口。 Session 代表与数据库之间的一次操作。 Session 通过 SessionFactory 打开,在所有的工作完成后,需要关闭。 会话并不是线程安全的因此应该被设计为每次只能在一个线程中使用 。 Session session = sessionFactory.openSession();
Transaction  ( 事务 ) Transaction 将应用代码从底层的事务实现中抽象出来——可能是一个 JDBC 事务或一个 JTA 事务,这有助于保持 Hibernate 应用在不同类型的执行环境或容器中的可移植性。 使用 Hibernate 进行操作时(增、删、改)必须显示的调用 Transaction (默认: autoCommit=false )。 Transaction  tx = session.beginTransaction();
目  录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
对象-关系数据库的基本映射 public class  User  { private String  name ; private String  password ; private List address; ……… } create table  tbl_user  ( name  varchar(255) not null ,  password  varchar(255), ……… . primary key (name) ) 外键 1:n/n:1 表的列 类的属性(基本类型) 记录 对象 表 类 关系数据库 对象
一个简单例子 Department.hbm.xml
Hibernate 基本数据类型
实例前的准备—项目目录结构
insert Department dep = new Department(); dep.setName(“ 软件开发部” ); Session s = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); s.save(dep); tx.commit(); s.close();
Load Session s = sessionFactory.openSession(); Department dep = (Department)s.get(Department.class, depID); s.close();
update Session s = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Department dep = (Department)s.get(Department.class, depID); dep.setName("ggggg"); s.update(dep); tx.commit(); s.close();
delete Session s = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Department dep = (Department)s.get(Department.class, depID); s.delete(dep); tx.commit(); s.close();
使用 Ant 构建开发过程 Another Neat Tool  另一个整洁的工具。 ANT 是一个基于 Java 的自动化脚本引擎,脚本格式为 XML 。  每个 ant 脚本(缺省叫 build.xml )中设置了一系列任务 (target) ,而多个任务之间往往又包含了一定的依赖关系。 Ant 可以简化项目的编译、测试、文档、部署等日常工作的手工工作量。
进一步减少编码量 -XDoclet XDoclet 的灵感来自 JavaDoc , JavaDoc 把文档写在代码里,简化了文档与程序同步问题。 为 web 、 ejb 、 struts 、 webwork 、 hibernate 、 jdo 、 jmx 等等生成描述文件、源码等。 现在的 XDoclet 已经发展成了一个全功能的、面向属性的代码生成框架。( Attribute-Oriented Programming )
目  录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
Hibernate 面临的挑战: 对象—关系模型的不匹配( Paradigm Mismatch ) 粒度问题。( granularity ) 子类型问题。( subtypes ) 同一性问题。( identity ) 关联问题。( associations ) 对象导航问题。( navigation )
Identity—— 同一性问题 对象的同一性( identity ): == 是 jvm 定义的概念。 对象的相等性( equality ): java API 定义的方法。实现 equals() 方法。 数据库对象的同一性( identity ):指向同一个表的同一个记录。
Database identity with Hibernate 为持久化类增加一个 identifier property 。 Identifier 的值等于数据库中该记录的主键值,对于业务而言没有实际意义,一般该属性命名为 id 。 通常设置 getID() 为 public ,因为通过 id 查找对象会很方便,而 setID() 设为 private ,其值由 hibernate 产生, id 不可以改变。 a == b ; a.equals(b) ; a.getID().equals(b.getID()) 。
主键的选择 natural  keys :从业务意义上寻找一个或者多个属性来区分唯一性,和是否是自动产生的无关。业务逻辑和数据逻辑位于不同的层面,应该有清晰的界定,不要把业务逻辑牵扯到数据逻辑中,否则业务逻辑的变化将对数据逻辑产生根本的影响。 synthetic identifiers ( surrogate  keys ): surrogate keys  没有业务含义,它是由数据库或者应用产生的。 composite keys :多个 natural keys 联合组成的 primary key 。历史的遗留系统无法避免。
identifier generator—— 主键生成策略 <id  name=&quot;departmentID&quot;  column=&quot;department_id&quot;  type=&quot;long&quot; >  <generator class=“native&quot;></generator>  </id> native : hibernate 将根据底层数据库的方言( Dialect )来选择, SQLServer 用 identity , Oracle 用 sequence 等。
increment :主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加 1 作为主键。应用于 single-server 的环境下特别高效,如果被部署成多个应用的环境,会造成主键重复错误。 uuid.hex :用一个 128-bit 的 UUID 算法生成字符串类型的标识符。使用了 IP 地址, JVM 的启动时间(精确到 1/4 秒),系统时间和一个计数器值(在 JVM 中唯一)。用该算法生成的 id 可以确保在一个网络中唯一。适用于多应用的环境,即使在多实例并发的环境中也可以确保唯一。并且解决了多个数据库的部分数据合并。
granularity—— 粒度问题 fine-grained object model  适当的细粒度对象模型:所谓细粒度模型,就是将原本业务模型中的对象加以细分,从而得到更加精细的对象模型。
细粒度模型的设计 该设计体现了一个“合成 ”的关系( composition ):即整体和部分的关系。部分不可独立存在而依赖于整体。 sendMessage() 这种细粒度的设计更好的体现了类的内聚性,体现了对象设计的职责分配原则:将职责分配给拥有履行一个职责所必需信息的类 。
数据库设计的思考 这样的设计是不必要的,并且存在性能问题。
Entity and Component 在 Java 中不存在实体类和 component 类的区别,所有类的含义都是相同的。 持久化的类需要对应数据库的表,而表中记录的唯一性是通过主键实现的。故持久化的类需要区分实体类和 component 类。 Entity 类有数据库的主键值,  Entity 类有自己的生命周期,它不依赖于其他的类而独立存在。 component 类没有相应的主键值,它从属于 Entity ,它的生命周期从属于 Entity ,随着 Entity 的消亡而消亡。  component 类也称 value type 。
映射文件的格式
Subtypes—— 子类型问题 对象模型存在“ is a” 和“ has a” 的关系,而关系模型仅仅存在“ has a” 的关系,这是对象模型和关系模型最明显的 mismatch ,如何将“ is a” 转化为数据库中的“ has a” 是 hibernate 需要解决的问题。
Table per concrete class 这是最简单的一种方式:每一个子类对应一张表,父类没有表。 这种方式会产生“ Polymorphic queries” 的问题。
Polymorphic queries— 多态查询 对于父类的查询需要运行多条 select 语句,每个子类一条。 select CREDIT_CARD_ID, OWNER, NUMBER, CREATED, TYPE, ...from CREDIT_CARDwhere CREATED = ? select BANK_ACCOUNT_ID, OWNER, NUMBER, CREATED, BANK_NAME, ...from BANK_ACCOUNT where CREATED = ? 父类的变动困难:父类的变动会影响所有的子类属性,从而会影响多个表中的字段。 仅仅用在不需要多态查询的场合。
Table per class hierarchy 整个继承树对应一张表,子类用 type discriminator 字段来区分。这种方式在性能和简单性两方面都做的很好。 父类的变动很方便。
多态查询 查询父类 select BILLING_DETAILS_ID, BILLING_DETAILS_TYPE,OWNER, ..., CREDIT_CARD_TYPE, from BILLING_DETAILS where CREATED = ? 查询子类 select BILLING_DETAILS_ID,CREDIT_CARD_TYPE,CREDIT_CARD_EXP_MONTH, ...from BILLING_DETAILS where BILLING_DETAILS_TYPE='CC' and CREATED = ? problem :子类属性对应的 column 不可以有 not null 的约束。
映射文件的格式
Table per subclass 这种设计符合数据库的设计范式。 但是可能会有严重的性能问题。
多态查询的解决——父类 查询父类:用 outer join
多态查询的解决——子类 查询子类:用 inner join 这种方式如果用手工写代码完成则很困难。 查询需要关联多张表,对于复杂的继承树结构,性能是个大问题。
映射文件格式
继承策略选择 一般原则:如果你不需要多态查询,可以考虑用 table-per-concrete-class ,如果你需要多态查询,并且子类的属性差异不大,考虑用 table-per-class-hierarchy ,但是如果子类的属性差异很大,考虑用 table-per-subclass 。 经验:对于一般的解决,尽量用 table-per-class-hierarchy ,  table-per-subclass 请慎重使用。
associations— 关联 对象之间通过 reference 和 reference 集合来关联,而关系模型则通过外键进行关联。 对象的 reference 是有方向性的,始终是单向的,如果需要双向的,则需要定义两次;外键则没有方向性,或者说天然就是双向的,因此导航对于关系模型没有意义。 对象模型存在多对多的关系,而关系模型只有 one-to-many 和 one-to-one 。 如果关系模型要实现多对对,需要一个 link table , 而这个 link table 不存在于对象模型中。
many to one
one to many
双向关联产生的问题 在内存中有两个不同的地方代表同一个值:即外键 item_id 如果我们调用了 bid.setItem(item);bids.add(bid); hibernate 会认为是两个不同的持久类发生了变动,它并不知道这两个变动实际上是指向同一个数据库的字段, hibernate 会更新两次。 我们需要告诉 hibernate 这是一个双向的关联。
主控方和被控方 inverse=“true” 即告诉 hibernate 对方是主控方。 由 bid 端负责保持和 数据库的同步。 如果调用 bids.add(bid); 则不发生任何持久化动作,只有调用了 bid.setItem(item); 才持久化。 原则:应该将 many 端设为主控方,这样有助于改善性能。
cascading save 当我们把 bid 加入到 item , 并且把 item 持久化的时候, 我们希望 bid 能够自动的 持久化,而不用显示的去调用。 cascade 属性告诉 hibernate bid 可以被级联持久化。 cascade 是有方向性的,也可以在 bid 端设置级联持久化 item ,但是因为 bid 是在 item 后创建的,这样做没有意义。
cascading delete Item  和 bid 应该是父子关系, item 如果被删除, bid 也应被删除。子对象的生命周期依赖于父对象。 这类似于 Entity/Component 的关系,但是有本质的区别。 Bid 可以单独的被加载,而 component 不能; bid 可被共享而 component 不能。 bid 如果被 item 内的集合删除,则应该在持久化层被删除。
目  录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
对象的持久生命周期 持久类和一般类只有概念上的区别,从代码上看没有区别,持久类不知道自己的持久状态,所有的业务逻辑也与对象是在内存中还是在数据库中无关。 内存中的对象只有两种状态:有用和无用。 Hibernate 通过 session 来控制对象的持久生命周期: transient,persistent ,  detached.
Transient objects new 生成的对象称为 Transient , 它没有与数据库中的某一行记录关联,一旦它被 dereferenced 就会被 JVM 回收。 hibernate 认为所有的 transient 对象都是非事务的, hibernate 不提供对这些对象的回滚支持。 仅仅被 transient 对象 reference 的对象也是 transient 对象。
Persistent objects persistent 对象对应数据库的一条记录,并且具有 database identity 。 对于 transient 对象,调用 session.save() 可以将其转变为 persistent object 。如果通过 session.load() 加载一个对象,该对象也是 persistent 状态。 hibernate 对 persistent 对象提供与数据库的同步支持和事务支持。
与数据库的同步 脏数据:数据仅仅在内存中更新而没有同步到数据库中称为脏数据。 hibernate 会监测脏数据,在尽可能迟的时候做同步的动作。( transparent transaction-level write-behind ) hibernate 可以做到仅仅更新有变动的属性,但是需要在映射文件中设置 dynamic-update=“true” ,默认是 false 。这个功能用手工很难完成。
Detached objects 当调用 session.close() ,原先的 persistent object 就转化为 detached object 。 detached object 和数据库失去了联系,但是它们不是 transient object ,它们具有 datebase identity 。 hibernate 可以在新的事务中重新联系 detached object 。这样可以在多个层面中传递这些持久对象。对于多层架构的设计产生重大影响。
例子—— 1
例子—— 2
例子—— 3
对象的状态图
区分 transient and detached 对象 Identifier 属性是否为 null 对于非对象类型的 Identifier ,判断 unsaved-value 的值 优先考虑使用对象类型的 Identifier ,因为对象可以为 null 。 该方法只对 synthetic keys 有效,对于 assigned keys 和 composite keys 无效。
The scope of object identity no identity scope :数据的同一性没有范围,同一条记录分别取两次,返回的对象不满足 a==b ; transaction-scoped identity :在同一个事务中,同一条记录取两次,满足 a==b ,在事务级别需要缓存; Process-scoped identity :在同一个进程中只有一个对象被返回,范围达到整个 JVM 。
对象树 通常大型应用中操作的不可能只有一个对象,而是一个对象图。
Persistence by reachability compute 被持久化时,它所 reference 的任何对象都被持久化,但是不包括“ Electronics”  和 “ Cell Phones” 。 这是一个递归的过程。 在理想的环境中: root 对象被加载,那么整个对象树在任何时候都可以被重新建立。如果某一个对象是非 reachable 的,那么就应该成为 transient 。
不可能的任务 数据库没有 root 的概念,只有 foreign key 。 Java 有垃圾回收的算法,而数据库没有。 如果数据库要实现一个类似的,需要做全表扫描,那时不现实的。 对象图只是数据库的一部份。 persistence by reachability 只解决了一半问题,如何完成 persistence -> transient 。
解决之道— Cascade none :默认设置 ,hibernate 不做级联任何动作。 save-update :如果 item 被持久化,那么所有被 item 所 reference 的 bid 都应该被持久化。 delete :如果 item 被删除,那么 item 所 reference 的 bid 的被删除。 all : = save-update + delete delete-orphan :如果 Item 所属的 Bid Set 中的某个 Bit 被删除 ,  那么数据库中 Bid 也被删除 .
目  录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
重新思考项目之间的关系
etong-common 的依赖关系
etong-common 的内部体系
持久包—通用的持久化类设计
优化设计后的代码 User user = new User(); user.setName(name); user.setPassword(password); user.setEmail(email); PersistenceFactory.getInstance().makePersistent(user);
沉重的反思——事务脚本 粒度顺序是: service > dao > domain 业务逻辑尽量写在 domain 里,不要在 service 写任何业务逻辑,而仅仅在 service 里调用 dao 和 domain ,完成事务逻辑,供前台调用。
参考书籍 Hibernate In Action——CHRISTIAN BAUER 、 GAVIN KING Patterns of Enterprise Application Architecture ( 企业应用架构模式 )——Martin Fowle 深入浅出 Hibernate—— 夏昕、曹晓钢

More Related Content

PPT
Hibernate
PPT
Hibernate教程
PDF
Entities in DCPS (DDS)
PPT
3 hibernate映射元素和类型
PDF
领域驱动设计实践
PPT
Struts学习笔记
PPT
OOAD with Applications - Chapter 2
PPS
POSTURAS COCHINAS
 
Hibernate
Hibernate教程
Entities in DCPS (DDS)
3 hibernate映射元素和类型
领域驱动设计实践
Struts学习笔记
OOAD with Applications - Chapter 2
POSTURAS COCHINAS
 

Similar to hibernate (20)

PPTX
前端基础知识回顾
PPTX
An introduce to n hibernate (part 1) pub
PPT
Metadata4shenzhen Final
DOC
Java面试笔试题大汇总
PDF
getPDF.aspx
PDF
getPDF.aspx
DOC
Java相关基础知识
PDF
Chapter 4 models
PPT
Hibernate 映射配置文件详解
DOC
Java程序员面试之葵花宝典
PPT
Java Script 引擎技术
PPT
深入理解Andorid重难点
PPT
Js培训
PDF
Struts+Spring+Hibernate整合教程
PDF
Struts+Spring+Hibernate整合教程
PPT
资源整合与Web2.0
PPT
资源整合与Web2.0
PPT
Kevenjiaoda
PPTX
Ecma script edition5-小试
PPT
Abap oo
前端基础知识回顾
An introduce to n hibernate (part 1) pub
Metadata4shenzhen Final
Java面试笔试题大汇总
getPDF.aspx
getPDF.aspx
Java相关基础知识
Chapter 4 models
Hibernate 映射配置文件详解
Java程序员面试之葵花宝典
Java Script 引擎技术
深入理解Andorid重难点
Js培训
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
资源整合与Web2.0
资源整合与Web2.0
Kevenjiaoda
Ecma script edition5-小试
Abap oo
Ad

hibernate

  • 1. 对象 / 关系映射— Hibernate 学员要求 :熟悉 Java 、 SQL 、 JDBC ,掌握面向对象的 开发 方法,并有实际项目开发经验 课程目标: 理解 O/R Mapping 原理,掌握 Hibernate 开发的相关知识,并能使用 Hibernate 进行实际项目开发 作者:赵青
  • 2. 目 录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
  • 3. 数据的持久化 持久化  持久层 持久化如何演变为持久层? 是不是只要在应用中用了数据库就天然具备了“持久层”了呢?未必!
  • 5.  
  • 9. 引入 DAO 模式 DAO = Data Accessor Object 数据访问对象 数据库访问的实现细节被隐藏到 DAO 里面。 Domain Object 则提供了面向领域的对象,封装了具体的业务规则。
  • 10. 引入 DAO 模式的优点 业务层无需关心具体的 select 、 insert 等操作,使得业务业务逻辑实现更加清晰,也使得开发人员的专业划分成为可能,业务人员专注于业务逻辑编码。 业务层和持久层可以彼此独立的变化,比如:仅仅替换数据访问层的实现,可以将系统部署在不同的数据库平台上。
  • 13. 问题的症状 - 用 JDBC 实现持久层 为域中的每个类手工编写持续性代码的工作量繁重。 这些代码基本上都是“支撑性”代码,单调、机械、乏味、不优雅。 特别是需要支持多种 SQL 方言时,对于持久层的开发者是个大难题。
  • 14. 新需求的产生 - 通用的持久层框架 将编写支撑性代码的工作量降到最低。编码是有趣的工作,但是敲键盘决不有趣。凡是无趣的工作都交给机器去完成。 对象模型和关系模型的映射( ORM ),编码时只需要关心对象,而无需再纠缠于 JDBC ResultSet 中的字段。 更好的移植性,只需要简单的修改配置参数,即可实现底层数据库的切换。
  • 15. 目 录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
  • 16. Hibernate 在应用中的位置 基于 B/S 的典型三层架构 开发如何分层? 业务逻辑层和持久化层绝不要依赖于展现层。 持久层对于业务层是透明的,持久层和业务层的变化是彼此独立的。
  • 18. Configuration Configuration 类负责管理 Hibernate 的配置信息。它包括如下内容: Hibernate 运行的底层信息:数据库的 URL 、用户名、密码、 JDBC 驱动类,数据库 Dialect, 数据库连接池等。 Hibernate 映射文件( *.hbm.xml )。 Configuration cfg = new Configuration();
  • 19. SessionFactory 会话工厂缓存了生成的 SQL 语句和 Hibernate 在运行时使用的映射元数据。 会话工厂在应用初始化时被创建,是一个重量级的类,它在多个应用线程间进行被共享,通常情况下,整个应用只有唯一的一个会话工厂,然而,如果你使用 Hibernate 访问多个数据库,你需要对每一个数据库使用一个会话工厂。 应用程序从会话工厂里获得 Session( 会话 ) 实例。 SessionFactory sessionFactory = cfg.buildSessionFactory();
  • 20. Session Session 也称为持久化管理器,因为它是与持久化有关的操作接口。 Session 代表与数据库之间的一次操作。 Session 通过 SessionFactory 打开,在所有的工作完成后,需要关闭。 会话并不是线程安全的因此应该被设计为每次只能在一个线程中使用 。 Session session = sessionFactory.openSession();
  • 21. Transaction ( 事务 ) Transaction 将应用代码从底层的事务实现中抽象出来——可能是一个 JDBC 事务或一个 JTA 事务,这有助于保持 Hibernate 应用在不同类型的执行环境或容器中的可移植性。 使用 Hibernate 进行操作时(增、删、改)必须显示的调用 Transaction (默认: autoCommit=false )。 Transaction tx = session.beginTransaction();
  • 22. 目 录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
  • 23. 对象-关系数据库的基本映射 public class User { private String name ; private String password ; private List address; ……… } create table tbl_user ( name varchar(255) not null , password varchar(255), ……… . primary key (name) ) 外键 1:n/n:1 表的列 类的属性(基本类型) 记录 对象 表 类 关系数据库 对象
  • 27. insert Department dep = new Department(); dep.setName(“ 软件开发部” ); Session s = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); s.save(dep); tx.commit(); s.close();
  • 28. Load Session s = sessionFactory.openSession(); Department dep = (Department)s.get(Department.class, depID); s.close();
  • 29. update Session s = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Department dep = (Department)s.get(Department.class, depID); dep.setName(&quot;ggggg&quot;); s.update(dep); tx.commit(); s.close();
  • 30. delete Session s = sessionFactory.openSession(); Transaction tx = s.beginTransaction(); Department dep = (Department)s.get(Department.class, depID); s.delete(dep); tx.commit(); s.close();
  • 31. 使用 Ant 构建开发过程 Another Neat Tool 另一个整洁的工具。 ANT 是一个基于 Java 的自动化脚本引擎,脚本格式为 XML 。 每个 ant 脚本(缺省叫 build.xml )中设置了一系列任务 (target) ,而多个任务之间往往又包含了一定的依赖关系。 Ant 可以简化项目的编译、测试、文档、部署等日常工作的手工工作量。
  • 32. 进一步减少编码量 -XDoclet XDoclet 的灵感来自 JavaDoc , JavaDoc 把文档写在代码里,简化了文档与程序同步问题。 为 web 、 ejb 、 struts 、 webwork 、 hibernate 、 jdo 、 jmx 等等生成描述文件、源码等。 现在的 XDoclet 已经发展成了一个全功能的、面向属性的代码生成框架。( Attribute-Oriented Programming )
  • 33. 目 录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
  • 34. Hibernate 面临的挑战: 对象—关系模型的不匹配( Paradigm Mismatch ) 粒度问题。( granularity ) 子类型问题。( subtypes ) 同一性问题。( identity ) 关联问题。( associations ) 对象导航问题。( navigation )
  • 35. Identity—— 同一性问题 对象的同一性( identity ): == 是 jvm 定义的概念。 对象的相等性( equality ): java API 定义的方法。实现 equals() 方法。 数据库对象的同一性( identity ):指向同一个表的同一个记录。
  • 36. Database identity with Hibernate 为持久化类增加一个 identifier property 。 Identifier 的值等于数据库中该记录的主键值,对于业务而言没有实际意义,一般该属性命名为 id 。 通常设置 getID() 为 public ,因为通过 id 查找对象会很方便,而 setID() 设为 private ,其值由 hibernate 产生, id 不可以改变。 a == b ; a.equals(b) ; a.getID().equals(b.getID()) 。
  • 37. 主键的选择 natural keys :从业务意义上寻找一个或者多个属性来区分唯一性,和是否是自动产生的无关。业务逻辑和数据逻辑位于不同的层面,应该有清晰的界定,不要把业务逻辑牵扯到数据逻辑中,否则业务逻辑的变化将对数据逻辑产生根本的影响。 synthetic identifiers ( surrogate keys ): surrogate keys 没有业务含义,它是由数据库或者应用产生的。 composite keys :多个 natural keys 联合组成的 primary key 。历史的遗留系统无法避免。
  • 38. identifier generator—— 主键生成策略 <id name=&quot;departmentID&quot; column=&quot;department_id&quot; type=&quot;long&quot; > <generator class=“native&quot;></generator> </id> native : hibernate 将根据底层数据库的方言( Dialect )来选择, SQLServer 用 identity , Oracle 用 sequence 等。
  • 39. increment :主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加 1 作为主键。应用于 single-server 的环境下特别高效,如果被部署成多个应用的环境,会造成主键重复错误。 uuid.hex :用一个 128-bit 的 UUID 算法生成字符串类型的标识符。使用了 IP 地址, JVM 的启动时间(精确到 1/4 秒),系统时间和一个计数器值(在 JVM 中唯一)。用该算法生成的 id 可以确保在一个网络中唯一。适用于多应用的环境,即使在多实例并发的环境中也可以确保唯一。并且解决了多个数据库的部分数据合并。
  • 40. granularity—— 粒度问题 fine-grained object model 适当的细粒度对象模型:所谓细粒度模型,就是将原本业务模型中的对象加以细分,从而得到更加精细的对象模型。
  • 41. 细粒度模型的设计 该设计体现了一个“合成 ”的关系( composition ):即整体和部分的关系。部分不可独立存在而依赖于整体。 sendMessage() 这种细粒度的设计更好的体现了类的内聚性,体现了对象设计的职责分配原则:将职责分配给拥有履行一个职责所必需信息的类 。
  • 43. Entity and Component 在 Java 中不存在实体类和 component 类的区别,所有类的含义都是相同的。 持久化的类需要对应数据库的表,而表中记录的唯一性是通过主键实现的。故持久化的类需要区分实体类和 component 类。 Entity 类有数据库的主键值, Entity 类有自己的生命周期,它不依赖于其他的类而独立存在。 component 类没有相应的主键值,它从属于 Entity ,它的生命周期从属于 Entity ,随着 Entity 的消亡而消亡。 component 类也称 value type 。
  • 45. Subtypes—— 子类型问题 对象模型存在“ is a” 和“ has a” 的关系,而关系模型仅仅存在“ has a” 的关系,这是对象模型和关系模型最明显的 mismatch ,如何将“ is a” 转化为数据库中的“ has a” 是 hibernate 需要解决的问题。
  • 46. Table per concrete class 这是最简单的一种方式:每一个子类对应一张表,父类没有表。 这种方式会产生“ Polymorphic queries” 的问题。
  • 47. Polymorphic queries— 多态查询 对于父类的查询需要运行多条 select 语句,每个子类一条。 select CREDIT_CARD_ID, OWNER, NUMBER, CREATED, TYPE, ...from CREDIT_CARDwhere CREATED = ? select BANK_ACCOUNT_ID, OWNER, NUMBER, CREATED, BANK_NAME, ...from BANK_ACCOUNT where CREATED = ? 父类的变动困难:父类的变动会影响所有的子类属性,从而会影响多个表中的字段。 仅仅用在不需要多态查询的场合。
  • 48. Table per class hierarchy 整个继承树对应一张表,子类用 type discriminator 字段来区分。这种方式在性能和简单性两方面都做的很好。 父类的变动很方便。
  • 49. 多态查询 查询父类 select BILLING_DETAILS_ID, BILLING_DETAILS_TYPE,OWNER, ..., CREDIT_CARD_TYPE, from BILLING_DETAILS where CREATED = ? 查询子类 select BILLING_DETAILS_ID,CREDIT_CARD_TYPE,CREDIT_CARD_EXP_MONTH, ...from BILLING_DETAILS where BILLING_DETAILS_TYPE='CC' and CREATED = ? problem :子类属性对应的 column 不可以有 not null 的约束。
  • 51. Table per subclass 这种设计符合数据库的设计范式。 但是可能会有严重的性能问题。
  • 53. 多态查询的解决——子类 查询子类:用 inner join 这种方式如果用手工写代码完成则很困难。 查询需要关联多张表,对于复杂的继承树结构,性能是个大问题。
  • 55. 继承策略选择 一般原则:如果你不需要多态查询,可以考虑用 table-per-concrete-class ,如果你需要多态查询,并且子类的属性差异不大,考虑用 table-per-class-hierarchy ,但是如果子类的属性差异很大,考虑用 table-per-subclass 。 经验:对于一般的解决,尽量用 table-per-class-hierarchy , table-per-subclass 请慎重使用。
  • 56. associations— 关联 对象之间通过 reference 和 reference 集合来关联,而关系模型则通过外键进行关联。 对象的 reference 是有方向性的,始终是单向的,如果需要双向的,则需要定义两次;外键则没有方向性,或者说天然就是双向的,因此导航对于关系模型没有意义。 对象模型存在多对多的关系,而关系模型只有 one-to-many 和 one-to-one 。 如果关系模型要实现多对对,需要一个 link table , 而这个 link table 不存在于对象模型中。
  • 59. 双向关联产生的问题 在内存中有两个不同的地方代表同一个值:即外键 item_id 如果我们调用了 bid.setItem(item);bids.add(bid); hibernate 会认为是两个不同的持久类发生了变动,它并不知道这两个变动实际上是指向同一个数据库的字段, hibernate 会更新两次。 我们需要告诉 hibernate 这是一个双向的关联。
  • 60. 主控方和被控方 inverse=“true” 即告诉 hibernate 对方是主控方。 由 bid 端负责保持和 数据库的同步。 如果调用 bids.add(bid); 则不发生任何持久化动作,只有调用了 bid.setItem(item); 才持久化。 原则:应该将 many 端设为主控方,这样有助于改善性能。
  • 61. cascading save 当我们把 bid 加入到 item , 并且把 item 持久化的时候, 我们希望 bid 能够自动的 持久化,而不用显示的去调用。 cascade 属性告诉 hibernate bid 可以被级联持久化。 cascade 是有方向性的,也可以在 bid 端设置级联持久化 item ,但是因为 bid 是在 item 后创建的,这样做没有意义。
  • 62. cascading delete Item 和 bid 应该是父子关系, item 如果被删除, bid 也应被删除。子对象的生命周期依赖于父对象。 这类似于 Entity/Component 的关系,但是有本质的区别。 Bid 可以单独的被加载,而 component 不能; bid 可被共享而 component 不能。 bid 如果被 item 内的集合删除,则应该在持久化层被删除。
  • 63. 目 录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
  • 65. Transient objects new 生成的对象称为 Transient , 它没有与数据库中的某一行记录关联,一旦它被 dereferenced 就会被 JVM 回收。 hibernate 认为所有的 transient 对象都是非事务的, hibernate 不提供对这些对象的回滚支持。 仅仅被 transient 对象 reference 的对象也是 transient 对象。
  • 66. Persistent objects persistent 对象对应数据库的一条记录,并且具有 database identity 。 对于 transient 对象,调用 session.save() 可以将其转变为 persistent object 。如果通过 session.load() 加载一个对象,该对象也是 persistent 状态。 hibernate 对 persistent 对象提供与数据库的同步支持和事务支持。
  • 67. 与数据库的同步 脏数据:数据仅仅在内存中更新而没有同步到数据库中称为脏数据。 hibernate 会监测脏数据,在尽可能迟的时候做同步的动作。( transparent transaction-level write-behind ) hibernate 可以做到仅仅更新有变动的属性,但是需要在映射文件中设置 dynamic-update=“true” ,默认是 false 。这个功能用手工很难完成。
  • 68. Detached objects 当调用 session.close() ,原先的 persistent object 就转化为 detached object 。 detached object 和数据库失去了联系,但是它们不是 transient object ,它们具有 datebase identity 。 hibernate 可以在新的事务中重新联系 detached object 。这样可以在多个层面中传递这些持久对象。对于多层架构的设计产生重大影响。
  • 73. 区分 transient and detached 对象 Identifier 属性是否为 null 对于非对象类型的 Identifier ,判断 unsaved-value 的值 优先考虑使用对象类型的 Identifier ,因为对象可以为 null 。 该方法只对 synthetic keys 有效,对于 assigned keys 和 composite keys 无效。
  • 74. The scope of object identity no identity scope :数据的同一性没有范围,同一条记录分别取两次,返回的对象不满足 a==b ; transaction-scoped identity :在同一个事务中,同一条记录取两次,满足 a==b ,在事务级别需要缓存; Process-scoped identity :在同一个进程中只有一个对象被返回,范围达到整个 JVM 。
  • 76. Persistence by reachability compute 被持久化时,它所 reference 的任何对象都被持久化,但是不包括“ Electronics” 和 “ Cell Phones” 。 这是一个递归的过程。 在理想的环境中: root 对象被加载,那么整个对象树在任何时候都可以被重新建立。如果某一个对象是非 reachable 的,那么就应该成为 transient 。
  • 77. 不可能的任务 数据库没有 root 的概念,只有 foreign key 。 Java 有垃圾回收的算法,而数据库没有。 如果数据库要实现一个类似的,需要做全表扫描,那时不现实的。 对象图只是数据库的一部份。 persistence by reachability 只解决了一半问题,如何完成 persistence -> transient 。
  • 78. 解决之道— Cascade none :默认设置 ,hibernate 不做级联任何动作。 save-update :如果 item 被持久化,那么所有被 item 所 reference 的 bid 都应该被持久化。 delete :如果 item 被删除,那么 item 所 reference 的 bid 的被删除。 all : = save-update + delete delete-orphan :如果 Item 所属的 Bid Set 中的某个 Bit 被删除 , 那么数据库中 Bid 也被删除 .
  • 79. 目 录 持久层的概念及必要性 hibernate 框架及核心类介绍 hibernate 进行持久化的一个例子 hibernate 如何解决对象和模型的不匹配 对象的持久性生命周期 对目前项目的进一步封装和思考
  • 84. 优化设计后的代码 User user = new User(); user.setName(name); user.setPassword(password); user.setEmail(email); PersistenceFactory.getInstance().makePersistent(user);
  • 85. 沉重的反思——事务脚本 粒度顺序是: service > dao > domain 业务逻辑尽量写在 domain 里,不要在 service 写任何业务逻辑,而仅仅在 service 里调用 dao 和 domain ,完成事务逻辑,供前台调用。
  • 86. 参考书籍 Hibernate In Action——CHRISTIAN BAUER 、 GAVIN KING Patterns of Enterprise Application Architecture ( 企业应用架构模式 )——Martin Fowle 深入浅出 Hibernate—— 夏昕、曹晓钢