资源预览内容
第1页 / 共86页
第2页 / 共86页
第3页 / 共86页
第4页 / 共86页
第5页 / 共86页
第6页 / 共86页
第7页 / 共86页
第8页 / 共86页
第9页 / 共86页
第10页 / 共86页
亲,该文档总共86页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
对象对象/关系映射关系映射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核心架构核心架构ConfigurationConfiguration 类负责管理Hibernate 的配置信息。它包括如下内容:Hibernate运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等。Hibernate映射文件(*.hbm.xml)。Configuration cfg = new Configuration();SessionFactory会话工厂缓存了生成的SQL语句和Hibernate在运行时使用的映射元数据。会话工厂在应用初始化时被创建,是一个重量级的类,它在多个应用线程间进行被共享,通常情况下,整个应用只有唯一的一个会话工厂,然而,如果你使用Hibernate访问多个数据库,你需要对每一个数据库使用一个会话工厂。应用程序从会话工厂里获得Session(会话)实例。SessionFactory sessionFactory = cfg.buildSessionFactory();SessionSession也称为持久化管理器,因为它是与持久化有关的操作接口。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外键一个简单例子一个简单例子Hibernate基本数据类型基本数据类型实例前的准备实例前的准备项目目录结构项目目录结构insertDepartment dep = new Department();dep.setName(“软件开发部”);Session s = sessionFactory.openSession();Transaction tx = s.beginTransaction();s.save(dep);tx.commit();s.close();LoadSession s = sessionFactory.openSession();Department dep = (Department)s.get(Department.class, depID);s.close();updateSession 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();deleteSession 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可以简化项目的编译、测试、文档、部署等日常工作的手工工作量。进一步减少编码量进一步减少编码量-XDocletXDoclet的灵感来自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主键生成策略主键生成策略 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 oneone 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 deleteItem 和bid应该是父子关系,item如果被删除,bid也应被删除。子对象的生命周期依赖于父对象。这类似于Entity/Component的关系,但是有本质的区别。Bid可以单独的被加载,而component不能;bid可被共享而component不能。bid如果被item内的集合删除,则应该在持久化层被删除。目目 录录持久层的概念及必要性hibernate框架及核心类介绍hibernate进行持久化的一个例子hibernate如何解决对象和模型的不匹配对象的持久性生命周期对象的持久性生命周期对目前项目的进一步封装和思考对象的持久生命周期对象的持久生命周期持久类和一般类只有概念上的区别,从代码上看没有区别,持久类不知道自己的持久状态,所有的业务逻辑也与对象是在内存中还是在数据库中无关。内存中的对象只有两种状态:有用和无用。Hibernate通过session来控制对象的持久生命周期:transient,persistent, detached.Transient objectsnew生成的对象称为Transient,它没有与数据库中的某一行记录关联,一旦它被dereferenced就会被JVM回收。hibernate认为所有的transient对象都是非事务的,hibernate不提供对这些对象的回滚支持。仅仅被transient对象reference的对象也是transient对象。Persistent objectspersistent对象对应数据库的一条记录,并且具有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 identityno identity scope:数据的同一性没有范围,同一条记录分别取两次,返回的对象不满足a=b;transaction-scoped identity:在同一个事务中,同一条记录取两次,满足a=b,在事务级别需要缓存;Process-scoped identity:在同一个进程中只有一个对象被返回,范围达到整个JVM。对象树对象树通常大型应用中操作的不可能只有一个对象,而是一个对象图。Persistence by reachabilitycompute被持久化时,它所reference的任何对象都被持久化,但是不包括“Electronics” 和 “Cell Phones”。这是一个递归的过程。在理想的环境中:root对象被加载,那么整个对象树在任何时候都可以被重新建立。如果某一个对象是非reachable的,那么就应该成为transient。不可能的任务不可能的任务数据库没有root的概念,只有foreign key。Java有垃圾回收的算法,而数据库没有。如果数据库要实现一个类似的,需要做全表扫描,那时不现实的。对象图只是数据库的一部份。persistence by reachability只解决了一半问题,如何完成persistence - transient。解决之道解决之道Cascadenone:默认设置,hibernate不做级联任何动作。save-update:如果item被持久化,那么所有被item所reference的bid都应该被持久化。delete:如果item被删除,那么item所reference的bid的被删除。all:= save-update + deletedelete-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 ActionCHRISTIAN BAUER、GAVIN KINGPatterns of Enterprise Application Architecture (企业应用架构模式)Martin Fowle深入浅出Hibernate夏昕、曹晓钢
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号