gpt4 book ai didi

java - 如何使用 Spring Boot Data JPA 在一对多映射的子实体中设置parentId

转载 作者:行者123 更新时间:2023-11-30 02:00:29 26 4
gpt4 key购买 nike

用例:我们有一对多双向关系,我们将收到作为父项进行更新的请求,其中包含正在修改的子项或未修改的子项。

技术栈

  • Spring 启动 2.0.2
  • Spring 数据 Jpa

示例代码:

父类实体:

package com.example.demo.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@Entity
public class Parent {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String a;

private String b;

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent")
private Set<Child> childs = new HashSet<>();

public void addChild(Child child) {
childs.add(child);
child.setParent(this);
}

public void removeChild(Child child) {
childs.remove(child);
child.setParent(null);
}

public void setChilds(
Set<Child> childrens) {
if (this.childs == null) {
this.childs = childrens;
}
else {
this.childs.retainAll(childrens);
this.childs.addAll(childrens);
}
}

}

子类实体

package com.example.demo.model;

import java.util.Objects;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@Entity
@Table(name = "child", uniqueConstraints = {
@UniqueConstraint(columnNames = { "a", "b", "c", "parent_id" }) })
public class Child {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String a;

private String b;

private String c;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Child)) {
return false;
}
Child that = (Child) o;
return Objects.equals(getA(), that.getA()) && Objects.equals(getB(), that.getB())
&& Objects.equals(getC(), that.getC());
}

@Override
public int hashCode() {
return Objects.hash(getA(), getB(), getC());
}

}

存储库类:

package com.example.demo.model;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface ParentRepository extends JpaRepository<Parent, Long> {

@Query("select p from Parent p join fetch p.childs where p.a = ?1")
Parent findByA(String a);
}

带有业务案例的主要类(class):

package com.example.demo;

import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.Assert;

import com.example.demo.model.Child;
import com.example.demo.model.Parent;
import com.example.demo.model.ParentRepository;

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

@Autowired
ParentRepository repository;

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
Child c1 = new Child();
c1.setA("a1");
c1.setB("b1");
c1.setC("c1");
Child c2 = new Child();
c2.setA("a2");
c2.setB("b2");
c2.setC("c2");
Parent p = new Parent();
p.addChild(c1);
p.addChild(c2);
p.setA("a");

repository.save(p);

// This works till now

// We will get the request for updating parent which might contain removal or addition of the child

Parent retrievedParent = repository.findByA("a");
retrievedParent.setB("b");

Child c4 = new Child();
c4.setA("a2");
c4.setB("b2");
c4.setC("c2");
Child c3 = new Child();
c3.setA("a3");
c3.setB("b3");
c3.setC("c3");

//If we know that c1 is removed and c3 is added we can use synchronize methods written in Parent
//As we don't know which are removed and which are added also as we won't get the id from request passing them
// directly to set to let hibernate handle it as equals and Hashcode is already written.

Set<Child> childrens = new HashSet<>();
childrens.add(c3);
childrens.add(c4);
retrievedParent.setChilds(childrens);

Parent persistedParent = repository.save(retrievedParent);
for (Child child : persistedParent.getChilds()) {
Assert.notNull(child.getParent(), "Parent must not be null");
//For child 3 it is failing
}
}


}

使用上面的代码,无法为子实体 4 设置父 id,如果我们打印 SQL 日志,我们可以观察到 id 1 的子实体被删除,并且 id 3 的子实体被插入,这是预期的。

作为一种解决方法,我正在迭代所有子条目,如果未设置父条目,则手动设置。我不需要这个额外的更新声明。

尝试了其他方法,使用同步的removeChild方法删除所有子条目,然后使用同步的addChild方法逐一添加剩余的子条目。这导致唯一约束失败异常。

需要什么?在执行插入语句时设置父级而不是解决方法。

最佳答案

问题出在这部分:

Set<Child> childrens = new HashSet<>();
childrens.add(c3);
childrens.add(c4);
retrievedParent.setChilds(childrens);

您永远不需要重写托管集合。

现在,根据您的设计:

If we know that c1 is removed and c3 is added we can use synchronizemethods written in Parent.

As we don't know which are removed andwhich are added also as we won't get the id from request passing themdirectly to set to let hibernate handle it as equals and Hashcodeis already written.

如果客户端向您发送条目集合,您需要do the matching yourself ,这意味着您需要:

  • 添加新元素
  • 删除不再需要的元素
  • 更新现有的

关于java - 如何使用 Spring Boot Data JPA 在一对多映射的子实体中设置parentId,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53019285/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com