spring-boot - 如何在 JPA 中对多级子级执行所有级联操作(PERSIST、UPDATE、REMOVE)
问题描述
我有 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)我尝试了很多谷歌搜索,但我找不到解决方案。
解决方案
(这里假设 PKCfgMaster
是FGID
- 希望这是正确的。如果是这样......我想我可以解释发生了什么,尽管用当前的表映射解决它很棘手)
看起来问题与insertable = false, updatable = false
外键是否存在有关。
如果同一实体上有两个属性映射到同一列,则使用这些属性的通常原因。Hibernate 需要知道从哪个属性设置列值,因此最多可以有一个属性是可写的。
看起来这是这里的问题,但有两次:
首先FeatureMaster
,该parameters
集合使用 的外键连接列FGID
。因为 this is a @OneToMany
this 实际上是 上的一列CFGMASTER
,它(假设是)已经被id
属性映射,所以第二个映射需要是只读的。
通过此更改,级联删除 from FeatureMaster
toCfgMaster
开始工作:
@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;
这甚至适用于孙子级联删除。
然而,显然这引发了下一个问题——有没有办法让 PKCfgData
也成为两部分外键的一部分?我见过外键是主键的例子,但以前没有额外的列。显然你已经在这里管理它,但副作用是关系也是只读的,至少对于级联。
尽管这不是您希望听到的,但从 Hibernate 的角度来看,这确实有些道理。例如,如果@ManyToOne 属性为空,Hibernate 会希望将两列都清空,这对主键来说是个问题。除非其他人知道得更好,否则我认为选择是更改数据库映射,或者如果这不是一个选项,则需要编写级联删除的CfgData
's.
推荐阅读
- python - 为什么终端中的runserver命令不会运行?我(一个完整的初学者)正在使用 Django 构建一个 Web 应用程序(学习日志)
- vue.js - 如何使用 vee-validade 更改 axios 后面的规则
- javascript - 如何防止未经授权的用户使用反应路由访问页面
- powershell - Start-Command PowerShell v3.0,什么都不做
- java - 数组未按预期初始化
- android - Android - 如何实现无暂停/延迟的振动模式?
- sql - 如何计算取决于月份的值
- sql - 找不到sas的日志中指出的错误在哪里
- .net - 运行 dotnet test --collect "Code coverage" 时如何设置自定义覆盖结果文件路径?
- php - POST 请求在 POSTMAN 上工作,但不在 PHP 中