Как сделать запрос JPQL, чтобы найти записи, не найденные в этом объединении? - PullRequest
4 голосов
/ 25 января 2020

В жизни я не могу понять, как построить этот запрос JPA.

Мне нужно найти TransactionLogs, которые не были переданы в данном SyncSendingConfig , упорядочены по идентификатору.

Исследуя его на SO, я полагаю, что в SQL должно быть возможно выполнить внешнее соединение, где идентификаторы равны нулю для одной стороны, как на этой диаграмме:

enter image description here

Вот сущности, с которыми мне приходится работать.

@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

После работы с @S B ', похоже, что 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);

1 Ответ

0 голосов
/ 28 января 2020

При условии установки фиктивных данных ниже:

  1. Идентификаторы журнала транзакций: 1, 2, 3, 4
  2. Идентификаторы SyncSendingConfig: 1, 2
  3. Syn c Задания:
    • ID 1, SyncSendingConfigID 1
    • ID 2, SyncSendingConfigID 1
    • ID 3, SyncSendingConfigID 2
    • ID 4, SyncSendingConfigID 2
  4. sync_job_transaction_log
    • SyncJobId 1, TransactionLogId 1
    • SyncJobId 1, TransactionLogId 2
    • SyncJobId 2, TransactionLogId 1 *

TransactionLogs 1 и 2 передаются под идентификатором SyncSendingConfig 1 в соответствии с отображением в таблице sync_job_transaction_log. Следовательно, TransactionLogs , а не , передаваемый под идентификатором SyncSendingConfig 1, будет 3 и 4. Таким образом, чтобы найти TransactionLogs, которые не были переданы в данном SyncSendingConfig , соответствующий 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 как SQL, примененный к Java объектам с сущностями представляющие таблицы, их свойства, представляющие столбцы, и отношение 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>

Обновление после обсуждения в комментариях - Так как право присоединиться в текущей настройке невозможно, предложите оценить 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)
...