Hibernate实战_笔记2(Hibernate范未不匹配问题)

来源:互联网 发布:三鹿乳业的网络公关 编辑:程序博客网 时间:2024/05/16 14:18

      现在,看一下当我们考虑更现实一点的事情时会发生什么。当给应用程序添加更多的实体和实体关系时,范式不匹配的问题显而易见了。
上面笔记的最明显问题是把地址设计成了一个简单的String值。在大部分系统中,必需分别保存街道、城市、省/州、国家和邮政编码的信息。当然,可以直接给User类添加这些属性,但是由于系统中的其他类极有可能也含有地址信息,这使得创建一个单独的Address类变得更有意义了。更新过的模型如图

如图User有一个Address

      也应该添加一个ADDRESS表吗?没有必要。通常只需把地址信息保存在USERS表中单独的列里。这种设计可能执行得更好,因为如果要在单个查询中获取用户和地址,就不需要表联结。最好的解决方案可能是创建一个用户定义的SQL数据类型来表示地址,在USERS表中使用类型的单个列,而不用几个新列。
基本上,可以选择添加(一种新SQL数据类型的)几个列或者单个列。这明显是个粒度(granularity)问题。

粒度问题

      粒度是指你在使用的类型的相对大小。
      回到我们的示例上。给数据库目录添加一个新的数据类型,在单个列中保存Address的Java实例,听起来像是最好的办法。Java中的一个新Address类型(类)和一个新的ADDRESS的SQL数据类型应该保证互用性。然而,如果检查当今的SQL数据库管理系统对UDT(User-defined DataType,用户定义的数据类型)的支持,就会发现各种各样的问题。
      所以在SQL数据库内部使用UDT或者Java类型,都还不是目前行业的一项共通的实践,你不可能遇到一个广泛使用UDT的遗留Schema。因此我们不能并且也不会在单个有着与Java层相同的数据类型的新列中保存新Address类的实例。

      对于这个问题,务实的解决方案是使用几列内建的SQL类型。USERS表通常定义如下:

create table USERS(USERNAME varchar(15) not null primary key,NAME varchar(50) not null,ADDRESS_STREET varchar(50),ADDRESS_CITY varchar(15),ADDRESS_STATE varchar(15),ADDRESS_ZIPCODE varchar(6),ADDRESS_COUNTRY varchar(15));

      领域模型中的类按照各种不同的粒度等级排列起来——从粗粒度实体类如User,到细粒度如Address,直到简单String值的属性如zipcode。相比之下,在SQL数据库等级中只有两种粒度等级是可见的:表(如USERS)和列(如ADDRESS_ZIPCODE)。

      当考虑依赖继承(inheritance)的领域模型时,一种更复杂和更值得关注的问题出现了。继承是面向对象设计的一个特性,可用来以一种更新更有趣的方式给电子商务应用程序中的用户开帐单。

子类型问题

      在Java中,用超类(superclass)和子类(subclass)实现类型继承。为了举例说明为什么这会出现不匹配的问题,我们来添加到电子商务应用程序中,以便现在不仅可以接受银行帐户的帐单,还可以信息卡和借记卡。在该模型中体现这种变化的最自然方式是给BillingDetails类使用继承。


为不同的记帐策略使用继承

      SQL或许应该包括对超表(supertable)和子表(subtable)的标准支持。这实际上支持我们创建从父表中继承某些列的表。然而,这样的一种特性备受置疑,因为它会在基表(base table)中引进一个新的概念:虚拟列(virtual column)。一旦把继承引进到模型中,就有了多态(polymorphism)的可能。
User类和BillingDetails超类有一个关联。这是个多态关联(polymorphic association)。运行时,User对象可以引用BillingDetails任何子类的一个实例。类似地我们想要能够编写引用BillingDetails类的多态查询(polymorphic query),并让查询返回它子类的实例。
      子类型的这种不匹配的结果是,模型中的继承结构必须在一个不提供继承策略的SQL数据库中被持久化。庆幸的是,第5章介绍的继承映射解决方案中,有3种被设计为适应多态关联的表示法和多态查询的有效执行。
对象/关系不匹配问题的另一个方面是对象同一性(object identity)。你可能注意到了我们把USERNAME定义为USERS表的主键。这是个好办法吗?我们如何处理Java中的同一对象?

同一性问题

例如当需要检查两个对象是否为同一对象的时候。解决这个问题有3种方法 :2种使用Java,1种使用SQL数据库。
Java对象定义两个不同的同一性(sameness)概念:
1、对象同一性(粗略等同于内存位置,用a==b检查)
2、等同性,通过equals()方法的实现来确定
另一方面,数据库行的同一性用主键值表达。equals()和==都不会必然等于主键值。几个不恒等的对象同时表示数据库的同一行很觉,例如,在并发运行的应用程序线程中。
所以可以把表定义变成这样:

create table USERS(USER_ID bigint not null primary key,USERNAME varchar(15) not null unique,NAME varchar(50) not null,...);create table BILLING_DETAILS(BILLING_DETAILS_ID bigint not null primary key,ACCOUNT_NUMBER varchar(10) not null unique,ACCOUNT_NAME varchar(50) not null,ACCOUNT_TYPE varchar(2) not null,USER_ID bigint foreign key references USERS);

      目前为止,我们设计的构架电子商务应用程序已经识别了映射粒度、子类型和对象同一性的不匹配问题。我们差不多准备转向应用程序的其他部分了。

4 0
原创粉丝点击