元数据引擎设计思考.docx
《元数据引擎设计思考.docx》由会员分享,可在线阅读,更多相关《元数据引擎设计思考.docx(35页珍藏版)》请在冰豆网上搜索。
元数据引擎设计思考
javameta-date元数据
7.2 关于元数据
系统开发中存在各种各样的数据,比如Tom是一个年龄为30岁的男性员工、Liliy是一个21岁的女性员工、这张报表是今年第三季度的利润表、那张报表是今年上半年的销售波动图、对话框上有三个按钮控件、窗口上有一个多行文本控件和一个保存按钮、这个WebService提供了股票实时情况查询的服务、那个WebService提供了查询天气预报的服务。
以上数据存在很多共性的特征,这些特性都可以通过某种形式进行抽象。
对于“Tom是一个年龄为30岁的男性员工”、“Liliy是一个21岁的女性员工”,在数据库级别就会抽象成含有FIdVarchar(50)、FNameVarchar(50)、FAge(int)、FSex(int)四个字段的数据库表T_Employee,在Hibernate中就被抽象成含有id、name、age、sex四个字段的JavaBean以及对应的hbm配置文件。
这些数据是平台无关的,在描述“Tom是一个年龄为30岁的男性员工”这条数据的时候,它即可以是保存在数据库中的,也可以是保存在XML配置文件中的,甚至有可能只是写在一张便条上的。
与此相反的是,对这些数据的抽象方式大都是与特定平台相关的,是无法移植的。
比如要把数据的存储方式由数据库改为XML文档,那么就必须针对XML文件的存取特点重新进行抽象。
由于抽象方式是平台相关的,这些抽象出来的模型就不具有通用性,无法通过统一的方式来读取它们。
比如要读懂T_Employee这张表中的字段的含义就要去查阅数据字典,要读懂便条上的“Tom30m”就要去询问写便条的人。
元数据(MetaData)是MDA中非常重要的概念。
它通过统一的、平台无关的、规范的方式对数据的模式特征进行描述,通过一个模型结构来表达通用的信息,它集设计模型、开发模型与运行模型为一体。
元数据具有如下几个作用。
(1)元数据是独立于平台的,无论使用什么技术平台,元数据本身是不受影响的,这保证了先期工作成果的效用最大化。
(2)元数据是生成平台相关模型的基础,可以使用代码生成器等工具将元数据转换成平台相关代码。
(3)元数据为运行时系统提供了统一的可读的系统模型,系统运行时可以使得实体对象通过运行时元数据模型来得知自身的结构、自身的特征、在系统模型中的位置以及与其他对象之间的关系等。
这样就可以从一个新的角度来观察、设计、开发系统。
(4)元数据模型是系统运行不可或缺的部分,如果直接修改平台相关代码而不修改元数据,就会造成系统运行异常,这就强迫保证元数据模型与代码同步,保证了设计模型和实现代码的一致性。
(5)元数据本身就是一个设计模型。
系统设计人员可以使用元数据进行系统建模,在某种程度上元数据可以取代UML图等传统的设计模型。
设计人员将设计完成的元数据模型交给开发人员,开发人员使用代码生成器将元数据转换成平台相关代码,然后就可以基于这些平台相关代码进行开发了。
元数据起到了设计人员和开发人员沟通桥梁的作用,设计人员的工作立即就可以转换为可以运行的平台相关代码。
7.2.1 元数据示例
枚举类型在不同的系统中有不同的表示方式,而且有不同的模型描述方式(即枚举有哪些项、项的值是多少等信息),有的平台还没有提供足够的模型描述方式。
客户类型包括:
普通客户、会员客户、VIP客户。
在JDK1.5中可以表示为enumCustomerTypeEnum{Normal,Member,VIP},取得CustomerTypeEnum枚举类型中定义的所有枚举项的方法为CustomerTypeEnum.values(),取得“Normal”这个字符串对应的枚举项的方法为Enum.valueOf(CustomerTypeEnum.class,"Normal")。
在JDK1.4中使用ApacheCommons包提供的Enum类可以表示为:
publicclassCustomerTypeEnumextendsmons.lang.enums.Enum
{
publicstaticDataTypeEnumNormal=newDataTypeEnum("Normal");
publicstaticDataTypeEnumMember=newDataTypeEnum("Member");
publicstaticDataTypeEnumVIP=newDataTypeEnum("VIP");
privateDataTypeEnum(Stringname)
super(name);
}
取得CustomerTypeEnum枚举类型中定义的所有枚举项的方法为EnumUtils.get-EnumList(CustomerTypeEnum.class),取得“Normal”这个字符串对应的枚举项的方法为EnumUtils.getEnum(CustomerTypeEnum.class,"Normal")。
在C#中,可以表示为enumCustomerTypeEnum{Normal,Member,VIP},取得Customer-TypeEnum枚举类型中定义的所有枚举项的方法为Enum.GetNames(typeof(CustomerTypeEnum)),取得“Normal”这个字符串对应的枚举项的方法为Enum.Parse(typeof(CustomerTypeEnum),"Normal")。
在Delphi中,可以表示为typeCustomerTypeEnum=(Normal,Member,VIP);没有提供取得CustomerTypeEnum枚举类型中定义的所有枚举项的方法,取得“Normal”这个字符串对应的枚举项的方法也没有直接提供,必须借助RTTI。
要将一个平台上的CustomerTypeEnum移植到另一个平台,必须用目标平台的枚举语法重新改写,而且使用的取得枚举类描述信息的方式也要发生变化,这都给系统的移植带来了很大的工作量。
【例7.1】元数据示例。
为了解决这个问题,我们设计一个元数据模型:
CustomerTypeEnum
提供一个描述这个元数据模型的描述类:
//枚举描述类
publicclassEnumInfo
…
//得到所有的枚举项
publicEnumItemInfo[]getEnumItems();
//得到名字为name的枚举项的信息
publicEnumItemInfogetEnumItem(Stringname);
//枚举项描述类
publicclassEnumItemInfo()
//枚举项的名字
publicStringgetName();
//枚举项的显示信息
publicStringgetDisplayName();
提供一个读取元数据模型的API:
publicclassEnumMetaDataLoader
//加载元数据类型enumTypeName对应的元数据模型
publicEnumInfoloadEnum(StringenumTypeName)
枚举元数据模型的描述类和读取元数据模型的API的实现代码仍然是平台相关的,因为这些类都是要被特定平台使用的。
因为XML解析在各个平台是大同小异的,所以这些描述类和API的实现方式的移植是非常简单的。
使用这样的元数据模型我们还可以定义其他的枚举类型,比如:
SexEnum
在JDK1.4平台下,使用代码生成器将SexEnum的元数据模型转换成JDK1.4下的枚举代码:
publicclassSexEnumextendsmons.lang.enums.Enum
publicstaticSexEnumMale=newDataTypeEnum("Male");
publicstaticSexEnumFemale=newDataTypeEnum("Female");
privateSexEnumStringname)
当要得到所有SexEnum定义的枚举项的时候,按如下方式调用:
EnumInfoenumInfo=EnumMetaDataLoader.getInstance().loadEnum("SexEnum");
EnumItemInfo[]itemInfos=enumInfo.getgetEnumItems();
for(inti=0,n=itemInfos.length;i{ EnumItemInfoitemInfo=itemInfos[i]; System.out.println("项名称:"+itemInfo.getName() +";显示名称:"+getDisplayName());}这样我们不用再去调用特定平台的API实现了,元数据信息提供了比平台API更多的功能,并且写出的代码不会受平台API的限制。 若某天客户提出要增加一种“不详”的性别类别,如果开发人员直接修改生成的SexEnum类,在其中加入“不详”的性别类别的枚举定义的话,系统就会工作不正常,因为没有修改SexEnum元数据。这样就限制了开发人员直接修改SexEnum类,这样开发人员只能去修改SexEnum元数据,然后用代码生成器来重新生成SexEnum类代码。这规范了开发人员的行为,保证了设计模型与实现代码的一致性。 若某一天,公司决定将开发平台从Java迁移到C#,那么对于枚举这部分需要做的改造工作就是用C#重写元数据模型描述类和元数据读取API,并开发一个针对C#枚举的元数据转换器,系统所有的枚举就都可以自动转换成C#下的了。这保证了前期对于枚举元数据模型的设计开发成本利用的最大化。由于枚举在各个平台之间差异并不算大,而且一个平台整体从Java迁移到C#的可能性也非常小,所以元数据在这里起到的作用并不大。但是在Java平台上ORM工具的迁移倒是很有可能的,要想体会元数据的更重要的作用就要看案例系统的实体元数据了。7.2.2 元元数据元数据是对数据共性的抽象,而不同的元数据本身也是具有共性的,以上一节的两个枚举元数据来说,客户类型枚举元数据与性别枚举元数据为共同的模式: 枚举名称 … 可以定义一种模型来描述所有枚举元数据的共性特征,也就是枚举元数据的元数据(Metadataofmetadata)。这种对元数据进行抽象描述的形式被称为元元数据(MetaMetaData)。7.2.3 设计时与运行时元数据的直接表示形式被称为设计时元数据,而在运行的时候能被系统读取的形式(比如上边的EnumInfo)被称为运行时元数据。通常,运行时元数据描述的特性是设计时元数据的特性的子集。系统承担着设计模型与运行时模型的多重责任,而且元数据还作为代码生成器的“源”,承载着描述目标代码的作用。这些责任之间有相交的部分,也有自己独特的部分。举例来说,一个描述实体对象的元数据,它描述这个实体对象有哪些字段、字段的类型是什么、和其他实体对象之间有什么关系等信息,而作为代码生成器的“源”,它还要描述一些目标平台特有的东西,比如当目标平台为Hibernate的时候,就需要指定主键字段的生成策略、关联字段的LazyLoad策略、Casade策略等。从严格意义上来讲,为了维持元数据的平台无关性,这些平台相关的特性是不能放在元数据中的,而应该放在一个描述平台相关属性的地方,不过这样就使得元数据模型过于复杂。一个较好的策略是在元数据中增加一个专门存放这些平台相关属性的区域。 运行时的元数据是要被平台相关代码访问的,如果运行时元数据中包含平台相关特性的话,就会导致以后平台移植难度加大,而且也混淆了设计时语义与运行时语义之间的界限。所以运行时的元数据中一定不能包含平台相关特性。7.2.4 元数据设计的基本原则除了上边提到的运行时的元数据中一定不能包含平台相关特性之外,在元数据的设计中,“适可而止”也是需要铭记在心的核心原则。对元数据描述的范围要适可而止,不要试图包罗万象。运行时元数据是能够给运行时的系统提供元数据的信息的,这在一定程度上简化了系统的开发,但是切不可把应该写在代码中或者写到配置文件中的信息写到元数据中。比如在实体对象元数据中,给字段增加了“allowNull”特性来表示此字段是否允许为空。系统保存实体对象的时候,可以读取此实体对象对应的元数据,进而取得所有字段的是否为空的特性,从而对数据进行校验。这是对运行时元数据非常合理的运用。但是如果试图把字段为空时提示什么样的信息、字段最大长度是多少、字段是否进行加密操作等特性加入元数据的话就会使得元数据模型过于庞大,这也违反了“适可而止”这一基本原则。如果元数据直接驱动系统的运行过程,并且有取代程序代码的趋势的话,就说明设计人员对元数据概念理解错误了,用元数据驱动系统运行虽然减少了代码的编写,但是这些本不应该放在元数据中的特性是不完备的,一旦需要扩展就会遇到难以逾越的鸿沟。由于客户需求的复杂性,模型结构不能表达出所有业务的处理过程,仍然存在需要利用编程语言才能完成的业务功能。元数据模型解决大多数通用的问题,而对于具有差异性的问题还是要通过编码来完成的,不应该让运行时元数据承担过多的运行时语义。7.2.5 此“元数据”非彼“元数据”元数据这个词汇并不是MDA发明的,在其他领域“元数据”早已经被使用了,在软件开发领域,“元数据”也不是MDA中才有的。JDK5.0的annotation机制也被称为元数据,它为属性的物理驻留位置提供了新的选择。annotation使得代码具有自解释的能力,代码变成能同时提供行为及自我描述能力的实体,也就是说代码从一维变成二维的了。使用JDK提供的API就可以从代码中读取到这些描述信息。/***@filedName="FName"type="Varchar(44)"*/publicStringgetName(){…}这段代码中类似JavaDoc的东西就是annotation,它描述了name这个属性对应着数据库中的类型为“Varchar(44)”名称为“FName”的字段。Hibernate3、EJB3都推荐并且支持这种方式。在Hibernate本身也有元数据机制,在Hibernate的包org.hibernate.metadata中的类就是提供元数据支持的API,通过它们能读取到一个实体对象有哪些字段、字段的类型是什么、是否允许为空、是否关联其他的实体对象等。JDK的annotation、Hibernate中的元数据都符合元数据的定义,它们也是真正的元数据。它们与MDA中的元数据的最主要区别就是是否具有平台无关性。很显然JDK的annotation、Hibernate中的元数据都不能脱离它们所依赖的平台,元数据中有很多描述平台专有属性的东西,无法作为一个跨平台的元数据引擎使用。不过这些元数据能反应平台的更多的细节,如果合理利用将极大地提高开发效率。在后边关于HibernateDTOGenerator的分析中读者将会看到我们是如何使用Hibernate的元数据来实现DTO产生器的。图7.4 元数据编辑器(5)单击add按钮增加id字段,在如图7.5所示的属性视图中编辑字段属性。图7.5 属性视图(6)按照同样方式增加number、name字段。在PrimaryKey下拉列表框中选择id作为主键。然后单击Eclipse的保存图标完成商品元数据的建模。按照同样的步骤建立SaleBill实体元数据,增加id、number、saleDate属性。在增加saler属性的时候,此属性关联着系统中已经建立的Person元数据,因此设置isLinkProperty为true,设置完毕后属性视图中的属性比普通属性多了一些内容,主要是linkEntity、linkType、casadeType等。单击linkEntity属性右边的浏览按钮,如图7.6所示,选择系统中已经定义好的“Person元数据”。选择linkType属性为MANYTOONE。SaleBillDetail元数据没有建立,所以暂时不增加details属性。(7)按照同样步骤增加“SaleBillDetail”实体元数据。回到SaleBill实体元数据编辑界面,增加details属性,设置linkedEntity指向SaleBillDetail实体,设定linkType属性为OneToMany,从keycolumn属性的下拉列表框中选择FHeadId属性,表示SaleBillDetail实体通过FHeadId字段指向SaleBill实体。 图7.6 选择关联元数据(8)建模完毕,下面开始生成代码和配置文件。同时选中Goods.emf、SaleBill.emf、SaleBillDetail.emf三个文件,右击,在弹出的快捷菜单中选择CownewStudio∣GenerateCodefromModelFile命令,弹出如图7.7所示的界面。图7.7 代码生成选项(9)TargetORM为生成的文件对应的ORM类型,目前支持Hibernate2和Hibernate3。按照图7.7进行设置,单击【完成】按钮,然后在Eclipse中就可以看到生成的文件了,如图7.8所示。hbm配置文件生成在bizLayer包下,JavaBean生成在common包下。前边介绍了元数据在系统设计和平台相关代码生成中的应用,本节介绍元数据的运行时模型,这部分也是整个元数据中最复杂的部分。7.4.1 实体元数据运行时模型元数据中定义了很多丰富的属性,但并不是元数据所有的属性都对运行时系统有用,而且有的属性也不应该放到运行时模型中,图7.9是运行时的元数据模型。图7.9 运行时的元数据模型DataTypeEnum是数据类型枚举,LinkTypeEnum是关联类型枚举,EntityFieldModelInfo为实体字段模型,EntityModelInfo为整体的实体元数据模型。可以看到实体字段模型中去掉了实体元数据中的“CascadeType”、“Constrained”、“Inverse”等属性,这些字段是Hibernate平台特有的,所以是不能放到运行时模型中的。为了解析元数据文件以生成运行时元数据模型,我们开发了元数据解析器类EntityMetaDataParser(在包com.cownew.PISmon.metaDataMgr下),此类对外提供了一个静态方法:publicstaticEntityModelInfoxmlToBean(Documentdoc)。将元数据XML文件的Dom4j对象Document作为参数调用,返回值就是这个实体元数据的模型EntityModelInfo。因为XML文件中包含运行时元数据模型不需要的东西,所以此处不能使用XStream等OXMapping工具,而是使用Dom4j完成XML的解析。7.4.2 分包及命名规范在多层架构中,各个层之间有自己独立的不应被其他层访问的类,也有在层之间共享的类。对这些不同共享层次的类进行分包可以保证清晰的系统分层,也可以简化各层的配置安装。在案例系统中分为4种包:应用服务器包、SwingClient端包,WebClient端包,公共包。比如,有一个提供天气预报服务的RemotingService,定义了服务接口IWeatherForecast,此服务接口是应用服务器、SwingClient、WebClient都要访问的,所以要将它放到公共包中;此接口的实现类WeatherForecastImpl只有应用服务器需要,因此定义在应用服务器包中;如果编写了一个Swing客户端WeatherForecastUI访问此服务,WeatherForecastUI就要定义在SwingClient端包中;如果编写一个Web页面,此页面有WeatherForecastForm和WeatherForecastAction两个类,这两个类就要放到WebClient端包中。在案例系统中,应用服务器包命名为bizLayer、为了简化系统的开发及规范文件管理,案例系统对文件目录结构做了如下约定,如果实体元数据DemoEntity的名称(name)为DemoEntity,包名(packageName)为com.cownew.Demo,那么:l 生成的hbm配置文件DemoEntity.hbm.xml保存在com.cownew.Demo.bizLayer目录下。l 生成的JavaBean文件必须以实体名加“Info”命名,即DemoEntityInfo.java,它保存在com.cownew.Dmon目录下。l 此实体对应的DAO实现接口IDemoEntityDAO保存在com.cownew.Demo.bizLayer目录下。l 此实体对应的DAO实现类DemoEntityDAOImpl保存在com.cownew.Demo.bizLayer目录下。l 访问此接口的Swing客户端文件保存在com.cownew.Demo.client目录下。l 访问此接口的Web端源码保存在com.cownew.Demo.web目录下。前三条是必须遵守的规范,其他是建议遵守的规范。系统运行时可能需要根据实体的JavaBean得到其对应的接口或者根据元数据的位置得知接口的位置,只要根据上述规则就可以在JavaBean、接口、元数据之间互相转换。为了简化此操作,系统提供了一个工具类NameUtils,位于包com.cownew.PIS.mon.metaDataMgr下,
EnumItemInfoitemInfo=itemInfos[i];
System.out.println("项名称:
"+itemInfo.getName()
+";显示名称:
"+getDisplayName());
这样我们不用再去调用特定平台的API实现了,元数据信息提供了比平台API更多的功能,并且写出的代码不会受平台API的限制。
若某天客户提出要增加一种“不详”的性别类别,如果开发人员直接修改生成的SexEnum类,在其中加入“不详”的性别类别的枚举定义的话,系统就会工作不正常,因为没有修改SexEnum元数据。
这样就限制了开发人员直接修改SexEnum类,这样开发人员只能去修改SexEnum元数据,然后用代码生成器来重新生成SexEnum类代码。
这规范了开发人员的行为,保证了设计模型与实现代码的一致性。
若某一天,公司决定将开发平台从Java迁移到C#,那么对于枚举这部分需要做的改造工作就是用C#重写元数据模型描述类和元数据读取API,并开发一个针对C#枚举的元数据转换器,系统所有的枚举就都可以自动转换成C#下的了。
这保证了前期对于枚举元数据模型的设计开发成本利用的最大化。
由于枚举在各个平台之间差异并不算大,而且一个平台整体从Java迁移到C#的可能性也非常小,所以元数据在这里起到的作用并不大。
但是在Java平台上ORM工具的迁移倒是很有可能的,要想体会元数据的更重要的作用就要看案例系统的实体元数据了。
7.2.2 元元数据
元数据是对数据共性的抽象,而不同的元数据本身也是具有共性的,以上一节的两个枚举元数据来说,客户类型枚举元数据与性别枚举元数据为共同的模式:
枚举名称
可以定义一种模型来描述所有枚举元数据的共性特征,也就是枚举元数据的元数据(Metadataofmetadata)。
这种对元数据进行抽象描述的形式被称为元元数据(MetaMetaData)。
7.2.3 设计时与运行时
元数据的直接表示形式被称为设计时元数据,而在运行的时候能被系统读取的形式(比如上边的EnumInfo)被称为运行时元数据。
通常,运行时元数据描述的特性是设计时元数据的特性的子集。
系统承担着设计模型与运行时模型的多重责任,而且元数据还作为代码生成器的“源”,承载着描述目标代码的作用。
这些责任之间有相交的部分,也有自己独特的部分。
举例来说,一个描述实体对象的元数据,它描述这个实体对象有哪些字段、字段的类型是什么、和其他实体对象之间有什么关系等信息,而作为代码生成器的“源”,它还要描述一些目标平台特有的东西,比如当目标平台为Hibernate的时候,就需要指定主键字段的生成策略、关联字段的LazyLoad策略、Casade策略等。
从严格意义上来讲,为了维持元数据的平台无关性,这些平台相关的特性是不能放在元数据中的,而应该放在一个描述平台相关属性的地方,不过这样就使得元数据模型过于复杂。
一个较好的策略是在元数据中增加一个专门存放这些平台相关属性的区域。
运行时的元数据是要被平台相关代码访问的,如果运行时元数据中包含平台相关特性的话,就会导致以后平台移植难度加大,而且也混淆了设计时语义与运行时语义之间的界限。
所以运行时的元数据中一定不能包含平台相关特性。
7.2.4 元数据设计的基本原则
除了上边提到的运行时的元数据中一定不能包含平台相关特性之外,在元数据的设计中,“适可而止”也是需要铭记在心的核心原则。
对元数据描述的范围要适可而止,不要试图包罗万象。
运行时元数据是能够给运行时的系统提供元数据的信息的,这在一定程度上简化了系统的开发,但是切不可把应该写在代码中或者写到配置文件中的信息写到元数据中。
比如在实体对象元数据中,给字段增加了“allowNull”特性来表示此字段是否允许为空。
系统保存实体对象的时候,可以读取此实体对象对应的元数据,进而取得所有字段的是否为空的特性,从而对数据进行校验。
这是对运行时元数据非常合理的运用。
但是如果试图把字段为空时提示什么样的信息、字段最大长度是多少、字段是否进行加密操作等特性加入元数据的话就会使得元数据模型过于庞大,这也违反了“适可而止”这一基本原则。
如果元数据直接驱动系统的运行过程,并且有取代程序代码的趋势的话,就说明设计人员对元数据概念理解错误了,用元数据驱动系统运行虽然减少了代码的编写,但是这些本不应该放在元数据中的特性是不完备的,一旦需要扩展就会遇到难以逾越的鸿沟。
由于客户需求的复杂性,模型结构不能表达出所有业务的处理过程,仍然存在需要利用编程语言才能完成的业务功能。
元数据模型解决大多数通用的问题,而对于具有差异性的问题还是要通过编码来完成的,不应该让运行时元数据承担过多的运行时语义。
7.2.5 此“元数据”非彼“元数据”
元数据这个词汇并不是MDA发明的,在其他领域“元数据”早已经被使用了,在软件开发领域,“元数据”也不是MDA中才有的。
JDK5.0的annotation机制也被称为元数据,它为属性的物理驻留位置提供了新的选择。
annotation使得代码具有自解释的能力,代码变成能同时提供行为及自我描述能力的实体,也就是说代码从一维变成二维的了。
使用JDK提供的API就可以从代码中读取到这些描述信息。
/**
*@filedName="FName"type="Varchar(44)"
*/
publicStringgetName(){…}
这段代码中类似JavaDoc的东西就是annotation,它描述了name这个属性对应着数据库中的类型为“Varchar(44)”名称为“FName”的字段。
Hibernate3、EJB3都推荐并且支持这种方式。
在Hibernate本身也有元数据机制,在Hibernate的包org.hibernate.metadata中的类就是提供元数据支持的API,通过它们能读取到一个实体对象有哪些字段、字段的类型是什么、是否允许为空、是否关联其他的实体对象等。
JDK的annotation、Hibernate中的元数据都符合元数据的定义,它们也是真正的元数据。
它们与MDA中的元数据的最主要区别就是是否具有平台无关性。
很显然JDK的annotation、Hibernate中的元数据都不能脱离它们所依赖的平台,元数据中有很多描述平台专有属性的东西,无法作为一个跨平台的元数据引擎使用。
不过这些元数据能反应平台的更多的细节,如果合理利用将极大地提高开发效率。
在后边关于HibernateDTOGenerator的分析中读者将会看到我们是如何使用Hibernate的元数据来实现DTO产生器的。
图7.4 元数据编辑器
(5)单击add按钮增加id字段,在如图7.5所示的属性视图中编辑字段属性。
图7.5 属性视图
(6)按照同样方式增加number、name字段。
在PrimaryKey下拉列表框中选择id作为主键。
然后单击Eclipse的保存图标完成商品元数据的建模。
按照同样的步骤建立SaleBill实体元数据,增加id、number、saleDate属性。
在增加saler属性的时候,此属性关联着系统中已经建立的Person元数据,因此设置isLinkProperty为true,设置完毕后属性视图中的属性比普通属性多了一些内容,主要是linkEntity、linkType、casadeType等。
单击linkEntity属性右边的浏览
按钮,如图7.6所示,选择系统中已经定义好的“Person元数据”。
选择linkType属性为MANYTOONE。
SaleBillDetail元数据没有建立,所以暂时不增加details属性。
(7)按照同样步骤增加“SaleBillDetail”实体元数据。
回到SaleBill实体元数据编辑界面,增加details属性,设置linkedEntity指向SaleBillDetail实体,设定linkType属性为OneToMany,从keycolumn属性的下拉列表框中选择FHeadId属性,表示SaleBillDetail实体通过FHeadId字段指向SaleBill实体。
图7.6 选择关联元数据
(8)建模完毕,下面开始生成代码和配置文件。
同时选中Goods.emf、SaleBill.emf、SaleBillDetail.emf三个文件,右击,在弹出的快捷菜单中选择CownewStudio∣GenerateCodefromModelFile命令,弹出如图7.7所示的界面。
图7.7 代码生成选项
(9)TargetORM为生成的文件对应的ORM类型,目前支持Hibernate2和Hibernate3。
按照图7.7进行设置,单击【完成】按钮,然后在Eclipse中就可以看到生成的文件了,如图7.8所示。
hbm配置文件生成在bizLayer包下,JavaBean生成在common包下。
前边介绍了元数据在系统设计和平台相关代码生成中的应用,本节介绍元数据的运行时模型,这部分也是整个元数据中最复杂的部分。
7.4.1 实体元数据运行时模型
元数据中定义了很多丰富的属性,但并不是元数据所有的属性都对运行时系统有用,而且有的属性也不应该放到运行时模型中,图7.9是运行时的元数据模型。
图7.9 运行时的元数据模型
DataTypeEnum是数据类型枚举,LinkTypeEnum是关联类型枚举,EntityFieldModelInfo为实体字段模型,EntityModelInfo为整体的实体元数据模型。
可以看到实体字段模型中去掉了实体元数据中的“CascadeType”、“Constrained”、“Inverse”等属性,这些字段是Hibernate平台特有的,所以是不能放到运行时模型中的。
为了解析元数据文件以生成运行时元数据模型,我们开发了元数据解析器类EntityMetaDataParser(在包com.cownew.PISmon.metaDataMgr下),此类对外提供了一个静态方法:
publicstaticEntityModelInfoxmlToBean(Documentdoc)。
将元数据XML文件的Dom4j对象Document作为参数调用,返回值就是这个实体元数据的模型EntityModelInfo。
因为XML文件中包含运行时元数据模型不需要的东西,所以此处不能使用XStream等OXMapping工具,而是使用Dom4j完成XML的解析。
7.4.2 分包及命名规范
在多层架构中,各个层之间有自己独立的不应被其他层访问的类,也有在层之间共享的类。
对这些不同共享层次的类进行分包可以保证清晰的系统分层,也可以简化各层的配置安装。
在案例系统中分为4种包:
应用服务器包、SwingClient端包,WebClient端包,公共包。
比如,有一个提供天气预报服务的RemotingService,定义了服务接口IWeatherForecast,此服务接口是应用服务器、SwingClient、WebClient都要访问的,所以要将它放到公共包中;此接口的实现类WeatherForecastImpl只有应用服务器需要,因此定义在应用服务器包中;如果编写了一个Swing客户端WeatherForecastUI访问此服务,WeatherForecastUI就要定义在SwingClient端包中;如果编写一个Web页面,此页面有WeatherForecastForm和WeatherForecastAction两个类,这两个类就要放到WebClient端包中。
在案例系统中,应用服务器包命名为bizLayer、
为了简化系统的开发及规范文件管理,案例系统对文件目录结构做了如下约定,如果实体元数据DemoEntity的名称(name)为DemoEntity,包名(packageName)为com.cownew.Demo,那么:
l 生成的hbm配置文件DemoEntity.hbm.xml保存在com.cownew.Demo.bizLayer目录下。
l 生成的JavaBean文件必须以实体名加“Info”命名,即DemoEntityInfo.java,它保存在com.cownew.Dmon目录下。
l 此实体对应的DAO实现接口IDemoEntityDAO保存在com.cownew.Demo.bizLayer目录下。
l 此实体对应的DAO实现类DemoEntityDAOImpl保存在com.cownew.Demo.bizLayer目录下。
l 访问此接口的Swing客户端文件保存在com.cownew.Demo.client目录下。
l 访问此接口的Web端源码保存在com.cownew.Demo.web目录下。
前三条是必须遵守的规范,其他是建议遵守的规范。
系统运行时可能需要根据实体的JavaBean得到其对应的接口或者根据元数据的位置得知接口的位置,只要根据上述规则就可以在JavaBean、接口、元数据之间互相转换。
为了简化此操作,系统提供了一个工具类NameUtils,位于包com.cownew.PIS.mon.metaDataMgr下,
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1