JPA Spring boot将新的子级附加到多个关系中的现有实体

JPA Spring boot将新的子级附加到多个关系中的现有实体,spring,spring-boot,jpa,Spring,Spring Boot,Jpa,在我看来,我发现jpa有一种奇怪的行为。首先,这里是我的代码的简化版本(如果我遗漏了什么,请告诉我) 所以现在我从Rest接口获取数据,以添加一个可能有名称的新子级 @Autowired ChildRpo childRepo; @Autowired ParentRepo parentRepo; @PostMapping("/example/{parentName}/{childName}") public void add(@PathVar("parentNam

在我看来,我发现jpa有一种奇怪的行为。首先,这里是我的代码的简化版本(如果我遗漏了什么,请告诉我)

所以现在我从Rest接口获取数据,以添加一个可能有名称的新子级

@Autowired
ChildRpo childRepo;
@Autowired
ParentRepo parentRepo;


@PostMapping("/example/{parentName}/{childName}")
public void add(@PathVar("parentName") String pName,
                @PathVar("childName") String cName) {
      // Here is the Problem I think 
      Parent p = parentRepo.findByName(pName).get();
      p.getChilds.add(new Child(cName,p);
      this.parentRepo.save(p);
}

案例1:pName=pa2&cName=child3(按预期工作)

案例2:pName=pa2&cName=child1(与我预期的不完全一样),但情况如何

| Parent     |  | parent_child_mapping |  | Child       |
| id | name  |  | child_id | parent_id |  | id | name   |
|  1 | pa1   |  |     1    |     1     |  |  1 | child1 |
|  2 | pa2   |  |     2    |     1     |  |  2 | child2 |    
                |     3    |     2     |  |  3 | child1 |    
案例3:pName=pa2&cName=child1我期望的是什么,但事实并非如此

| Parent     |  | parent_child_mapping |  | Child       |
| id | name  |  | child_id | parent_id |  | id | name   |
|  1 | pa1   |  |     1    |     1     |  |  1 | child1 |
|  2 | pa2   |  |     2    |     1     |  |  2 | child2 |    
                |     1    |     2     |     
我必须解决以下问题:

我认为unique属性使列唯一,因此具有相同内容的两个字符串不能同时存在于此行“name”中,但它可以工作。我希望在案例2中出现例外情况

第二个:

我如何配置它,使其像我希望的那样工作? 我必须加载childOne的id吗?并将它们附加到新的child1实体? 我想,因为这个领域是独一无二的,spring可以决定:

该字段不在列中,因此我添加了一个新的子字段。 该字段位于列中,因此我不会创建新的子项,而是将父项附加到具有相同名称的旧子项。-但默认情况下,这不是它的工作方式——有没有一种方式可以像上面描述的那样工作

附言:

以下是当前代码中的域模型:

Book -- oneToMany --> Page -- oneToMany --> Version -- ManyToOne --> Author
Book -- oneToMany --> Locations
..
如何在不加载所有关系的情况下向id=5的页面和id=4的作者添加“Quick”新版本

编辑:数据库由hibernate创建,以下是属性:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update
  • 只有在JPA提供者自动创建数据库时,
    @列中的Unique属性才起作用。如果可能,让提供商重新创建数据库。您可以从属性中执行此操作

    spring.jpa.hibernate.ddl auto=createdrop

  • 您必须在jpa接口中使用一个方法来查找(或不查找)使用该名称的子级。然后检查一个结果并创建一个新的子项,如果它还不存在的话。这应该很好:

     Parent p = parentRepo.findByName(pName).get();
     Child newChild;
     Optional<Child> result = childRpo.findByName(cName);
     if(result != null) {
         newChild = result.get();
     }
     else {
         newChild = new Child(cName);
     }
     p.getChilds.add(newChild);
     newChild.getParents().add(p);
     parentRepo.save(p);
    
    Parent p=parentRepo.findByName(pName.get();
    儿童-新生儿;
    可选结果=childRpo.findByName(cName);
    如果(结果!=null){
    newChild=result.get();
    }
    否则{
    newChild=新子(cName);
    }
    p、 getChilds.add(newChild);
    newChild.getParents().add(p);
    父母储蓄(p);
    
  • 只有在JPA提供者自动创建数据库时,
    @列中的Unique属性才起作用。如果可能,让提供商重新创建数据库。您可以从属性中执行此操作

    spring.jpa.hibernate.ddl auto=createdrop

  • 您必须在jpa接口中使用一个方法来查找(或不查找)使用该名称的子级。然后检查一个结果并创建一个新的子项,如果它还不存在的话。这应该很好:

     Parent p = parentRepo.findByName(pName).get();
     Child newChild;
     Optional<Child> result = childRpo.findByName(cName);
     if(result != null) {
         newChild = result.get();
     }
     else {
         newChild = new Child(cName);
     }
     p.getChilds.add(newChild);
     newChild.getParents().add(p);
     parentRepo.save(p);
    
    Parent p=parentRepo.findByName(pName.get();
    儿童-新生儿;
    可选结果=childRpo.findByName(cName);
    如果(结果!=null){
    newChild=result.get();
    }
    否则{
    newChild=新子(cName);
    }
    p、 getChilds.add(newChild);
    newChild.getParents().add(p);
    父母储蓄(p);
    

  • 这些表是手动创建的还是由hibernate创建的?您的问题肯定缺少详细信息,如
    p.getChilds.add(new Child(cName));
    不会对数据库做任何事情。如果您的示例应用程序具有
    Parent
    Child
    实体,您可以在注释中提供链接并在本地复制它。您是对的。我编辑的帖子不正确。您必须建立双向关系。
    Child newChild=newChild(cName);p.getChilds.add();newChild.setParent(p);this.parentRepo.save(p);
    同样,你是对的,我在孩子的构造函数中添加了缺少的关系作为功能。我可以通过frandpaste.com共享它们,但如果你想“工作”实体我认为您需要所有这些。这将是七个实体和七个回购。我将为您提供链接。这里有一个指向从实体到控制器的所有类的链接:您是手动创建表还是由hibernate创建的?您的问题肯定缺少详细信息,如
    p.getChilds.add(new Child(cName));
    不会对数据库做任何事情。如果您的示例应用程序具有
    Parent
    Child
    实体,您可以在注释中提供链接并在本地复制它。您是对的。我编辑的帖子不正确。您必须建立双向关系。
    Child newChild=newChild(cName);p.getChilds.add();newChild.setParent(p);this.parentRepo.save(p);
    同样,你是对的,我在孩子的构造函数中添加了缺少的关系作为功能。我可以通过frandpaste.com共享它们,但如果你想“工作”实体我认为你需要所有这些。这将是七个实体和七个回购。我将为你儿子提供链接这里有一个链接,指向从实体到控制器的所有类:是的,这个答案很有魅力,但如果你有很多孩子或父母或其他“重”(RAM使用方面)的话,它会有缓慢的一面关系。有没有一种方法可以在不加载洞实体的情况下归档完全相同的结果?我认为有一种方法,你必须多读一些关于它的内容,因为我不确定我是否可以解释它。因为这是一种多对多关系,你可以使用
    EmbeddedId
    而不是通常的ID生成,这样可以节省你的时间r childName和parent。这可能会解决您的问题,并且在保存之前不必加载子项。好的。@3asyPe答案对于您发布的原始问题是正确的,除了
    childRpo.save(newChild)如果您有cascade on parent,则不需要使用
    。现在您的功能没有问题。您在此处的评论是一个单独的问题,因此可能会提出一个单独的问题。或者,检查如何将您的父实体加载为代理,如果您当前的问题是PerformanceEyes,则直接使用代理保存子实体。这个答案很有魅力,但我如果你有很多孩子,那就慢了
    | Parent     |  | parent_child_mapping |  | Child       |
    | id | name  |  | child_id | parent_id |  | id | name   |
    |  1 | pa1   |  |     1    |     1     |  |  1 | child1 |
    |  2 | pa2   |  |     2    |     1     |  |  2 | child2 |    
                    |     3    |     2     |  |  3 | child1 |    
    
    | Parent     |  | parent_child_mapping |  | Child       |
    | id | name  |  | child_id | parent_id |  | id | name   |
    |  1 | pa1   |  |     1    |     1     |  |  1 | child1 |
    |  2 | pa2   |  |     2    |     1     |  |  2 | child2 |    
                    |     1    |     2     |     
    
    Book -- oneToMany --> Page -- oneToMany --> Version -- ManyToOne --> Author
    Book -- oneToMany --> Locations
    ..
    
    spring.datasource.url=jdbc:h2:mem:testdb
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=password
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
    # Hibernate ddl auto (create, create-drop, validate, update)
    spring.jpa.hibernate.ddl-auto= update
    
     Parent p = parentRepo.findByName(pName).get();
     Child newChild;
     Optional<Child> result = childRpo.findByName(cName);
     if(result != null) {
         newChild = result.get();
     }
     else {
         newChild = new Child(cName);
     }
     p.getChilds.add(newChild);
     newChild.getParents().add(p);
     parentRepo.save(p);