Spring mvc 弹簧数据Neo4j在保存期间插入意外的关系(边)

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

我使用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 {

@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,我仍然有同样的问题。创建新的子类别时,会正确创建单向父、子关系,但先前创建的子类别会获得新的子关系,尽管其子类别应保持为空。当我使用主类别的子类别集添加新的子类别时,以前正确创建的子类别已更改,但不是由我更改的。您建议的解决方法工作正常。我将在这个问题上发表意见。谢谢你的帮助