假设要架构一个联系人管理项目,其核心管理对象是“人(Person)”,但是人的属性繁多,例如应用在娱乐行业,有星座、爱好等属性,用在健康项目中可能有是否喝酒和吸烟、胖瘦等字段。完全不可能固定。如:
Person Table:
Record 1.
-----------
_id:主键,
nickname:昵称,
realname:真实姓名,
birthday:生日,
height:身高,
hairColor:发色,
drinker:是否喝酒,
sexualOrientation:性取向,
sports:喜欢的运动,
books:喜欢的书,
emails:电子邮件,
ims:即时通信工具,
created:创建日期,
...
Record 2.
-----------
_id:主键,
nickname:昵称,
realname:真实姓名,
birthday:生日,
weight:体重,
position:职务,
languagesSpoken:语言,
smoker:是否吸烟,
religion:宗教信仰,
interests:兴趣,
phoneNumbers:电话号码,
addresses:地址,
created:创建日期,
...
记录1和记录2有相同的字段,但也有很多不同的字段,要求在同一表中存储,显然表结构存在很大的差异。而传统关系数据库非常严格的数据元定义,大大消弱了数据管理的自由度。Person表要想兼容上述两条记录,要么统一表的结构,要么使用扩展属性表进行管理。大幅增加了业务层程序复杂度,查询也受到限制;而且,在后续阶段不排除表结构变更。虽然以Hibernate为代表的ORM框架将关系转化为对象进行处理,能降低上层程序的影响程度,但仍然避免不了上层程序的调整。
NoSQL之文档数据库弥补了它的不足,除了超凡出俗的性能之外,无模式特性在很多领域具有极高的应用价值。以MongoDB为例,其Bson文档的key可以随意增加与移除,每条记录间并不需要完全相等的key存在。因此,这种场景特别适用于NoSQL数据库。
但是,当映射到某语言,尤其某种面向对象的语言中,又面临新的问题,以Java语言为例,上述记录要映射到一个Entity类中,这个Entity类该如何定义:
public class Person implements Serializable{
private Long id;
private String nickname;
private String realname,
//......
}
似乎又回到了RDMBS表结构问题,Person类的属性到底怎么定义?
没错,将Person变成Map类型。
@Document
public class Person extends LinkedHaskMap<String, Object> implements Map<String, Object>{
}
不过,又面临一个问题。玩过Hibernate、JPA、以及其它JDBC封装的ORM框架都知道,集合类型是不允许作为Java Entity。谁见过实现List、Set、Map接口或者继承其子类的实体类?虽然MongoDB的集合本质上是Map类型,但一些MongoDB操作中间件,如Morphia、Spring-data-mongodb,是禁止@Document类型的实体类是集合类型。也就是说上述Person类不能扫描成实体类。
Spring-data-mongodb是一款非常灵活,接口优雅的MongoDB ORM工具。通过扩展此框架可以有效解决问题。解决步骤如下:
<