Spring Data Neo4j在保存期间插入意外关系(边缘)

时间:2017-06-13 14:23:49

标签: spring-mvc spring-data-neo4j-4

我使用Spring Boot 1.5.3和2.1.2的OGM。 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;
    }
}

在实践中,没有必要将对类别的父母和子女的引用保持为neo4j&#34;方向不可知&#34;但这是由于与绩效相关的问题。

SDN的一个非常奇怪的行为是,如果我创建一个类别(非小说)和一个子类别(数学),一切正常。如果我在同一主要类别下创建新的子类别(生物学),则前一个子类别与主要类别(数学和非小说)之间将存在意外关系。 日志显示以下内容:

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";
    }

经过几个小时的调试后发现,不知何故,子类别(Math)子集具有非小说主类别的成员,因此OGM只是正确映射它。

问题是如何在不调用公共方法addChildren(类别c)的情况下修改我的私有子属性。我的addChildren方法中有一个System.out.println()行来查看它何时被调用。它只被调用两次:首先我们添加数学子类别,第二次添加生物子类别。 没有addChildren调用Math子类别有子项? 这是什么?

2 个答案:

答案 0 :(得分:0)

这看起来像个错误。

您可以尝试注释关系字段的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;
}

如果您可以使用SDN / OGM问题模板重现它,那就太棒了。 https://github.com/neo4j-examples/neo4j-sdn-ogm-issue-report-template

答案 1 :(得分:0)

如果两个字段的结束节点类型相同,则可能需要使用与字段相同的@Relationship注释setter。我相信这在2.1.4中已得到修复。 https://github.com/neo4j/neo4j-ogm/issues/361