如何编写一个 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 查询来查找此连接中未找到的记录?的主要内容,如果未能解决你的问题,请参考以下文章