I have 3 tables (Table 1, Table 2, Table 3). Table 1 is related to Table 2 by @onetomany using primary key. Table 2 is related to Table 3 by @manytoone. Table 2 has EmbeddedId.
When I get the details using Table 1's primary key, I am able to fetch the data in Table 2 and Table 3. But I can't do save and delete. Save and delete is happening on the Table 1's child table (ie Table 2), but does not impact Table 3(which is child to Table 2)
Below are the entity models of all the three tables
@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
}
The problem I am facing is, I am not able to delete/save the entities of CfgData by passing FeatureMaster's primary id. Any operation I do is affecting only parent &child, not the grand child (CfgData) I tried a lot googling, but I cant find the solution.
(There's an assumption here that the PK for CfgMaster
is FGID
- hope this right. If so ... I think I can explain what's happening, though solving it with the current table mappings is tricky)
It looks like the problem relates to the presence or otherwise of the insertable = false, updatable = false
on the foreign keys.
The usual reason for using these if there are two properties on the same entity that map to the same column(s). Hibernate needs to know which of the properties to set the column value from, so at most one of the properties can be writeable.
It looks like this is the problem here, but twice:
Firstly on FeatureMaster
, the parameters
collection uses a foreign key join column of FGID
. Because this is a @OneToMany
this is actually a column on CFGMASTER
, which is (assumed to be) already mapped by the id
property, this second mapping needs to be read-only.
With this change, a cascade delete from FeatureMaster
to CfgMaster
started working:
@OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
@JoinColumn(name = "FGID", insertable = false, updatable = false)
private List<CfgMaster> parameters = new ArrayList<>();
Secondly on CfgMaster
, the cfgData
property is using read-only @JoinColumns
. I would think the reason for this is the overlap on the (assumed) FGID
column?
Unfortunately, because these are foreign key columns on CFGMASTER
, this effectively makes the CfgMaster.cfgData
property read-only too. E.g. switching to non-overlapping, writeable columns enabled the cascade delete here too:
@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;
This even worked for the grandchild cascade delete.
However, clearly this raises the next question - is there any way to have the PK of CfgData
also be part of a two-part foreign key? I have seen examples where the foreign key was the primary key, but not previously with an extra column. Clearly you have managed it here, but the side-effect is that the relationship is read-only too, at least for cascades.
Although it's not what you'll be hoping to hear, this does make some sense from Hibernate's perspective. E.g. if the @ManyToOne property were to be null'ed, Hibernate would want to blank both columns, which is a problem for the primary key. Unless someone else knows better, I think the choice is to change the DB mappings, or if that's not an option, you'll need to code the cascade delete of the CfgData
's.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With