Stefan Armbruster在自己的Blog上公布了Grails Neo4j插件0.2版发布的消息。
前些时候InfoQ中文站已经报导了Neo4j 1.0的发布。Neo4j是一款基于Java的NoSQL图形数据库:
相对于关系数据库来说,图形数据库善于处理大量复杂、互连接、低结构化的数据,这些数据变化迅速,需要频繁的查询——在关系数据库中,这些查询会导致大量的表连接,因此会产生性能上的问题。
Neo4j有3个基本构建块:
- Node(又叫做vertex)——从概念上来说,这类似于对象实例,拥有唯一的ID。
- Relationship(又叫做edge)——它连接了两个Node,此外还有方向和RelationshipType。
- Property(又叫做attribute)——他们是字符串类型的key/Object值对,Node与Relationship都有Property。
虽然Neo4j 1.0版最近发布不久,但它的Grails插件早在去年10月就已经出现了,按照Stefan Armbruster的说法:
这个插件可以在Grails应用中透明的使用。你只需要做一件事情:移除Hibernate插件,安装neo4j插件。
grails uninstall-plugin hibernate grails install-plugin neo4j
需要提醒的是,Neo API的每个调用都需要在一个事务中,对于一般使用(控制器中),该插件采用类似Hibernate的OSIV方式工作。但对于其他情形,就得自己控制事务了,Stefan给出了他的Bootstrap.groovy代码:
import org.springframework.transaction.PlatformTransactionManager import org.springframework.transaction.support.TransactionTemplate import org.springframework.transaction.support.TransactionCallback class BootStrap { PlatformTransactionManager transactionManager def init = { servletContext -> def result = new TransactionTemplate(transactionManager) .execute({ status -> // setup Roles if (!Role.list()) { log.error "added roles" new Role(authority:"ROLE_USER").save() } } as TransactionCallback) } def destroy = { } }
在0.1版,该插件可以完成:
- 领域类的基本CRUD
- 领域类的one-to-one、one-to-many、many-to-one和many-to-many关系
- 支持Scaffolding
- 结合acegi plugin,你可以保存自己的user、role和requestmap实现到neo4j中。由于缺省UserDetailsService依赖Hibernate的SessionFactory,要想让它工作,你必须自定义UserDetailsService。
如今,插件已经到了0.2版,该版本的特性:
- 由Neo4j的领域类可以和传统的领域类(即由Hibernate映射的)共存
- 升级到Neo4j 1.0
- 利用Grails依赖注入,而非嵌入/lib目录的jar。
- 增加检查Neo4j节点空间的单独控制器
- 仿照couchdb插件,利用AST Transformation大幅重构代码
- 支持Neo4j indexer
- 支持未声明属性(non-declared properties)
- 支持traversers
对于以上特性的细节,摘录如下:
- 领域类共存:每个使用@Neo4jEntity标注的领域类将由Neo4j管理,该注解利用了AST Transformation。
- 利用最新的依赖注入DSL,插件自己不再包含相应的jar。
- 可视化节点空间的控制器,显示如下图:
支持Neo4j indexer:使用@Neo4jIndex标注想在其上建立索引的属性;在GORM的‘findBy <属性> ’或‘findAllBy <属性> ’方法在索引属性上被调用时,将会使用索引。
支持未声明属性的示例:
def car = new Car(licenseTag:'ABC123', color:'red') car.save() assert 'red' == car.color // declared property // N.B: numberOfSeats is _not_ declared assert null = car.numberOfSeats car.numberOfSeats = 5 assert '5' == car.numberOfSeats
支持traversers:使用引用节点作为起点的DomainClass.traverse;使用domainClassInstance作为起点的 .traverse。代码示例如下:
def traverser = car.traverse( { true }, // StopEvaluator // ReturnableEvaluator { it.currentNode().getProperty("color",null)=="red"} )
详情请参见原文。另外提醒一下,在使用时请注意Neo4j的许可证协议,尤其是你打算用它开发商业应用的时候。
Neo4j……既有基于AGPLv3的开源版本,也有商业版本。如果在闭源软件中使用Neo4j则需要商业协议。