2016-06-25 8 views
1

Я создаю приложение с использованием Spring-Data-Neo4J (4.0.0.RELEASE). Приложение имеет два объекта, объект Person и объект Group. Существует также объект отношений Member_Of, в котором говорится, что человек является членом группы. Субъекты выглядят нижеПредотвращение множественных отношений между двумя узлами в Neo4J

@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, то есть отношения отсутствуют, он добавляет его в группу.

Проблема: один и тот же человек добавляется несколько раз в ту же группу. Это происходит, когда два человека запускают приложение, и оба они пытаются добавить одного и того же человека в ту же группу.

Как предотвратить это? Мне нужно иметь одну связь между человеком и группой, а не несколькими.

ответ

1

Будет создано только одно отношение между двумя данными объектами, если все свойства объекта отношения равны. У вас есть временная метка, которая является виновником здесь. SDN понимает, что эти два отношения отличаются друг от друга, потому что они имеют разные значения для свойства и идут вперед и создают вторую.

В настоящий момент SDN не имеет конфигурации, позволяющей указать объединение и создание для отношений объектов.

Возможно, вам придется управлять некоторой синхронизацией на уровне приложения.

+0

Временная метка обязательна для записи времени присоединения элемента к группе. Я сохранил чек, чтобы избежать дублирования отношений. Я проверяю, есть ли какие-либо существующие отношения. Если нет, добавьте только член. Я даже синхронизировал весь метод. Однако передача одновременных запросов приводит к дублированию отношений. Как я могу решить проблему? – Soumya

+0

Как реализация метода hashtag и equals для java-объектов влияет (если вообще) на то, как они хранятся или извлекаются из Neo4J? Поскольку я обнаружил, что если метод equals не реализован должным образом или только частично реализован, возникают проблемы при обновлении богатых отношений в Neo4j. Собственно, отношения теряются в последнем случае. – Soumya

1

У меня была такая же проблема, и я смог «решить» ее, используя собственный запрос cypher. Учитывая, что вы добавили успешно 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); 

Однако, не принимайте это как должное. Я не проводил обширных тестов, чтобы гарантировать, что этот подход является потокобезопасным.

This Ответ Уильяма Лиона на атомное исполнение MERGE может быть дополнительным шагом, который вы должны предпринять.

+0

Я пробовал это (без уникального ограничения), но он также создает повторяющиеся отношения. Мы широко используем SDN и использование cypher, в то время как создание отношений не рекомендуется, как сказано в «http://stackoverflow.com/questions/33461927/creating-relationships-in-4o-using-spring-data» – Soumya

Смежные вопросы