Java Spring级联元素集合删除
Posted
技术标签:
【中文标题】Java Spring级联元素集合删除【英文标题】:Java Spring cascade elementcollection delete 【发布时间】:2020-08-12 23:22:21 【问题描述】:由于某种原因,当我尝试删除其中包含 elementcollection 的父元素时,我的 delete 没有级联,这两个类如下:
@Entity
@Table(name="Timestamps")
@JsonIgnoreProperties(ignoreUnknown = true)
public class ProductList
private boolean success;
@Id
private Date lastUpdated;
private String time = "minute";
@ElementCollection
@MapKeyColumn(name="product_id")
@CollectionTable(name="Products")
private Map<String,Product> products;
还有:
@Embeddable
@JsonIgnoreProperties(ignoreUnknown = true)
public class Product
@Embedded
private Status quick_status;
目前这是我在班级中唯一的字段,因为我已经删除了所有其他字段,试图弄清楚为什么当我尝试删除父对象时删除不会级联到 Products 表。以下是我正在运行的查询:
DELETE FROM Timestamps list WHERE list.last_updated !=0;
last_updated 值总是非零,所以我只是使用这个查询来测试删除,但即使我在 mysql shell 中运行查询,我也会得到“无法删除或更新父行:外键约束失败”我认为 elementcollection 注释应该是自动级联的,有什么我遗漏的吗?
编辑,当下面是 Hibernate 发送的 sql 命令时,你会注意到第三个它缺少级联。
Hibernate: create table products (product_list_last_updated datetime(6) not null, buy_price float not null, sell_price float not null, product_id varchar(255) not null, primary key (product_list_last_updated, product_id)) engine=InnoDB
Hibernate: create table timestamps (last_updated datetime(6) not null, success bit not null, time varchar(255), primary key (last_updated)) engine=InnoDB
Hibernate: alter table products add constraint FKo31ur4gpvx1d5720rgs3qaawi foreign key (product_list_last_updated) references timestamps (last_updated)
编辑 2:下面是 ProductListRepository 类的@Query,我包含在与删除相关的查询中。
@Repository
public interface ProductListRepository extends CrudRepository<ProductList, Integer>
@Modifying
@Query(value = "DELETE FROM Timestamps list WHERE list.last_updated !=0", nativeQuery = true)
void deleteOld();
【问题讨论】:
您如何发布删除声明:DELETE FROM Timesta...
?直接在您的 SQL 编辑器中?或者,在@Query
?或者是其他东西?如果您尝试使用对应的产品存储库删除它会发生什么情况:例如:productRepository.delete(productsToBeDeleted)
?
@Rafa 好吧,我已经尝试了这两种方法,最终目标是让它与 Query 注释一起运行,但我一直在使用 MySQL shell 对其进行测试,因为如果我可以让它工作那么在存储库类中使用它应该没问题。我也试过 productRepository.deleteAll();它确实级联了删除,但是为什么它可以工作,但指定使用 Query 注释并将其直接输入 MySQL shell 不起作用?
@IDKWhatImDoing ,您想要实现的方式似乎很疯狂。为什么要在删除日志时删除产品???如果在删除产品时删除日志,我会更有意义。没有感觉仍然存储已删除产品的日志。
【参考方案1】:
(警告:我不知道设计师的想法。)
对我来说,自动删除数据很可怕。我的一点点误会可能会导致我没想到的数据被删除。
当然,在精心设计的教科书式架构中,数据将具有完美的结构,应该清楚哪些要删除,哪些不应该删除。
但教科书模式仅限于教科书。
【讨论】:
虽然在级联删除中射中自己的脚可能是其他关系的问题,例如@OneToMany
这里在删除父级时级联删除是唯一合乎逻辑的过程,因为集合没有父元素的嵌入元素没有意义,因为它们是父元素的一部分。类似于通常使用 @Embedded
将单个类嵌入到实体中的方式,会将子字段的列嵌入到父表中并删除工作,这里的想法是相同的,它只需要一个不同的表用于一对多的喜欢关系【参考方案2】:
有几个变量在起作用。 @ElementCollection
有一些限制。见:https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection
使用 ElementCollection 而不是 OneToMany 的限制 是目标对象不能被查询、持久化、合并 独立于它们的父对象。他们严格 私有(依赖)对象,与嵌入式映射相同。 ElementCollection 上没有级联选项,目标对象 总是与它们的父级保持、合并、删除。 ElementCollection 仍然可以使用 fetch 类型并默认为 LAZY 与其他集合映射相同。
它按预期工作,因为 productRepository.deleteAll()
有效。
为什么不能使用原生查询? 因为原生查询是“按原样”执行的,这意味着它不会考虑实体中的注释。
另外,因为它是用@ElementCollection
注释的,所以你的constraint foreign key
中没有包含任何ON DELETE CASCADE
也就是说,这个alter table...
没有ON DELETE CASCADE
alter table products add constraint FKo31ur4gpvx1d5720rgs3qaawi foreign key (product_list_last_updated) references timestamps (last_updated)
建议的解决方案 1
使用预期的级联选项从 @ElementCollection
更改为 @OneToMany
。
建议的解决方案 2
删除 nativeQuery = true
并改用 JPA 查询。它应该类似于:
@Modifying
@Query("DELETE FROM ProductList list WHERE list.lastUpdated != 0")
void deleteOld();
建议的解决方案 3
在 ProductListRepository
中使用 Spring Data 命名查询。比如:
deleteByLastUpdatedNot(Date date);
or
deleteByLastUpdatedLessThan(Date date);
【讨论】:
正如您在回答中所述,alter table
没有ON CASCADE DELETE
,但这是问题的症结所在。为什么它不会自动应用级联?正如引用中所说的“目标对象总是与其父对象一起持久化、合并、删除”,但是如果它不级联删除,它如何与父对象一起删除子对象?这似乎不必要地限制了您与数据库的交互方式。
至于解决方案,解决方案 1 是我最终可能不得不使用的,尽管这是一种解决方法,我更愿意继续使用 @ElementCollection
注释。解决方案 2 不起作用,它遇到与本机查询相同的错误,这是有道理的,因为使用非本机查询只是编写查询的一种更好的方式,但它最终会被处理为我正在使用的本机查询,因为那是它可以运行的唯一方法。最后一个也可以,但它有其自身的局限性,我更愿意使用@Query
指定我自己的查询
嗨@IDKWhatImDoing,ON CASCADE DELETE
是createc 以及级联选项,例如:@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
。哼,我不知道Solution2 在@ElementCollection
上不起作用。我个人觉得 JPA 非常固执己见,有时甚至死板。对于高灵活性JDBC Template
是您编写自己的查询的好选择。
Option2 是批量删除。 HIbernate 不会为可嵌入集合实体生成 delete-sql-statements。这就是问题所在。 productRepository.deleteAll()
为每个元素生成 1 个 delete-sql 语句。这是非常低效的。由于所有这些限制,我看不到 @ElementCollection 比 @ManyToOne 有任何好处。【参考方案3】:
在 mysql shell 中,您无法执行查询,因为您没有在 mysql 中设置外键 ON DELETE CASCADE。将 ON DELETE CASCADE 添加到您的外键将允许 mysql shell 在删除时级联。
日志一经删除就删除产品似乎不合逻辑。
我认为结构必须是这样的:
创建表products
(id
INT NOT NULL AUTO_INCREMENT, buy_price
FLOAT, sell_price
FLOAT, lastUpdateDate
DATETIME, 主键 (id
));
创建表timestamps
(id
INT NOT NULL AUTO_INCREMENT,product
INT NOT NULL,success
BIT NOT NULL,updateDate
DATETIME NOT NULL,主键(id
),外键(@ 987654333@) 参考products
(id
) 删除级联);
所以现在当你删除一个产品时,它的日志就会被删除。
【讨论】:
以上是关于Java Spring级联元素集合删除的主要内容,如果未能解决你的问题,请参考以下文章
为啥手动定义的 Spring Data JPA 删除查询不会触发级联?