简体   繁体   English

防止Neo4J中两个节点之间的多重关系

[英]Prevent Multiple Relationships between two nodes in Neo4J

I am building an application using Spring-Data-Neo4J (4.0.0.RELEASE). 我正在使用Spring-Data-Neo4J(4.0.0.RELEASE)构建应用程序。 The application has two entities, a Person entity, and a Group entity. 该应用程序具有两个实体,一个Person实体和一个Group实体。 There is also a relationship entity, Member_Of, which says that a person is a member of a group. 还有一个关系实体Member_Of,它表示一个人是一个组的成员。 The entities look like below 实体如下图所示

@NodeEntity(label="Person")
public class Person {
   protected String uuid;
   protected String fullName;
   protected String email;

   @Relationship(type = RelationshipNames.MEMBER_OF, direction = Relationship.OUTGOING)
   protected List<GroupMembership> groupMemberships = new ArrayList<GroupMembership>() ;
}

@NodeEntity(label="Group")
public class Group implements Serializable{
   protected static final long serialVersionUID = 1L;

   @GraphId
   protected Long id;
   protected String uuid;
   protected String name;

   @Relationship(type = RelationshipNames.MEMBER_OF, direction = Relationship.INCOMING)
   protected List<GroupMembership> groupMemberships = new ArrayList<GroupMembership>() ;
}

@RelationshipEntity(type = RelationshipNames.MEMBER_OF)
public class GroupMembership implements Serializable{
   private static final long serialVersionUID = 1L;

   @GraphId Long id;
   @Property String uuid; 
   @StartNode Person member;
   @EndNode Group group;
   @DateLong
   Date date;
}

There is a method which adds members to a group. 有一种将成员添加到组的方法。 The method is annotated with @Transactional. 该方法用@Transactional注释。 The sample code is given below. 示例代码如下。

@Override
@Transactional(propagation=Propagation.REQUIRED)
public ResponseEntity<GroupVO> addMembersToGroup(String groupUuid, String personUuid, List<String> personsToAdd){
   Group group = groupRepository.findGroupByUuid(groupUuid);
   List<Person> personsToAdd = IterableUtils.toList(personRepository.findPersonsByUuid(personUuids));

    personsToAdd.forEach(personToAdd -> {
        GroupMembership existingGroupMembership = getActiveMembership(group.getUuid(), personToAdd.getUuid());
        if(existingGroupMembership==null){
            GroupMembership newGroupMembership = new GroupMembership(personToAdd, group, GroupRoleNames.MEMBER, member.getUuid(), new Date());
            personToAdd.getGroupMemberships().add(newGroupMembership);
            group.getGroupMemberships().add(newGroupMembership);
   }
   groupRepository.save(group);
}

What it tries to do is that, it searches for a existing relationship between the personToAdd and the group. 它试图做的是,它搜索personToAdd和组之间的现有关系。 If it returns null, that is, no relationship exists, it adds it to the group. 如果返回null,即不存在任何关系,则将其添加到组中。

The problem is sometimes the same person is added multiple times to the same group. 问题有时是同一个人多次添加到同一组中。 This is happenning when two people are running the application and both of them tries to add the same person to the same group. 当两个人正在运行该应用程序并且他们都试图将同一个人添加到同一组时,就会发生这种情况。

How do I prevent this from happenning? 如何防止这种情况发生? I need to have a single relationship between a person and the group and not multiple ones. 我需要一个人和一个小组之间有一个单一的关系,而不是多个。

There will be only one relationship created between two given entities provided all properties on the relationship entity are equal. 只要关系实体上的所有属性均相等,则在两个给定实体之间将仅创建一个关系。 You have a timestamp which is the culprit here- SDN realises that the two relationships differ because they have different values for a property and goes ahead and creates the second one. 您有一个时间戳记,这是罪魁祸首-SDN意识到这两个关系是不同的,因为它们的属性值不同,因此继续创建第二个关系。

At the moment, SDN does not have configuration to allow you to specify a merge vs create for relationship entities. 目前,SDN没有配置允许您为关系实体指定合并还是创建。

You'll probably have to manage some synchronization at the application level. 您可能必须在应用程序级别管理一些同步。

I had the same issue, and was able to "solve" it by using a custom cypher query. 我遇到了同样的问题,并且能够通过使用自定义密码查询来“解决”它。 Given that you have added successfully Person and Group entity you can run the following query: 既然您已经成功添加了Person和Group实体,则可以运行以下查询:

 @Query("MATCH (group:Group) " +
        "MATCH (person:Person) " +
        "WHERE person.uuid={0} AND group.uuid={1} "+
        "MERGE (person)-[r:MEMBER_OF]->(group) " +
        "SET r.uuid ={2} , r.date={3} " +
        "RETURN r")
GroupMembership isMemberOf(String personUuid,String groupUuid, String uuid, Date date);

by calling it like this: 通过这样调用:

personRepository.isMemberOf(personUuid,groupUuid,uuid,date);

However , don't take it for granted. 但是 ,不要认为这是理所当然的。 I haven't done extensive tests to ensure that this approach is thread-safe. 我尚未进行大量测试以确保此方法是线程安全的。

This answer by William Lyon on atomic execution of MERGE may be an extra step that you have to take. 答案由威廉·里昂对MERGE的原子执行可能是,你必须采取额外的步骤。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM