[英]JPA Query to deleteById for a Composite Key declared using IdClass
如果我已經使用@IdClass
聲明了我的(復合)主鍵,我該如何編寫我的@Query
以便能夠使用Collection<MyIdClass>
發出DELETE
查詢?
請問CASCADE實際上觸發相關的缺失AnotherEntity
盡管使用@Query
?
@Entity
@Table(name = "myentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@IdClass(MyIdClass.class)
public class MyEntity {
@Id
@Column(updatable = false)
private String foo;
@Id
@Column(updatable = false)
private String bar;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "my_foreign_key", referencedColumnName = "external_pk")
private AnotherEntity anotherEntity;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyIdClass implements Serializable {
private String foo;
private String bar;
}
@Entity
@Table(name = "anotherentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class AnotherEntity {
@Id
@Column(name = "external_pk", nullable = false, updatable = false)
private String externalPk;
}
一些資源:
而且我還發現這個 SO question似乎非常接近我正在尋找的問題,但不幸的是沒有答案。
類似於:
@Repository
public interface MyCRUDRepository extends CrudRepository<MyEntity, MyIdClass> {
@Modifying
@Query("DELETE FROM myentity m WHERE m IN ?1") // how do I write this?
void deleteAllWithIds(Collection<MyIdClass> ids);
}
最終,我想這樣做來批處理我的 DELETE 請求以提高性能。
我知道有一個deleteAll(Iterable<? extends MyEntity>)
但是我實際上需要從這些實體開始,這需要額外調用數據庫。
還有deleteById(MyIdClass)
,但實際上總是在將單個 DELETE 語句作為事務發送之前發出一個findById
:對性能findById
!
我不確定這是否有幫助,但我的 JPA 提供程序是EclipseLink
。 我的理解是有批處理請求的屬性,這就是我最終要使用的。
但是,我不完全確定要進行批處理的內部要求是什么。 例如,如果我在for-loop
執行deleteById
,交替的SELECT
和DELETE
語句會阻止批處理的發生嗎? 關於這一點的文檔非常少。
如果您確定在您的情況下 IdClass 是比 EmbeddedId 更好的選擇,您可以添加一個額外的映射到 MyEntity :
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "foo",
column = @Column(name = "foo", insertable = false, updatable = false)),
@AttributeOverride(name = "bar",
column = @Column(name = "bar", insertable = false, updatable = false))})
private MyIdClass id;
並在您的存儲庫中使用它:
@Modifying
@Query("DELETE FROM MyEntity me WHERE me.id in (:ids)")
void deleteByIdIn(@Param("ids") Collection<MyIdClass> ids);
這將生成一個查詢: delete from myentity where bar=? and foo=? [or bar=? and foo=?]...
delete from myentity where bar=? and foo=? [or bar=? and foo=?]...
delete from myentity where bar=? and foo=? [or bar=? and foo=?]...
,導致此測試通過(以下表記錄insert into myentity(foo,bar) values ('foo1', 'bar1'),('foo2', 'bar2'),('foo3', 'bar3'),('foo4', 'bar4');
):
@Test
@Transactional
void deleteByInWithQuery_multipleIds_allDeleted() {
assertEquals(4, ((Collection<MyEntity>) myEntityRepository.findAll()).size());
MyIdClass id1 = new MyIdClass("foo1", "bar1");
MyIdClass id2 = new MyIdClass("foo2", "bar2");
assertDoesNotThrow(() -> myEntityRepository.deleteByIdIn(List.of(id1, id2)));
assertEquals(2, ((Collection<MyEntity>) myEntityRepository.findAll()).size());
}
這個答案提供了很好的見解,但似乎該方法僅適用於 Hibernate。
EclipseLink 是我使用的 JPA 提供程序,對於相同的代碼,它會不斷向我拋出錯誤。
我發現的唯一可行的解決方案是以下 hack:
@Repository
JPA 查詢@Repository
public interface MyCRUDRepository extends CrudRepository<MyEntity, MyIdClass> {
@Modifying
@Query("DELETE FROM myentity m WHERE CONCAT(m.foo, '~', m.bar) IN :ids")
void deleteAllWithConcatenatedIds(@Param("ids") Collection<String> ids);
}
DROP INDEX IF EXISTS concatenated_pk_index;
CREATE UNIQUE INDEX concatenated_pk_index ON myentity USING btree (( foo || '~' || bar ));
由於 EclipseLink 拒絕正確處理我的@IdClass
,我不得不調整服務以將復合鍵連接成單個字符串。 然后,在 Postgres 中,您實際上可以在不同組合鍵列的串聯上創建索引。
將索引標記為UNIQUE
將極大地提高該查詢的性能,但只有在您確定串聯是唯一的(在我的情況下是因為我使用復合鍵的所有列)時才應該這樣做。
然后調用服務只需執行類似String.join("~", dto.getFoo(), dto.getBar())
並將所有這些收集到將傳遞給存儲庫的列表中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.