如何编写一个 JPQL 查询来查找此连接中未找到的记录?

Posted

技术标签:

【中文标题】如何编写一个 JPQL 查询来查找此连接中未找到的记录?【英文标题】:How to do a write a JPQL query to find records not found in this join? 【发布时间】:2020-05-11 14:39:54 【问题描述】:

在我的一生中,我不知道如何构造这个 JPA 查询。

我需要找到 在给定的 SyncSendingConfig 下没有传输的事务日志,按 ID 排序。

在 SO 上对其进行研究,我认为应该可以在 SQL 中进行外连接,其中一侧的 ID 为空,如下图所示:

这是我必须使用的实体。

@Entity
public class SyncSendingConfig 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "sendingConfig")
    private Set<SyncJob> sendJobs = new HashSet<>();


@Entity
public class SyncJob 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "sending_config_id")
    private SyncSendingConfig sendingConfig;

    @ManyToMany(cascade =  CascadeType.ALL )
    @JoinTable(
        name = "SyncJob_TransactionLog", 
        joinColumns =  @JoinColumn(name = "sync_job_id") , 
        inverseJoinColumns =  @JoinColumn(name = "transaction_log_id") 
    )
    private Set<TransactionLog> transmitted = new HashSet<>();


@Entity
public class TransactionLog 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;

    @ManyToMany(mappedBy = "transmitted")
    private Set<SyncJob> syncJobs = new HashSet<>();

还有我正在尝试编写的 DAO:

public interface SyncSendingConfigDao extends JpaRepository<SyncSendingConfig, Long> 

    // TODO: This is the query I'm trying to get to work
    /** Returns those transactions that were never sent for the given SyncSenderConfig, ordered by ID */
    @Query("SELECT tl FROM SyncJob sj "+
        "JOIN SyncSendingConfig ssc ON sj.sendingConfig = ssc.id AND ssc.id= :sendingConfigId "+
        "RIGHT JOIN TransactionLog tl on tl.syncJobs = sj "+
        "WHERE sj.id is null"
    )
    Stream<TransactionLog> findTransactionsNotSentForSyncSendingConfigId(@Param("sendingConfigId") long sendingConfigId);

    // If this part is relevant, this join shows how I can get only those SyncJobs which are related to the SyncSendingConfig of interest
    @Query("SELECT sj FROM SyncJob sj JOIN SyncSendingConfig ssc ON sj.sendingConfig = ssc.id WHERE ssc.id= :sendingConfigId ")
    @QueryHints(value = @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE, value = "500"))
    Stream<SyncJob> findJobs(@Param("sendingConfigId") long sendingConfigId);


上面关于 DAO 的查询显示了我正在尝试做的事情。我真的不确定如何将 SQL 转换为 JPQL ......尤其是在连接条件和顺序上。

更新:

这是我要翻译的确切 SQL 查询。它匹配上面类中由hibernate定义的所有关系。

select tl.* 
from sync_job sj 
  join sync_sending_config ssc 
    on ssc.id = sj.sending_config_id and ssc.id=2 
  join sync_job_transaction_log sjtl 
    on sjtl.sync_job_id = sj.id 
  RIGHT JOIN transaction_log tl 
    on tl.id = sjtl.transaction_log_id 
  where sjtl.sync_job_id is null

当直接运行此查询时,它会返回正在寻找的确切结果。

如果有人能提供帮助,我将不胜感激。我一直在碰壁,试图弄清楚 JPQL 语法。

谢谢

更新 2

使用“@SB”后,JPQL 似乎不支持右连接。由于没有找到如何在 JPQL 中使用左连接(如果可能的话)来编写它,我使用了原生查询:

@Query(value = "select tl.* from sync_job sj "+
                "join sync_sending_config ssc on ssc.id = sj.sending_config_id and ssc.id = :sendingConfigId "+
                "join sync_job_transaction_log sjtl on sjtl.sync_job_id = sj.id "+
                "RIGHT JOIN transaction_log tl on tl.id = sjtl.transaction_log_id "+
                "where sjtl.sync_job_id is null", 
                nativeQuery = true)
@QueryHints(value = @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE, value = "500"))
Stream<TransactionLog> findTransactionsNotSentForSyncSendingConfigId(@Param("sendingConfigId") long sendingConfigId);

【问题讨论】:

