Spring mvc 弹簧数据Neo4j在保存期间插入意外的关系(边)
我使用Spring Boot 1.5.3和OGM 2.1.2。SDN版本是4.2.3,我使用neo4j 3.2.1数据库。我修改了配置文件来修复chyper版本的小问题,所以我使用chyper 3.1作为默认语言 在这个用例中,我只有一个名为category的简单域类。每个类别都可以有一个父级和多个子级,就像经典的树结构一样 由于类别较多,与数据库所需的磁盘空间相比,我更喜欢性能更好的数据库。这是我的域类:Spring mvc 弹簧数据Neo4j在保存期间插入意外的关系(边),spring-mvc,spring-data-neo4j-4,Spring Mvc,Spring Data Neo4j 4,我使用Spring Boot 1.5.3和OGM 2.1.2。SDN版本是4.2.3,我使用neo4j 3.2.1数据库。我修改了配置文件来修复chyper版本的小问题,所以我使用chyper 3.1作为默认语言 在这个用例中,我只有一个名为category的简单域类。每个类别都可以有一个父级和多个子级,就像经典的树结构一样 由于类别较多,与数据库所需的磁盘空间相比,我更喜欢性能更好的数据库。这是我的域类: @NodeEntity public class Category { @GraphI
@NodeEntity
public class Category {
@GraphId
Long id;
@Convert(UuidStringConverter.class)
@Index(unique = true, primary = true)
UUID uuid;
@DateString("yy-MM-dd")
private Date dateAdded;
@Index(unique = true, primary = false)
private String name;
@Relationship(type = "parent", direction = Relationship.OUTGOING)
private Category parent;
@Relationship(type = "children", direction = Relationship.OUTGOING)
private Set<Category> children;
public Category() {
dateAdded = new Date();
uuid = UUID.randomUUID();
}
public Category(String name) {
this();
this.name = name;
}
public Category(String name, Category parent) {
this(name);
this.parent = parent;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
/**
* @return the dateAdded
*/
public Date getDateAdded() {
return dateAdded;
}
/**
* @param dateAdded
* the dateAdded to set
*/
public void setDateAdded(Date dateAdded) {
this.dateAdded = dateAdded;
}
/**
* @return the children
*/
public Set<Category> getChildren() {
return children;
}
/**
* @param children
* the children to set
*/
public void setChildren(Set<Category> children) {
this.children = children;
}
public void addChildren(Category c) {
if (children == null) {
children = new HashSet<>();
}
children.add(c);
}
public void removeChildren(Category c) {
if (children != null) {
children.remove(c);
if (children.size() == 0) {
children = null;
}
};
}
public String toString(){
return name;
}
}
创建新子类别的代码如下所示:
Create Non-fiction main category
2017-06-14 09:47:06.312 DEBUG 6788 --- [nio-8083-exec-4] o.s.b.w.f.OrderedRequestContextFilter : Bound request context to thread: org.apache.catalina.connector.RequestFacade@241797fe
2017-06-14 09:47:06.331 INFO 6788 --- [nio-8083-exec-4] o.n.o.drivers.http.request.HttpRequest : Thread: 21, url: http://neo4j:password@localhost:7474/db/data/transaction/commit, request: {"statements":[{"statement":"MATCH (n:`Category`) WHERE n.`name` = { `name_0` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)","parameters":{"name_0":"Non-fiction"},"resultDataContents":["graph","row"],"includeStats":false}]}
2017-06-14 09:47:06.390 INFO 6788 --- [nio-8083-exec-4] o.n.o.drivers.http.request.HttpRequest : Thread: 21, url: http://localhost:7474/db/data/transaction/23, request: {"statements":[{"statement":"MATCH (n) WHERE n.uuid = { id } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p","parameters":{"id":"3a497cec-d67c-4c44-ab86-e6639dc60d13"},"resultDataContents":["graph"],"includeStats":false}]}
2017-06-14 09:47:06.465 INFO 6788 --- [nio-8083-exec-4] o.n.o.drivers.http.request.HttpRequest : Thread: 21, url: http://localhost:7474/db/data/transaction/24, request: {"statements":[{"statement":"UNWIND {rows} as row MERGE (n:`Category`{uuid: row.props.uuid}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type","parameters":{"rows":[{"nodeRef":-684249223,"type":"node","props":{"name":"Non-fiction","uuid":"3a497cec-d67c-4c44-ab86-e6639dc60d13","dateAdded":"17-06-14"}}]},"resultDataContents":["row"],"includeStats":false}]}
2017-06-14 09:47:06.545 INFO 6788 --- [nio-8083-exec-4] h.b.services.CategoryServiceImpl : Non-fiction category were created
2017-06-14 09:47:06.547 INFO 6788 --- [nio-8083-exec-4] o.n.o.drivers.http.request.HttpRequest : Thread: 21, url: http://localhost:7474/db/data/transaction/25, request: {"statements":[{"statement":"MATCH (n:`Category`) WITH n MATCH p=(n)-[*0..1]-(m) RETURN p","parameters":{},"resultDataContents":["graph"],"includeStats":false}]}
2017-06-14 09:47:06.571 DEBUG 6788 --- [nio-8083-exec-4] o.s.b.w.f.OrderedRequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@241797fe
Create Math subcategory of Non-fiction parent category
2017-06-14 09:48:26.865 DEBUG 6788 --- [nio-8083-exec-7] o.s.b.w.f.OrderedRequestContextFilter : Bound request context to thread: org.apache.catalina.connector.RequestFacade@241797fe
2017-06-14 09:48:26.881 INFO 6788 --- [nio-8083-exec-7] o.n.o.drivers.http.request.HttpRequest : Thread: 24, url: http://neo4j:password@localhost:7474/db/data/transaction/commit, request: {"statements":[{"statement":"MATCH (n:`Category`) WHERE n.`name` = { `name_0` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)","parameters":{"name_0":"Math"},"resultDataContents":["graph","row"],"includeStats":false}]}
2017-06-14 09:48:26.916 INFO 6788 --- [nio-8083-exec-7] o.n.o.drivers.http.request.HttpRequest : Thread: 24, url: http://localhost:7474/db/data/transaction/30, request: {"statements":[{"statement":"MATCH (n) WHERE n.uuid = { id } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p","parameters":{"id":"3a839cc1-9af7-45fd-a29c-bfe96705655e"},"resultDataContents":["graph"],"includeStats":false}]}
2017-06-14 09:48:26.939 INFO 6788 --- [nio-8083-exec-7] o.n.o.drivers.http.request.HttpRequest : Thread: 24, url: http://localhost:7474/db/data/transaction/31, request: {"statements":[{"statement":"UNWIND {rows} as row MERGE (n:`Category`{uuid: row.props.uuid}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type","parameters":{"rows":[{"nodeRef":-1188832417,"type":"node","props":{"name":"Math","uuid":"3a839cc1-9af7-45fd-a29c-bfe96705655e","dateAdded":"17-06-14"}}]},"resultDataContents":["row"],"includeStats":false}]}
2017-06-14 09:48:26.995 INFO 6788 --- [nio-8083-exec-7] o.n.o.drivers.http.request.HttpRequest : Thread: 24, url: http://localhost:7474/db/data/transaction/31, request: {"statements":[{"statement":"UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`parent`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type","parameters":{"rows":[{"startNodeId":7,"relRef":-240693881,"type":"rel","endNodeId":6}]},"resultDataContents":["row"],"includeStats":false},{"statement":"UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`children`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type","parameters":{"rows":[{"startNodeId":6,"relRef":-12081903,"type":"rel","endNodeId":7}]},"resultDataContents":["row"],"includeStats":false}]}
2017-06-14 09:48:27.288 INFO 6788 --- [nio-8083-exec-7] o.n.o.drivers.http.request.HttpRequest : Thread: 24, url: http://localhost:7474/db/data/transaction/32, request: {"statements":[{"statement":"MATCH (n:`Category`) WITH n MATCH p=(n)-[*0..1]-(m) RETURN p","parameters":{},"resultDataContents":["graph"],"includeStats":false}]}
2017-06-14 09:48:27.344 DEBUG 6788 --- [nio-8083-exec-7] o.s.b.w.f.OrderedRequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@241797fe
Create Biology subcategory of Non-fiction parent category
2017-06-14 09:51:12.367 DEBUG 6788 --- [nio-8083-exec-1] o.s.b.w.f.OrderedRequestContextFilter : Bound request context to thread: org.apache.catalina.connector.RequestFacade@241797fe
2017-06-14 09:51:12.385 INFO 6788 --- [nio-8083-exec-1] o.n.o.drivers.http.request.HttpRequest : Thread: 18, url: http://neo4j:password@localhost:7474/db/data/transaction/commit, request: {"statements":[{"statement":"MATCH (n:`Category`) WHERE n.`name` = { `name_0` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)","parameters":{"name_0":"Biology"},"resultDataContents":["graph","row"],"includeStats":false}]}
2017-06-14 09:51:12.394 INFO 6788 --- [nio-8083-exec-1] o.n.o.drivers.http.request.HttpRequest : Thread: 18, url: http://localhost:7474/db/data/transaction/37, request: {"statements":[{"statement":"MATCH (n) WHERE n.uuid = { id } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p","parameters":{"id":"8004d142-85c5-461b-862e-aee7ddfc90fa"},"resultDataContents":["graph"],"includeStats":false}]}
2017-06-14 09:51:12.405 INFO 6788 --- [nio-8083-exec-1] o.n.o.drivers.http.request.HttpRequest : Thread: 18, url: http://localhost:7474/db/data/transaction/38, request: {"statements":[{"statement":"UNWIND {rows} as row MERGE (n:`Category`{uuid: row.props.uuid}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type","parameters":{"rows":[{"nodeRef":-1174392366,"type":"node","props":{"name":"Biology","uuid":"8004d142-85c5-461b-862e-aee7ddfc90fa","dateAdded":"17-06-14"}}]},"resultDataContents":["row"],"includeStats":false}]}
2017-06-14 09:51:12.414 INFO 6788 --- [nio-8083-exec-1] o.n.o.drivers.http.request.HttpRequest : Thread: 18, url: http://localhost:7474/db/data/transaction/38, request: {"statements":[{"statement":"UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`parent`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type","parameters":{"rows":[{"startNodeId":8,"relRef":-1580985829,"type":"rel","endNodeId":6}]},"resultDataContents":["row"],"includeStats":false},{"statement":"UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`children`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type","parameters":{"rows":[{"startNodeId":7,"relRef":-2084880007,"type":"rel","endNodeId":6},{"startNodeId":6,"relRef":-38658551,"type":"rel","endNodeId":8}]},"resultDataContents":["row"],"includeStats":false}]}
2017-06-14 09:51:12.456 INFO 6788 --- [nio-8083-exec-1] o.n.o.drivers.http.request.HttpRequest : Thread: 18, url: http://localhost:7474/db/data/transaction/39, request: {"statements":[{"statement":"MATCH (n:`Category`) WITH n MATCH p=(n)-[*0..1]-(m) RETURN p","parameters":{},"resultDataContents":["graph"],"includeStats":false}]}
2017-06-14 09:51:12.520 DEBUG 6788 --- [nio-8083-exec-1] o.s.b.w.f.OrderedRequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@241797fe
@PostMapping("/admin/category/newMid")
String newMidCategory(Model m, @RequestParam("newMidCategory") String newMidCategoryName,
@RequestParam("selectedMainCategory") Category mainCategory) {
if (mainCategory != null) {
Category existing = categoryService.findCategoryByName(newMidCategoryName);
if (existing == null) {
// indeed it is new category
Category newMidCategory = new Category(newMidCategoryName, mainCategory);
mainCategory.addChildren(newMidCategory);
categoryService.insertNewCategory(newMidCategory);
} else {
m.addAttribute("midCatError", ctx.getMessage("error.admin.MidCategoryExistsAlready", null,
new Locale(env.getProperty("spring.mvc.locale"))));
}
} else {
m.addAttribute("midCatError", ctx.getMessage("error.admin.mainCategoryNotSelected", null,
new Locale(env.getProperty("spring.mvc.locale"))));
}
m.addAttribute("midCategories", categoryService.getChildrenOfParent(mainCategory));
m.addAttribute("allMainCategories", categoryService.getAllMainCategories());
return "admin/category :: #categoryForm";
}
经过几个小时的调试,结果证明子类别(数学)子集合中有一个成员是非虚构的主类别,因此OGM只是正确地映射了它
问题是我的私有children属性如何能够在不调用公共方法addChildren(类别c)的情况下进行修改。在我的addChildren方法中有一行System.out.println()可以查看何时调用它。它只被调用了两次:第一次当我们添加数学子类别时,第二次当我们添加生物学子类别时。
如果没有addChildren调用,Math子类别是否有子类?
这是什么?这看起来像个bug
您还可以尝试为关系字段的setter添加注释,作为一种解决方法:
@Relationship(type = "parent", direction = Relationship.OUTGOING)
private Category parent;
@Relationship(type = "children", direction = Relationship.OUTGOING)
private Set<Category> children;
@Relationship(type = "parent", direction = Relationship.OUTGOING)
public void setParent(Category parent) {
this.parent = parent;
}
@Relationship(type = "children", direction = Relationship.OUTGOING)
public void setChildren(Set<Category> children) {
this.children = children;
}
@关系(type=“parent”,direction=Relationship.OUTGOING)
私人类别家长;
@关系(type=“children”,direction=Relationship.OUTGOING)
私人儿童;
@关系(type=“parent”,direction=Relationship.OUTGOING)
公共无效集合父对象(类别父对象){
this.parent=parent;
}
@关系(type=“children”,direction=Relationship.OUTGOING)
公共无效集合子对象(集合子对象){
这个。孩子=孩子;
}
如果您可以使用SDN/OGM问题模板复制它,那就太好了 如果两个字段的结束节点类型相同,则可能需要使用与字段相同的@Relationship注释setter。我相信这在2.1.4中是固定的 你能试试OGM版本2.1.3中是否存在问题吗?@frant.hartm我刚刚升级了OGM版本2.1.3,我仍然有同样的问题。创建新的子类别时,会正确创建单向父、子关系,但先前创建的子类别会获得新的子关系,尽管其子类别应保持为空。当我使用主类别的子类别集添加新的子类别时,以前正确创建的子类别已更改,但不是由我更改的。您建议的解决方法工作正常。我将在这个问题上发表意见。谢谢你的帮助