我正在使用Spring-Data-Neo4J (4.0.0.RELEASE)构建一个应用程序。应用程序有两个实体,一个Person实体和一个Group实体。还有一个关系实体Member_Of,它表示一个人是一个组的成员。实体如下所示
@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;
}
有一个向组添加成员的方法。该方法带有@Transactional注释。示例代码如下:
@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);
}
它试图做的是,它搜索personToAdd和组之间存在的关系。如果返回null,即不存在任何关系,则将其添加到组中。
问题是有时同一个人被多次加入同一组。当两个人都在运行应用程序并且他们都试图将同一个人添加到同一组时,就会发生这种情况。
如何防止这种情况发生?我需要在一个人和组之间有一个单一的关系,而不是多个。
在两个给定实体之间只创建一个关系,前提是关系实体上的所有属性都相等。你有一个时间戳,这是这里的罪魁祸首——SDN意识到这两种关系不同,因为它们对一个属性有不同的值,然后继续创建第二个关系。
目前,SDN没有配置允许您为关系实体指定合并与创建。
您可能需要在应用程序级别管理一些同步。
我有同样的问题,并且能够通过使用自定义密码查询来"解决"它。假设您已经成功添加了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);
可以这样调用:
personRepository.isMemberOf(personUuid,groupUuid,uuid,date);
然而,不要认为这是理所当然的。我还没有做大量的测试来确保这种方法是线程安全的。
William Lyon关于MERGE的原子执行的回答可能是您必须采取的额外步骤。