我将我的节点表示为域实体。我想通过REST接口公开我的节点,使用GET按id查询,POST以保存节点及其相关节点,并通过PUT更新节点及其相关节点。 GET工作得很好,但我有一个关于POST和PUT情况的实现问题。让我来说明这个问题,因为代码经常说的不仅仅是单词。用于暴露Neo4j节点的REST接口实现
在这个例子中,有两个相关节点类型表示为域实体。协作可以有多个标签,标签可以属于多个协作。所以我们有很多关系。它们都共享相同的基类NodeBacked ,它基本上充当底层节点的包装。
NodeBacked
abstract class NodeBacked {
private Node node;
public NodeBacked(final Node node) {
this.node = node;
}
public Long getId() {
return this.node.getId();
}
@Override
public int hashCode() {
return this.node.hashCode();
}
@JsonIgnore
public Node getNode() {
return this.node;
}
}
协作
public class Collaboration extends NodeBacked {
public Collaboration(final Node node) {
super(node);
}
// Leaving out some properties for clearness
@JsonProperty(NAME_JSON)
public String getName() {
return (String) getNode().getProperty(NAME);
}
@JsonProperty(TAGS_JSON)
public Iterable<Tag> getTags() {
return new IterableWrapper<Tag, Path>(Traversal.description().breadthFirst()
.relationships(Relationships.HAS, Direction.OUTGOING).uniqueness(Uniqueness.NODE_GLOBAL)
.evaluator(Evaluators.atDepth(1)).evaluator(Evaluators.excludeStartPosition()).traverse(getNode()))
{
@Override
protected Tag underlyingObjectToObject(final Path path) {
return new Tag(path.endNode());
}
};
}
public void setName(final String name) {
final Index<Node> index = getNode().getGraphDatabase().index().forNodes(Indexes.NAMES);
getNode().setProperty(NAME, name);
if (StringUtils.isNotEmpty(getName())) {
index.remove(getNode(), NAME, name);
}
index.add(getNode(), NAME, name);
}
public void addTag(final Tag tag) {
if (!Traversal.description().breadthFirst().relationships(Relationships.HAS, Direction.OUTGOING)
.uniqueness(Uniqueness.NODE_GLOBAL).evaluator(Evaluators.atDepth(1))
.evaluator(Evaluators.excludeStartPosition())
.evaluator(Evaluators.includeWhereEndNodeIs(tag.getNode())).traverse(getNode()).iterator().hasNext
()) {
getNode().createRelationshipTo(tag.getNode(), Relationships.HAS);
}
}
@Override
public boolean equals(final Object o) {
return o instanceof Collaboration && getNode().equals(((Collaboration) o).getNode());
}
}
标签
public class Tag extends NodeBacked {
public Tag(final Node node) {
super(node);
}
@JsonProperty(NAME_JSON)
public String getName() {
return (String) getNode().getProperty(NAME);
}
public void setName(final String name) {
final Index<Node> index = getNode().getGraphDatabase().index().forNodes(Indexes.NAMES);
getNode().setProperty(NAME, name);
if (StringUtils.isNotEmpty(getName())) {
index.remove(getNode(), NAME, name);
}
index.add(getNode(), NAME, name);
}
@JsonProperty(COLLABORATIONS_JSON)
@JsonSerialize(using = SimpleCollaborationSerializer.class)
private Iterable<Collaboration> getCollaborations(int depth) {
return new IterableWrapper<Collaboration, Path>(Traversal.description().breadthFirst()
.relationships(Relationships.HAS, Direction.INCOMING).uniqueness(Uniqueness.NODE_GLOBAL)
.evaluator(Evaluators.atDepth(1)).evaluator(Evaluators.excludeStartPosition()).traverse(getNode()))
{
@Override
protected Collaboration underlyingObjectToObject(final Path path) {
return new Collaboration(path.endNode());
}
};
}
@Override
public boolean equals(final Object o) {
return o instanceof Tag && getNode().equals(((Tag) o).getNode());
}
}
我露出Collaboratio n通过REST(Spring 3.2)进行如下操作。 MappingJackson2HttpMessageConverter用于将POJO转换为JSON,反之亦然。
@Controller
@RequestMapping(value = CollaborationController.CONTEXT_PATH)
public class CollaborationController {
public static final String CONTEXT_PATH = "/collaborations";
@Autowired
private GraphDatabaseService db;
@Transactional
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public @ResponseBody Collaboration getCollaboration(final @PathVariable Long id) {
// should use a service layer but doing this for clearness
return new Collaboration(db.getNodeById(id));
}
}
这个伟大的工程。 getters向节点查询它的属性,并返回一个体面的JSON字符串。一个例子JSON字符串GET /collaborations/1
:
{
"name" : "Dummy Collaboration",
"tags" : [ {
"name" : "test",
"id" : 3
}, {
"name" : "dummy",
"id" : 2
} ],
"id" : 1
}
那么,有什么问题呢?想象一下,一个JSON的身体,看起来POST请求如下:
{
"name" : "Second collaboration",
"tags" : [ {
"name" : "tagged"
} ]
}
的CollaborationController有如下方法来处理POST请求:
@Transactional
@RequestMapping(method = RequestMethod.POST, headers = JSON_CONTENT_TYPE)
public @ResponseBody ResponseEntity<Collaboration> persist(final @RequestBody Collaboration collaboration,
final UriComponentsBuilder builder) {
final Collaboration collab = new Collaboration(db.createNode(Labels.COLLAB));
// Problem!!
collab.setName(collaboration.getName());
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path(CONTEXT_PATH + "/{id}").buildAndExpand(collab.getId()).toUri());
return new ResponseEntity<Collaboration>(collab, headers, HttpStatus.CREATED);
}
线collab.setName(collaboration.getName());
不会起作用,因为协作类做不包含其自己的属性,并使用直接查询底层节点的getter。在这种情况下,没有任何节点可用,因为Collaboration应该由Jackson2通过Spring的MappingJackson2HttpMessageConverter从JSON转换为POJO。有没有任何属性,所以没有什么要设置的...
我寻找到这个问题的干净的解决方案,但还没有找到一个还没有。我可以使用POJO(或VO或......)作为persist方法传入的参数,但是这并不是真正的维护。更改属性需要更新CollaborationVO(POJO)类的Collaboration类。
建议不止欢迎! Spring Data Neo4j主要解决所有这些问题,但我对它的性能不满意。这就是我尝试采用另一种方法的确切原因。
我希望这个解释不够清楚。感谢您的支持!使用
框架:
- 春天3.2。3.RELEASE
- 的Neo4j 2.0.0-M3(嵌入式)
- 杰克逊2.2.2
+1有趣而且写得很好的问题。看起来好像你需要将'Collaboration'和'Tag'从它们的底层实现中分离出来,但是你这样做并不需要增加另一个层来维护是非常棘手的。 –