导言
在开发企业级业务应用(企业规模)时,客户往往要求在不修改系统源代码的情况下对应用对象模型的扩展性提供支持。利用可扩展域模型可以实现新功能的开发,而不需要额外的精力和成本
- 应用的使用周期将被延长;
- 外部因素改变时,系统工作流也可以随之被修改;
- 已经被部署的应用可以被“设定”,使其符合企业的特定情况。
完成以上功能需求最简单、最具成本效益的方法应该是在应用中实现支持自定义字段的可扩展业务实体。
原创整理不易,转载请注明出处:用Hibernate实现领域对象的自定义字段
代码下载地址:http://www.zuidaima.com/share/1724475837090816.htm
什么是“自定义字段”?
什么是自定义字段?最终用户如何从中受益呢?自定义字段是一种对象属性,它不是由系统开发人员在开发阶段创建的,而是在系统实际使用中由系统用户在不改变任何源代码的情况下添加到对象中的。
可能会需要哪些功能呢?
让我们举一个CRM(客户关系管理系统)应用的例子来领会一下。 假设我们有一个客户“Client”对象。理论上讲,这个对象可以有任意多的各种属性:几个email地址、若干电话号码和地址等。某公司的销售部门可能会使用其中一个属性,但其它公司却会完全忽略它。将最终用户可能会用到的(也可能不会用到的)所有属性都加入到对象当中,这是很浪费并很不合理的。
既然这样,允许系统用户(或者管理员)来创建他们公司的销售经理们需要的属性,也许是更好的做法。例如,如果有需要,管理员可以创建“工作电话”或者“家庭地址”等属性。 此外,这些属性还可以用到数据过滤和查询中去。
简要说明
在实施Enterra CRM项目时,客户提出了在应用中支持自定义字段的目标,“系统管理员不需要重启系统就可以创建或删除自定义字段”。
系统后端开发使用了Hibernate 3.0框架,这个因素(技术约束)是考虑实现这个需求的关键。
实现
在这一章里面我们将介绍采用Hibernate框架实现的关键环节。
环境
例子的开发环境如下所示:
- JDK 1.5;
- Hibernate 3.2.0框架;
- MySQL 4.1。
限制
简单起见,我们不使用Hibernate EntityManager(译注一)和Hibernate Annotations(译注二)。持久化对象的映射关系将基于xml映射文件。此外,值得一提的是,由于演示用例是基于xml映射文件管理映射,所以使用Hibernate Annotations的话,它将不能正常运行。
功能定义
我们必须实现一种机制——允许实时地创建/删除自定义字段而不重启应用,向其中添加值并保证值能保存到应用的数据库中。此外我们还必须保证自定义字段能用于查询。
解决方案
域模型
首先,我们需要一个进行试验的业务实体类。假设是Contact类,它有两个持久化字段:id和name。
但是,除了这些持久不变的字段外,这个类还应该有一些存储自定义字段值的数据结构。Map也许是针对于此的理想数据结构。
为所有支持自定义字段的业务实体创建一个基类——CustomizableEntity,它包含处理自定义字段的Map类型属性customProperties:
01 | package com.enterra.customfieldsdemo.domain; |
04 | import java.util.HashMap; |
06 | public abstract class CustomizableEntity { |
08 | private Map customProperties; |
10 | public Map getCustomProperties() { |
11 | if (customProperties== null ) |
12 | customProperties= new HashMap(); |
13 | return customProperties; |
15 | public void setCustomProperties(Map customProperties) { |
16 | this .customProperties=customProperties; |
19 | public Object getValueOfCustomField(String name) { |
20 | return getCustomProperties().get(name); |
23 | public void setValueOfCustomField(String name, Object value) { |
24 | getCustomProperties().put(name, value); |
清单1-基类CustomizableEntity
Contact类继承上面的基类:
01 | package com.enterra.customfieldsdemo.domain; |
03 | import com.enterra.customfieldsdemo.domain.CustomizableEntity; |
05 | public class Contact extends CustomizableEntity { |
14 | public void setId( int id) { |
18 | public String getName() { |
22 | public void setName(String name) { |
清单2-继承自CustomizableEntity的Contact类
别忘了这个类的映射文件:
01 | <?xml version= "1.0" encoding= "UTF-8" ?> |
03 | <! DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" |
06 | <hibernate-mapping auto- import = "true" default -access= "property" default -cascade= "none" default -lazy= "true" > |
08 | < class abstract = "false" name= "com.enterra.customfieldsdemo.domain.Contact" table= "tbl_contact" > |
10 | <id column= "fld_id" name= "id" > |
11 | <generator class = "native" /> |
14 | <property name= "name" column= "fld_name" type= "string" /> |
15 | <dynamic-component insert= "true" name= "customProperties" optimistic-lock= "true" unique= "false" update= "true" > |
清单3-Contact类的映射
注意id和name属性都是当作普通的属性来处理,但对于customProperties,我们使用了 (动态组件)标签。Hibernate 3.2.0GA文档里面关于dynamic-component的要点如下:
<dynamic-component>映射的语义与<component>是一样的。该映射的优点是仅仅通过编辑映射文件,就能在部署时确定bean的现行属性。使用DOM解析器,映射文件的运行时操作也是可行的。甚至,你可以通过Configuration对象,来访问(和修改)Hibernate的配置时元模型。
基于Hibernate文档中的这段规则,我们来建立前面要求的功能机制。