具有多列主键的 JPA 批量删除
JPA bulk delete with multi columns primary key
给定以下实体:
@Entity(name = "Object")
@Table(name = "OBJECT_TABLE")
public class ObjectEntity {
@EmbeddedId
private ObjectEntityPk id;
@Column(name = "SOME_ATTIBUTE")
private String someAttribute;
... Getters/Setters
}
以及以下主键对象:
@Embeddable
public class ObjectEntityPk {
@Column(name = "id1")
private String id1;
@Column(name = "id2)
private Long id2;
... Getters/Setters
}
我有 50 个 ObjectEntity 要删除,我想避免循环执行 50 个删除请求。所以我在我的 dao 中编写了以下代码来生成删除查询,例如 DELETE FROM OBJECT_TABLE WHERE (id1, id2) IN ((someId1, someId2), (someOtherId1, someOtherId2), ... )
public void deleteObjects(final List<ObjectEntity> objects) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaDelete<ObjectEntity> deleteCriteria = criteriaBuilder.createCriteriaDelete(ObjectEntity.class);
Root<ObjectEntity> rootObject= deleteCriteria.from(ObjectEntity.class);
Expression<ObjectEntityPk> expression = rootObject.get("id");
Predicate predicate = expression.in(objects.stream().map(ObjectEntity::getId).collect(Collectors.toList()));
deleteCriteria.where(predicate);
this.entityManager.createQuery(deleteCriteria).executeUpdate();
}
除非我对生成的查询有疑问。表单是正确的,但它没有正确绑定列名。查看我得到的查询。
DELETE FROM OBJECT_TABLE WHERE ((?, ?) IN ((?, ?), (?, ?), (?, ?), (?, ?), ...))
bind => [null, null, 0d287cacc63ff01b5222bf0bb2b2c794, 8105793672, 222a1399065f41b10814d88690c32bcf, 8105793874, 0b0c2f8e73475759872eb97a96f942a3, 8105794177, ...]
我不明白为什么我的列名没有绑定。我是否遗漏了 Entity 或 Pk 上的某些内容?
提前致谢。
PS : 生成的查询在 Oracle 数据库上执行
编辑 -> 使用@Guenther 解决方案解决
问题已通过使用多个谓词构造同一个请求解决。
public void deleteObjects(final List<ObjectsEntity> objects) {
CriteriaDelete<ObjectEntity> deleteCriteria = this.entityManager.getCriteriaBuilder()
.createCriteriaDelete(ObjectEntity.class);
Root<ObjectEntity> rootObject = deleteCriteria.from(ObjectEntity.class);
List<Predicate> predicates = objects.stream().map(n -> generateAndPredicate(n, rootObject))
.collect(Collectors.toList());
Predicate predicate = this.entityManager.getCriteriaBuilder().or(predicates.toArray(new Predicate[predicates.size()]));
deleteCriteria.where(predicate);
this.entityManager.createQuery(deleteCriteria).executeUpdate();
}
private Predicate generateAndPredicate(final ObjectEntity object,
final Root<ObjectEntity> rootObject) {
Predicate id1Equal = this.entityManager.getCriteriaBuilder().equal(rootObject.get("id").get("id1"),
object.getId().getId1());
Predicate id2Equal = this.entityManager.getCriteriaBuilder().equal(rootObject.get("id").get("id2"),
object.getId().getId2());
return this.entityManager.getCriteriaBuilder().and(id1Equal, id2Equal);
}
最终请求看起来像 DELETE FROM OBJECT_TABLE WHERE ((ID1 = someId1 AND ID2 = someId2) OR (ID1 = someOtherId1 AND ID2 = someOtherId2) OR ...)
生成的SQL是错误的。无法绑定列名。这是因为 JPA 不支持 in 语句的多列。可以在此处找到更多详细信息 JPA query for select which multiple values in the "IN" clause
您需要创建一个包含 id.id1 = x and id.id2 = y
个谓词的列表,并将它们 or
在一起。
作为替代方案,您可以使用本机查询,如上面引用的答案中所述。
给定以下实体:
@Entity(name = "Object")
@Table(name = "OBJECT_TABLE")
public class ObjectEntity {
@EmbeddedId
private ObjectEntityPk id;
@Column(name = "SOME_ATTIBUTE")
private String someAttribute;
... Getters/Setters
}
以及以下主键对象:
@Embeddable
public class ObjectEntityPk {
@Column(name = "id1")
private String id1;
@Column(name = "id2)
private Long id2;
... Getters/Setters
}
我有 50 个 ObjectEntity 要删除,我想避免循环执行 50 个删除请求。所以我在我的 dao 中编写了以下代码来生成删除查询,例如 DELETE FROM OBJECT_TABLE WHERE (id1, id2) IN ((someId1, someId2), (someOtherId1, someOtherId2), ... )
public void deleteObjects(final List<ObjectEntity> objects) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaDelete<ObjectEntity> deleteCriteria = criteriaBuilder.createCriteriaDelete(ObjectEntity.class);
Root<ObjectEntity> rootObject= deleteCriteria.from(ObjectEntity.class);
Expression<ObjectEntityPk> expression = rootObject.get("id");
Predicate predicate = expression.in(objects.stream().map(ObjectEntity::getId).collect(Collectors.toList()));
deleteCriteria.where(predicate);
this.entityManager.createQuery(deleteCriteria).executeUpdate();
}
除非我对生成的查询有疑问。表单是正确的,但它没有正确绑定列名。查看我得到的查询。
DELETE FROM OBJECT_TABLE WHERE ((?, ?) IN ((?, ?), (?, ?), (?, ?), (?, ?), ...))
bind => [null, null, 0d287cacc63ff01b5222bf0bb2b2c794, 8105793672, 222a1399065f41b10814d88690c32bcf, 8105793874, 0b0c2f8e73475759872eb97a96f942a3, 8105794177, ...]
我不明白为什么我的列名没有绑定。我是否遗漏了 Entity 或 Pk 上的某些内容?
提前致谢。
PS : 生成的查询在 Oracle 数据库上执行
编辑 -> 使用@Guenther 解决方案解决
问题已通过使用多个谓词构造同一个请求解决。
public void deleteObjects(final List<ObjectsEntity> objects) {
CriteriaDelete<ObjectEntity> deleteCriteria = this.entityManager.getCriteriaBuilder()
.createCriteriaDelete(ObjectEntity.class);
Root<ObjectEntity> rootObject = deleteCriteria.from(ObjectEntity.class);
List<Predicate> predicates = objects.stream().map(n -> generateAndPredicate(n, rootObject))
.collect(Collectors.toList());
Predicate predicate = this.entityManager.getCriteriaBuilder().or(predicates.toArray(new Predicate[predicates.size()]));
deleteCriteria.where(predicate);
this.entityManager.createQuery(deleteCriteria).executeUpdate();
}
private Predicate generateAndPredicate(final ObjectEntity object,
final Root<ObjectEntity> rootObject) {
Predicate id1Equal = this.entityManager.getCriteriaBuilder().equal(rootObject.get("id").get("id1"),
object.getId().getId1());
Predicate id2Equal = this.entityManager.getCriteriaBuilder().equal(rootObject.get("id").get("id2"),
object.getId().getId2());
return this.entityManager.getCriteriaBuilder().and(id1Equal, id2Equal);
}
最终请求看起来像 DELETE FROM OBJECT_TABLE WHERE ((ID1 = someId1 AND ID2 = someId2) OR (ID1 = someOtherId1 AND ID2 = someOtherId2) OR ...)
生成的SQL是错误的。无法绑定列名。这是因为 JPA 不支持 in 语句的多列。可以在此处找到更多详细信息 JPA query for select which multiple values in the "IN" clause
您需要创建一个包含 id.id1 = x and id.id2 = y
个谓词的列表,并将它们 or
在一起。
作为替代方案,您可以使用本机查询,如上面引用的答案中所述。