Вот рабочее решение.Прежде чем я углублюсь в детали: ключ в том, какие вещи вы упорствуете.Вы должны стремиться к четкому ограниченному контексту и просто получить доступ к интересам к одному агрегату.Я решил для пользователя быть точкой входа в вещи.У пользователя есть интересы, и интересы следует добавлять и манипулировать через пользователя.
OGM и Spring Data Neo4j заботятся о сохранении отношений, исходящих от пользователя.
Итак, основные моменты: ДонНе спасай каждый NodeEntity
сам.Сохранять ассоциации между сущностями неявным образом, то есть: сохранять только родительский объект.Вы можете сделать это через сам сеанс или, как я это сделал, через хранилище.Обратите внимание, что вам не нужен репозиторий для каждой сущности.
Я опустил пользовательские стратегии, поскольку вы ими не делитесь.Я полагаюсь на сгенерированные идентификаторы.Если мой пример не соответствует вашим стратегиям, возможно, это хороший совет, где искать ошибки.
У нас есть интерес:
@NodeEntity
public class Interest {
@Id
@GeneratedValue
private Long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
А интерес пользователей:
@RelationshipEntity(type = UserInterest.TYPE)
public class UserInterest {
public static final String TYPE = "INTERESTED_IN";
private Long id;
@StartNode
private User start;
@EndNode
private Interest end;
private Long weight;
public void setStart(User start) {
this.start = start;
}
public Interest getEnd() {
return end;
}
public void setEnd(Interest end) {
this.end = end;
}
public void setWeight(Long weight) {
this.weight = weight;
}
}
И, наконец, пользователь:
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@Relationship(type = UserInterest.TYPE, direction = Relationship.OUTGOING)
private Set<UserInterest> interests = new HashSet<>();
public void setName(String name) {
this.name = name;
}
public Interest setInterest(String interstName, long weight) {
final UserInterest userInterest = this.interests.stream()
.filter(i -> interstName.equalsIgnoreCase(i.getEnd().getName()))
.findFirst()
.orElseGet(() -> {
// Create a new interest for the user
Interest interest = new Interest();
interest.setName(interstName);
// add it here to the interests of this user
UserInterest newUserInterest = new UserInterest();
newUserInterest.setStart(this);
newUserInterest.setEnd(interest);
this.interests.add(newUserInterest);
return newUserInterest;
});
userInterest.setWeight(weight);
return userInterest.getEnd();
}
}
См. setInterest
.Это один из способов использования User
в качестве совокупного корня для доступа ко всем вещам.Здесь: интерес.Если он существует, просто измените вес, в противном случае создайте новый, включая UserInterest
, добавьте его к интересам пользователей, наконец установите вес и затем верните его для дальнейшего использования.
Затем я заявляю один репозиторий, только для пользователя:
public interface UserRepository extends Neo4jRepository<User, Long> {
Optional<User> findByName(String name);
}
А теперь приложение:
@SpringBootApplication
public class SorelationshipsApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SorelationshipsApplication.class, args);
}
private final UserRepository userRepository;
private final SessionFactory sessionFactory;
public SorelationshipsApplication(UserRepository userRepository, SessionFactory sessionFactory) {
this.userRepository = userRepository;
this.sessionFactory = sessionFactory;
}
@Override
public void run(String... args) throws Exception {
Optional<User> optionalUser = this.userRepository
.findByName("Michael");
User user;
ThreadLocalRandom random = ThreadLocalRandom.current();
if(optionalUser.isPresent()) {
// Redefine interests and add a new one
user = optionalUser.get();
user.setInterest("Family", random.nextLong(100));
user.setInterest("Bikes", random.nextLong(100));
user.setInterest("Music", random.nextLong(100));
} else {
user = new User();
user.setName("Michael");
user.setInterest("Bikes", random.nextLong(100));
user.setInterest("Music", random.nextLong(100));
}
userRepository.save(user);
// As an alternative, this works as well...
// sessionFactory.openSession().save(user);
}
}
Это всего лишь пример командной строки, работающий с моим локальным экземпляром Neo4j, ноЯ думаю, это объясняет все достаточно хорошо.
Я проверяю, существует ли пользователь.Если нет, создайте его и добавьте интерес.На следующем этапе измените существующие интересы и создайте новый.Любой дальнейший прогон просто изменяет существующие интересы.
Смотрите результат:
Добавьте бонус: если вы на Java 11,см ifPresentOrElse
на Optional
.Намного более идиоматический способ иметь дело с Дополнительными.
userRepository.findByName("Michael").ifPresentOrElse(existingUser -> {
existingUser.setInterest("Family", random.nextLong(100));
existingUser.setInterest("Bikes", random.nextLong(100));
existingUser.setInterest("Music", random.nextLong(100));
userRepository.save(existingUser);
}, () -> {
User user = new User();
user.setName("Michael");
user.setInterest("Bikes", random.nextLong(100));
user.setInterest("Music", random.nextLong(100));
userRepository.save(user);
});
Надеюсь, это поможет.
Редактировать: Вот мои зависимости:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>sorelationships</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sorelationships</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>