簡體   English   中英

使用 IdClass 聲明的復合鍵的用於 deleteById 的 JPA 查詢

[英]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;
}

我讀過的

一些資源:

  1. https://www.baeldung.com/spring-data-jpa-query
  2. https://www.baeldung.com/spring-data-jpa-delete
  3. https://stackoverflow.com/a/36765129/9768291

而且我還發現這個 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 ,交替的SELECTDELETE語句會阻止批處理的發生嗎? 關於這一點的文檔非常少。

我認為您正在尋找會生成這樣的查詢的東西

delete from myentity where MyIdClass in (? , ? , ?)

你可以試試這個帖子,它可能對你有幫助。

如果您確定在您的情況下 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:

Spring @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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM