抱歉,这是一篇仅限中文的帖子 :D
现在,NO-SQL 的概念在互联网界非常火爆,好像如果一个网站不用这个,不推出自己的 K-V 存储,都不好意思混迹其中。网上有很多介绍各种各样 NO-SQL 的内容,所以本文不在此赘述,但是,作者想要强调的是,做技术有一个非常重要的原则是不跟风
,选择适合自己的技术最重要。《i class="wikiEmphasis">NO-SQL 并非万能,它并不能(至少目前)完全取代关系型数据库,可参考这篇文章。
作为Java程序员,我们已经(应该)非常熟悉RMDBS了,并且,有一套很完善(有人称之为笨重,但这取决于你的项目类型,如果是一个初创公司,那么自然笨重,但如果是为铁道部开发系统,那么再怎么谨慎也不为过)的开发方法论。
我们所熟悉的 Hibernate 现在已经更名为 Hibernate ORM 了,因为现在Hibernate team出品的项目不仅仅是一个O/R Mapping的框架了,我们还有提供完善的搜索支持的Hibernate Search和用于校验的Hibernate Validator,以及本文介绍的Hibernate OGM,所以需要一个更准确的命名。
对于一个Java项目,一般先创建好领域模型,然后定义实体对象以及他们之间的关系,剩下的事情Hibernate ORM会帮你全都打理好,很简单也很熟悉。
那么,如果想要用NO-SQL呢?
什么是Hibernate OGM?
OGM == Object/(Data) Grid Mapping
ORM的概念大家都很熟悉了,那么OGM的意思是对象--数据网格 映射
,Hibernate OGM同样是一个JPA的实现,所以你可以用熟悉的JPA API(当然,或者Hibernate API)来把实体模型存储进NO-SQL中,并执行查询等操作。
通过使用Hibernate OGM,我们可以把现有的、基于JPA/Hibernate ORM的项目不加改动的从RMDBS切换到NO-SQL之上。
Hibernate OGM并非从头搭建的全新项目,事实上,它跟牛顿一样,也站在了巨人的肩膀上。

通过使用Hibernate ORM的核心,提供了完整的JPA支持,以及实体对象生命周期管理等功能,确保了稳定性。
通过Hibernate Search提供了完善的全文检索功能,这正是NO-SQL相比RMDBS所缺少的部分。
而Infinispan,一个高性能、分布式的网格存储引擎,是Hibernate OGM所支持的标准NO-SQL实现(当然不仅限于Infinispan),同时,infinispan还可以作为Hibernate Search所需lucene index的存储。
当程序和存储(无论RMDBS 还是 NO-SQL)打交道的时候,核心问题实际上就是两个:存进去,取出来。
领域模型如何被持久化进NO-SQL store?
抛开各种NO-SQL的实现方式,例如K-V,Documents,Graph等,我们可以把NO-SQL简化成我们容易理解的 HashMap .
那么,现在问题是,如果你有一个简单的实体类,你该如何把它存储进一个HashMap呢?
对于RMDBS来说,这不是个问题,每个属性对应表中的一个column就好了。
可是如果是NO-SQL的话,情况就复杂一些了,最简单的方式是用这个实体类的类名和其id作为key(注意,这里的HashMap是个概念,指代NO-SQL store,所以它是全局的),把这个实体对象(经过序列化)作为value来存储。
可是,这个方案会有些问题
1. NO-SQL中存储的内容和你的领域模型紧密耦合,如果后面领域模型有改动,那么在反序列化之前存储的内容的时候会出问题。2. 别的系统可能会无法使用该NO-SQL中的数据,例如,你还有一个ruby的系统,那么两个系统之间可能会无法共享一个NO-SQL,因为ruby没办法反序列化Java Object。3. 序列化的时候,可能会把整个对象图(因为对象之间相互关联)都保存起来
RMDBS在此则有一些比较好的原则
实体对象和存储内容解耦(所以才有O/R Mapping) 使用基本类型,例如varchar,integer等 主键确保唯一性 外键确保一致性
Hibernate OGM在把实体对象持久化的时候,key是这样构造的 – 表名,主键列名,主键值,而value呢,是存储成一个基本数据类型组成的元组的,可以想象成Map<String,Object>,这个map的key是属性名,而值则是属性值的基本类型表示,例如 URL 会表示成一个 String。我们相信使用基本类型更有助于数据的可移植性。

实体之间的关联关系,则是另一个让人头痛的地方 – 如果自己处理实体的存储的话。
但是,得益于JPA/Hibernate早已定义的非常完善的关联关系处理方案,这个在Hibernate OGM中也很简单,也是存放在元组中的,其中key是由以下组成:-- 关联表名,外键列名,外键值。这样的结构保证了,关联关系可以由实体中保存的信息获取到。
如下图所示

在上图中,如果想找到user id为1的人的名字和地址的话,那么就可以
1. 通过key tbluser,userIdpk,1 得到 {userId_pk=1, name=Emmanuel
} 2. 通过key tbluseraddress,userIdfk,1 得到 { { userId_fk=1, addressId_fk=3}, { userId_fk=1, addressId_fk=5} } 得到与此user所关联的两个address的id分别为3和5。3. 再分别构造两个key tbladdress,addressIdpk,3 和 tbladdress, addressIdpk,5得到此人的地址。
所有上面介绍的都是发生在Hibernate OGM内部的,用户所需要的仅仅是
entitymanager.persist(user); entitymanager.persist(address); entitymanager.find(User.class, 1);
查询
目前,绝大多数应用对于存储的需求都是读大于写的,而传统的关系型数据库,由于其有完善的关系数学模型保证,所以能够提供完善的查询,甚至多表级联查询(join),而另一方面,NO-SQL因为起schema free / K-V的本质,基本上只能通过key来查找。
如上面例子所示,对于id已知的情况,那么NO-SQL表现得很出色,可是,如果想根据地址来搜索的话,那就比较麻烦了。
而Hibernate Search,正是在此起的作用,通过在实体对象添加一些annotation,定义要索引的字段,当你通过Hibernate ORM/OGM (Hibernate Search和这两个都集成了很好) 保存或更新实体对象的时候,Hibernate Search会自动的对此对象创建lucene索引,之后,就可以通过Hibernate Search Query API或者Lucene Query API对保存的实体对象进行全文索引了。
继续阅读Hibernate OGM实战