如何对JPA中的多级子级执行所有级联操作(PERSIST,UPDATE,REMOVE)

时间:2018-09-21 10:25:33

标签: spring-boot jpa

我有3个表(表1,表2,表3)。表1通过使用主键的@onetomany与表2相关。表2通过@manytoone与表3相关。表2具有EmbeddedId。

当我使用表1的主键获取详细信息时,便能够获取表2和表3中的数据。但是我无法进行保存和删除。在表1的子表(即表2)上进行保存和删除,但不影响表3(表2的子表)

下面是所有三个表的实体模型

 @Entity
    @Table(name = "FEATUREMASTER")
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

    public class FeatureMaster implements Serializable {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        @Id 
        @Column(name = "FGID")
        private String featureid;

        @Column(name = "FEATURENAME", nullable = false, unique = false)
        private String featurename;

        @Column(name = "DESCRIPTION", nullable = true, unique = false)
        private String description;

        @Column(name = "LIBNAME", nullable = true, unique = false)
        private String libname;

        @Column(name = "ISENABLED", nullable = false, unique = false)
        private String isenabled;

        @Column(name = "EDRULEGRP", nullable = true, unique = false)
        private String edrulegrp;

        // Do Not use - [orphanRemoval = true & CascadeType.ALL]- If used, deletion is not happening 
        @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
        @JoinColumn(name = "FGID")
        private List<CfgMaster> parameters;

// Getters and Setters
}


    @Entity
    @Table(name = "CFGMASTER")
    public class CfgMaster implements Serializable {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        @EmbeddedId
        private CfgMasterPK id;

        @Column(name = "CONFIGNAME", length = 45, nullable = true, unique = false)
        private String parameter_name;

        @Column(name = "CONFIGTYPE", length = 20, nullable = true, unique = false)
        private String type;

        @Column(name = "SUBPARAM", nullable = true, unique = false)
        private Integer subparam;

        @Column(name = "CONFIGDESCRIPTION", nullable = true, unique = false)
        private String description;

        @Column(name = "CONFIGLIMITFROM", nullable = true, unique = false)
        private String from;

        @Column(name = "CONFIGLIMITTO", nullable = true, unique = false)
        private String to;

        @ManyToOne(cascade = {CascadeType.ALL}, optional = true, fetch = FetchType.LAZY )
    //  @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
        @NotFound(action=NotFoundAction.IGNORE) // This is required to handle when no CfgData is found
        @JoinColumns({
                @JoinColumn(name = "FGID", insertable = false, updatable = false),
                @JoinColumn(name = "DATAKEY", insertable = false, updatable = false) 
                })
        private CfgData cfgData;

//Getters and Setters
    }

@Entity
@Table(name = "CFGDATA")
public class CfgData implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;


    /*@EmbeddedId
    private CfgDataPK id;*/

    @Id
    @Column(name = "FGID")
    private String fgid;

    @Id
    @Column(name = "DATAKEY")
    private String datakey;

    @Column(name = "EPID", nullable = false, unique = false)
    private int epid;


    @Column(name = "RESERVED1", length = 45, nullable = true, unique = false)
    private String reserved1;

    @Column(name = "VALUE1", length = 100, nullable = true, unique = false)
    private String value1;

    @Column(name = "VALUE2", length = 100, nullable = true, unique = false)
    private String value2;
     //Getters and Setters
}

我面临的问题是,我无法通过传递FeatureMaster的主要ID来删除/保存CfgData的实体。我所做的任何操作都只会影响父级&子级,而不会影响子级(CfgData) 我尝试了很多谷歌搜索,但找不到解决方法。

2 个答案:

答案 0 :(得分:3)

(这里有一个假设,CfgMaster的PK是FGID-希望这个权利。如果是的话……我想我可以解释发生了什么,尽管用当前的表映射来解决是棘手的)

问题似乎与外键上是否存在insertable = false, updatable = false有关。

如果在同一实体上有两个映射到同一列的属性,则通常使用这些属性。 Hibernate需要知道从哪个属性中设置列值,因此最多其中一个属性是可写的。

这似乎是这里的问题,但有两次:


首先在FeatureMaster上,parameters集合使用FGID的外键联接列。因为这是@OneToMany,所以它实际上是CFGMASTER上的一列,id属性已经(假定)已将其映射,因此第二个映射必须是只读的。 / p>

通过此更改,从FeatureMasterCfgMaster的级联删除开始起作用:

@OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
@JoinColumn(name = "FGID", insertable = false, updatable = false)
private List<CfgMaster> parameters = new ArrayList<>();

第二次在CfgMaster上,cfgData属性正在使用只读@JoinColumns。我认为原因是FGID列上的重叠?

不幸的是,由于这些是CFGMASTER上的外键列,因此有效地使CfgMaster.cfgData属性也变为只读。例如。切换到非重叠的可写列也启用了级联删除:

@ManyToOne(cascade = {
        CascadeType.ALL }, optional = true, fetch = FetchType.LAZY)
//  @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE) // This is required to handle when no CfgData is found
@JoinColumns({
        @JoinColumn(name = "FGID2"),
        @JoinColumn(name = "DATAKEY") })
private CfgData cfgData;

这甚至适用于孙级联删除。

但是,很明显,这引出了下一个问题-是否有任何方法可以使CfgData的PK也成为两部分外键的一部分?我看到过一些例子,其中外键是主键,但以前没有外加列。显然,您已经在此处进行了管理,但副作用是,该关系也是只读的,至少对于级联而言。

尽管这不是您希望听到的,但从Hibernate的角度来看这确实有意义。例如。如果@ManyToOne属性为空,则Hibernate希望将两列都清空,这对于主键是一个问题。除非其他人知道的更好,否则我认为选择是更改数据库映射,或者如果这不是一个选择,则需要对CfgData的级联删除进行编码。

答案 1 :(得分:0)

问题描述

该问题的根源是如果对其应用了PERSIST操作,则会重新持久保存已删除的实例。一个常见的错误是忽略PERSIST操作已应用于已删除的实例(通常是因为它是在刷新时从另一个实例级联的),因为JPA specification的第3.2.2节要求在这种情况下再次保留这种情况。

在您的示例中,当您从@ManyToOneCfgMaster定义了CfgData关联时,可以看到这种现象。在这种情况下,如果删除的CfgDataCfgMaster引用,则PERSIST操作将从CfgMasterCfgData级联,因为关联被标记为{ {1}},删除操作将未计划

  • 要验证这种情况是否发生,可以为cascade = CascadeType.ALL包启用跟踪日志级别,并搜索诸如 un-scheduling 实体删除之类的条目。

替代解决方法

  

org.hibernate不应在CascadeType.ALL上用作状态   实体的转换应从父实体(所有者端)传播到子实体,因此@ManyToOne始终是子关联,   应该避免级联。

您应该将@ManyToOne关联中的CascadeType.ALL设置为与其对应的@ManyToOne对应的@OneToMany


更多信息: