【问题标题】:Spring JPA how to persist parent when the child is persisted in ManyToMany relationshipSpring JPA如何在孩子坚持ManyToMany关系时坚持父母
【发布时间】:2019-08-16 21:22:47
【问题描述】:

我有一个具有多对多关系的基本项目。有2个类ParentChild,关系的所有者是类Parent。当父实体被持久化时,子实体也被持久化(这是期望的行为)。但相反,当子实体被持久化时,父实体不会被持久化。

如何让父实体与子实体同时持久化?下面的代码给出了允许重现问题的 spring-boot 类,在 logger.info("---> 2nd fetch all parents"); 之后我希望有 5 个父母,但我只有两个。

这是我的实体类:

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    // @JoinTable => owner of the relationship
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "parent_child",
            joinColumns = @JoinColumn(name = "parent_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "child_id", referencedColumnName = "id"))
    private Set<Child> children;
}

@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @ManyToMany(mappedBy = "children")
    private Set<Parent> parents;

    // getters and setters
}

存储库

public interface ChildRepository extends JpaRepository<Child, Long> {}
public interface ParentRepository extends JpaRepository<Parent, Integer> {}

springboot 应用程序

@SpringBootApplication
public class Application implements CommandLineRunner {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    private ParentRepository parentRepository;

    @Autowired
    private ChildRepository childRepository;

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

    @Override
    @Transactional
    public void run(String... strings) throws Exception {
        // save a couple of parents
        Child childA = new Child("Child A"); Child childB = new Child("Child B"); Child childC = new Child("Child C");
        Parent parentA = new Parent("Parent A", new HashSet<>(Arrays.asList(childA, childB))); Parent parentB = new Parent("Parent B", new HashSet<>(Arrays.asList(childA, childC)));
        parentRepository.saveAll(Arrays.asList(parentA, parentB));

        // fetch all parents
        logger.info("---> 1st fetch all parents");
        for (Parent parent : parentRepository.findAll()) {
            logger.info(parent.toString());
        }

        // save a couple of children
        Parent parentD = new Parent("Parent D"); Parent parentE = new Parent("Parent E"); Parent parentF = new Parent("Parent F");
        Child childD = new Child("Child D", new HashSet<Parent>(Arrays.asList(parentD, parentE))); Child childE = new Child("Child E", new HashSet<Parent>(Arrays.asList(parentE, parentF)));
        childRepository.saveAll(Arrays.asList(childD, childE));

        // fetch all children
        logger.info("---> 1st fetch all children");
        for (Child child : childRepository.findAll()) {
            logger.info(child.toString());
        }

        // fetch all parents
        logger.info("---> 2nd fetch all parents");
        for (Parent parent : parentRepository.findAll()) {
            logger.info(parent.toString());
        }

        // fetch all children
        logger.info("---> 2nd fetch all children");
        for (Child child : childRepository.findAll()) {
            logger.info(child.toString());
        }
    }
}

【问题讨论】:

  • 这可能是因为Child#parents 属性缺少级联。默认情况下它没有级联。
  • 我尝试在Childclass 中添加@ManyToMany(mappedBy = "children", cascade = CascadeType.MERGE),但我仍然遇到问题
  • MERGE 用于合并(更新现有实体),而不是用于持久化。我会添加CascadeType.ALLParent#children 对称

标签: java spring hibernate jpa


【解决方案1】:

使用 JPA,当您想要将更新传播到关系项目时,您必须指定要应用的传播类型。

因此,当您定义关系 ManyToMany 时,您可以添加级联类型“PERSIST”来传播实体 Parent 的指令 INSERT

@ManyToMany(mappedBy = "children", cascade = CascadeType.PERSIST)
private Set<Parent> parents;

这里是 Springboot 使用的 Hibernate 的完整规范

如果您使用注释,您可能已经注意到 cascade 属性将 CascadeType 数组作为值。 JPA 中的级联概念与上述的传递持久性和操作级联非常相似,但语义和级联类型略有不同:

  • CascadeType.PERSIST :将持久(创建)操作级联到 关联实体persist() 被调用,或者如果实体被管理
  • CascadeType.MERGE :将合并操作级联到关联 如果调用 merge() 或管理实体,则为实体
  • CascadeType.REMOVE :级联删除操作到关联 如果调用 delete() 则为实体
  • CascadeType.REFRESH :级联 如果调用了 refresh(),则对关联实体进行刷新操作
  • CascadeType.DETACH :将分离操作级联到关联 如果 detach() 被称为 CascadeType.ALL 的实体:以上所有

【讨论】:

    猜你喜欢
    • 2014-03-16
    • 2016-05-13
    • 2017-06-25
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多