我將我的節點表示爲域實體。我想通過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'從它們的底層實現中分離出來,但是你這樣做並不需要增加另一個層來維護是非常棘手的。 –