JPQL更新父实体时,能否基于子实体非ID属性筛选?
问题:JPQL更新父实体时无法正确关联子实体非ID属性
需求概述
需要通过JPQL更新Record实体的blockedToConvert属性为true,筛选条件为:关联的ClientesKanBanEntidade实体的documento属性匹配参数:documento,且r.moment < :time。
现有代码
JPQL更新方法
@Transactional @Modifying @Query("UPDATE Record r " + "SET r.blockedToConvert = true " + "WHERE r.cliente.documento = :documento " + "AND r.moment < :time") void blockToConversionBeforeDateTime(String documento, LocalDateTime time);
Record实体与子实体的关联配置
@ManyToOne @EqualsAndHashCode.Exclude @JsonBackReference @JoinColumn(name="cliente_kanban_id") private ClientesKanBanEntidade cliente;
ClientesKanBanEntidade实体包含documento属性,对应数据库表同名字段。
问题现象
当前JPQL生成的SQL错误地将documento当作Record对应表(records__users_made_contacts)的字段:
UPDATE records__users_made_contacts SET blocked_to_conversion=1 WHERE documento=? and moment<?
期望生成的SQL应关联子表clientes_kanban:
UPDATE records__users_made_contacts r JOIN clientes_kanban c SET r.blocked_to_conversion=1 WHERE c.documento=? and r.moment<?
解决方案与说明
可以在JPQL中基于子实体的非ID属性更新父实体,但需要调整JPQL写法——标准JPQL的UPDATE语句不支持直接使用JOIN语法,之前的写法会被ORM误解为引用父表字段,改用子查询即可实现正确关联。
修正后的JPQL写法(两种可选)
写法一:使用EXISTS子查询
@Transactional @Modifying @Query("UPDATE Record r " + "SET r.blockedToConvert = true " + "WHERE EXISTS (" + " SELECT 1 FROM ClientesKanBanEntidade c " + " WHERE c.id = r.cliente.id " + " AND c.documento = :documento" + ") " + "AND r.moment < :time") void blockToConversionBeforeDateTime(String documento, LocalDateTime time);
写法二:使用IN子查询
@Transactional @Modifying @Query("UPDATE Record r " + "SET r.blockedToConvert = true " + "WHERE r.cliente IN (" + " SELECT c FROM ClientesKanBanEntidade c " + " WHERE c.documento = :documento" + ") " + "AND r.moment < :time") void blockToConversionBeforeDateTime(String documento, LocalDateTime time);
原理说明
这两种写法都会让ORM正确生成关联子表的SQL:通过子查询先筛选出符合documento条件的ClientesKanBanEntidade实体,再关联到对应的Record实体进行更新,最终效果和你期望的JOIN写法一致。
内容的提问来源于stack exchange,提问作者Lucas Amaral
相关产品推荐
相关产品推荐