连接列的另一边是什么? T1 left join config on config.transmitted = ? 我的想法是TransactionLog TL可以根据Join Table“SyncJob_TransactionLog”中的TL.id和SJ.id与SyncJob“SJ”进行join。然后限制结果,其中唯一考虑的 SJ 是 SJ.id = SyncSendingConfig.id 其中 SyncSendConfig = id 参数。然后限制 SJ.id 为空的结果。 试试吧!让我们看看结果。基本上,两个表之间的连接应该至少在每一侧的一个相关列之间 我一直在尝试对查询进行不同的黑客攻击以使其正常工作...我将使用堆栈跟踪更新问题。但是我什至不确定加入表格的顺序,或者语法应该是什么样子,对于 JPQL 来说是新手。 只是基于编辑的想法,如果您只能获得与感兴趣的 SyncSendingConfig 相关的 SyncJobs 的列表,那么您可以从 TransactionLog 的 syncJobs 中排除这些?类似的东西 - SELECT tl from TransactionLog tl where t1.syncJobs not in(上面选择了syncJobs) 【参考方案1】:

假设以下虚拟数据设置:

    事务日志 ID:1、2、3、4 SyncSendingConfig ID:1、2 同步作业: ID 1,SyncSendingConfigID 1 ID 2,SyncSendingConfigID 1 ID 3,SyncSendingConfigID 2 ID 4,SyncSendingConfigID 2 sync_job_transaction_log SyncJobId 1,TransactionLogId 1 SyncJobId 1,TransactionLogId 2 SyncJobId 2,TransactionLogId 1 SyncJobId 2,TransactionLogId 2

TransactionLogs 12 根据 sync_job_transaction_log 表中的映射在 SyncSendingConfig ID 1 下传输。 因此,在 SyncSendingConfig ID 1 下传输的 not 的 TransactionLogs 将是 34。 所以,为了找到在给定的SyncSendingConfig下没有传输的TransactionLogs,对应的JPQL是-

@Query("select t from TransactionLog t where t not in (" +
        "select t1 from TransactionLog t1 join t1.syncJobs tsj where tsj in "
        + "(select sj from SyncJob sj where sj.sendingConfig.id = :sendingConfigId)"
        + ")")

将 JPQL 视为应用于 Java 对象的 SQL,其中实体表示表,它们的属性表示列,而 has-a 关系表示映射关系。 现在,当您要连接两个表时,只需参考相应的实体,只要正确指定连接列,SQL 查询就会在连接表和列上正确形成。 示例 SQL -

select column(s) from table1 <type of> join table2 on table1.column1 = table2.column1 where <filter conditions here>

相应的 JPQL 设置 -

Entity1 (corresponds to table1) -> 
    property1 (corresponds to column)
    property2 (corresponds to mapping relationship, has @JoinColumn details)

上述设置的 JPQL -

select property1 from entity1 <type of> join entity1.property2 where <filter condition here>

在 cmets 讨论后更新 - 由于无法在当前设置中进行右连接,因此建议根据性能参数评估 JPQL,或者将工作 SQL 用作 nativeQuery。

@Query(value = "select tl.* from sync_job sj "+
                "join sync_sending_config ssc on ssc.id = sj.sending_config_id "+
                "join sync_job_transaction_log sjtl on sjtl.sync_job_id = sj.id "+
                "RIGHT JOIN transaction_log tl on tl.id = sjtl.transaction_log_id "+
                "where sjtl.sync_job_id is null and ssc.id = :sendingConfigId", 
                nativeQuery = true)

【讨论】:

我真的很想学习执行此连接的 JPQL 语法。 当然,但这是否满足您的需求? 我用 JPQL 构造语法细节更新了答案。 我更新了帖子以包含我尝试编写的与数据一起使用的确切 SQL 查询。此查询在我直接在 sql 中运行时有效,与帖子中的实体类匹配 当我按照我的答案中的数据设置运行它时,您的 SQL 和我的 JPQL 返回了完全相同的结果。我的 JPQL 在没有正确连接的情况下工作,因为它首先获取为给定 SyncSendingConfig ID 传输的 TransactionLogs,然后从可用的 TransactionLogs 中删除所有此类传输的 TransactionLogs。

以上是关于如何编写一个 JPQL 查询来查找此连接中未找到的记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JPQL 中查找第一个

Hibernate:如何通过连接字符串来创建 JPQL?

如何使用 JPQL 创建动态更新查询?

JPQL在多对多关系上左外连接

JPQL 基于条件的多对多连接

使用 JPQL 通过连接表进行查询时出错