使用IdClass声明的复合密钥的deleteByteId的JPA查询



问题

如果我已经使用@IdClass声明了我的(复合)主键,那么我如何编写@Query以便能够使用Collection<MyIdClass>发出DELETE查询?

次要问题

尽管使用了@Query,CASCADE是否真的会触发删除相关的AnotherEntity

当前型号

@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问题,它似乎非常接近我想要的,但不幸的是没有答案。

目标

类似于:

@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>),但实际上我需要从这些实体开始,这将需要对DB进行额外的调用。

还有deleteById(MyIdClass),但它实际上总是在将单个DELETE语句作为事务发送之前发出findById:这对性能不好!

潜在无关精度

我不确定这是否有帮助,但我的JPA提供商是EclipseLink。我的理解是,有用于批处理请求的属性,而这正是我最终要使用的。

然而,我不完全确定要进行批处理的内部要求是什么。例如,如果我在for-loop中执行deleteById,那么交替的SELECTDELETE语句会阻止批处理吗?关于这方面的文献资料很少。

如果您确信在您的情况下,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=?]...,导致此测试通过(具有下表记录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());
}

我认为您正在寻找能够生成类似的查询的东西

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

你可以试试这个帖子,它可能会对你有所帮助。

这个答案提供了很好的见解,但这种方法似乎只适用于Hibernate。EclipseLink是我被迫使用的JPA提供程序,对于相同的代码,它会不断向我抛出错误。

我发现的唯一有效的解决方案是以下破解:

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);
}

数据库的关联索引(Postgres)

DROP INDEX IF EXISTS concatenated_pk_index;
CREATE UNIQUE INDEX concatenated_pk_index ON myentity USING btree (( foo || '~' || bar ));

说明

由于EclipseLink拒绝正确处理我的@IdClass,我不得不调整服务,将复合键连接到一个String中。然后,在Postgres中,您实际上可以在不同组合键列的串联上创建索引。

将索引标记为UNIQUE将极大地提高该查询的性能,但只有当您确信连接是唯一的(在我的情况下,这是因为我使用了组合键的所有列)时,才应该这样做。

然后,调用服务只需要执行类似String.join("~", dto.getFoo(), dto.getBar())的操作,并将所有这些操作收集到将传递给存储库的列表中。

相关内容

  • 没有找到相关文章

最新更新