当前位置: 首页 > 知识库问答 >
问题:

在关系实体具有属性的情况下复制关系

关浩壤
2023-03-14

我使用Spring Data Neo4J 5.0.10与Spring Boot 2.0.5。节点I具有以下2个实体,用户兴趣实体和用户兴趣实体的关系。

@NodeEntity
public class User {

    private Long id;    

    @Id 
    @GeneratedValue(strategy = UserIdStrategy.class)
    @Convert(UuidStringConverter.class)
    private UUID userId;

    @Relationship(type = UserInterest.TYPE, direction = Relationship.OUTGOING)
    private Set<UserInterest> interests = new HashSet<>();

    ... getters/setters

@NodeEntity
public class Interest {

    private Long id;

    @Id 
    @GeneratedValue(strategy = InterestIdStrategy.class)
    private String interestId;

    private String name;

    ... getters/setters

@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;

    ... getters/setters

这很有效。我可以创建一个新用户并将该用户与userInterest关联。当我再次发送相同的详细信息时,节点和边不会重复。

当我在关系实体中启用权重属性时,即使权重属性值相同,关系似乎也是重复的。

我记得我读到过,只要属性相同,就不应该创建另一种关系,对吗?

这是预期的行为吗?我需要做什么来防止重复这种关系?

共有1个答案

凤凡
2023-03-14

这里有一个有效的解决方案。在我进入细节之前:关键是你坚持什么。您应该以清晰的有界上下文为目标,只访问一个聚合的兴趣。我决定将用户作为事物的入口点。用户有兴趣,应该通过用户添加和操纵兴趣。

OGM和Spring Data Neo4j负责保存从用户传出的关系。

因此,要点是:不要自己保存每个节点。以隐式方式保存实体之间的关联,即:仅保存父对象。您可以通过会话本身或者像我一样,通过存储库来实现这一点。请注意,您不需要为每个实体都提供存储库。

我省略了自定义策略,因为你没有分享它们。我依赖于生成的Ids。如果我的例子在你的策略上失败了,也许这是一个很好的提示,可以在哪里寻找错误。

我们有兴趣:

@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。这是使用用户作为聚合根访问所有内容的一种方法。这里是利息。如果存在,只需修改权重,否则创建一个新的权重,包括UserInterest,将其添加到users interest中,最后设置权重,然后返回以供进一步使用。

然后,我声明一个存储库,仅供用户使用:

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实例运行的命令行示例,但我认为它解释得很好。

我检查用户是否存在。如果没有,创建它并添加一些兴趣。在下一次运行时,修改现有兴趣并创建新兴趣。任何进一步的跑步只会改变现有的兴趣。

查看结果:

添加奖励:如果您在Java11,请参阅可选上的ifPresentOrElse。更惯用的处理期权的方式。

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>

 类似资料:
  • 早上好 我正在使用ModelMapper将DTO映射到实体,反之亦然,与此相关,我有一个问题。 1)当我从SonController获取SonDTO时,我需要将长的motherId映射到实体Son,但在那里我有实体mothermother来建立关系,它在内部有id。那么我如何将这个SonDTO motherId映射到实体mothermother中,反之亦然? 类以下:

  • 问题内容: 我有以下用于测试的类: 为了进行测试,我想通过一次SaveChanges调用插入一家公司和该公司的一名员工,如下所示: 即使我没有使用导航属性,而是通过Id建立了联系,但这种方式还是神秘地起作用了- 员工用正确的外键保存了公司的外键,该公司的外键从0更新为实际值,这使我走了出去!一些隐藏的C#功能? 然后,我决定添加更多代码,上面的代码段中对此进行了注释,使其插入了2个Company实

  • 关联实体和关联关系属性有什么区别?在我的一本名为《现代数据库管理》(Hoffer,第11版)的书中,作者陈述了两者之间的区别。然而,它并没有真正解释为什么会有差别,相反,它只是给出了它们是如何不同的例子。 据我所知,一个有一个属性关联的关系是一个关联关系属性,并用一条虚线表示一个圆角矩形,该矩形内有该属性。而关联实体是描述关系的多个属性。两者都只能用于ER图解中的多对多关系。我的思维过程正确吗?

  • 我决定在我最近开始的项目中使用JPA 2.1的实体图功能,但我遇到了一个问题。 当您将图形扩展到 ManyToOne 关系上时,效果很好,但对于 OneToMany,结果会重复,因为我的提供程序(Hibernate)使用左外连接。 我看到一些解决方案,他们提出了独特的关键字来解决这个问题,但我认为这是一个棘手的解决方案,即使我们如何才能让下一级实体不同。 我的意思是,如果我有3个实体A、B和C,我

  • 我有这些实体: 用户 角色 权限 一个用户有很多角色,一个角色有很多权限。 null

  • 我有一个实体,它与其他实体有关系,比如下面的实体。实际上,实体有许多领域。 对于一些搜索,我只对的Basic属性感兴趣,而不想检索任何关系。一个选项是使用