Найти объект по точному совпадению в коллекции - PullRequest
6 голосов
/ 20 июня 2020

У меня есть такая сущность:

@Getter
@Setter
@Entity
public class Conversation extends AbstractEntity{

    @ElementCollection
    @Column(name = "user_id", nullable = false)
    @CollectionTable(name = "conversation_user", joinColumns = @JoinColumn(name = "conversation_id", nullable = false))
    private List<String> usersIds;
}

Можно ли найти сущность разговора по репозиторию Spring путем точного сопоставления идентификаторов пользователей? Например, у меня есть следующие сущности:

 id | user_ids
------------------------------------------
 1  | user-a, user-b, user-c
 2  | user-a, user-b
 3  | user-a, user-c

Итак, когда мне нужно будет найти беседу по идентификаторам пользователей user-a и user-c, обычное предложение IN, например:

SELECT c FROM Conversation c WHERE c.userIds IN :userIds

найдет разговоры с идентификаторами 1 и 3, но я хочу найти точное совпадение, поэтому мой ожидаемый результат - только разговор 3.

Возможное решение - использовать обычное предложение IN в репозитории и следующую коллекцию фильтров на уровне обслуживания, но я предпочитаю решение, которое возвращает требуемый объект непосредственно из базы данных. Возможно ли это в JPQL или хотя бы родном sql? Спасибо.

Ответы [ 3 ]

2 голосов
/ 30 июня 2020

Используйте HAVING с CASE для подсчета совпадений userId и проверки совпадения с найденным userIds count.

@Query(value = "SELECT c FROM Conversation c LEFT JOIN c.usersIds cu GROUP BY c "
           + "HAVING SUM(CASE WHEN cu IN (:userIds) THEN 1 ELSE -1 END) = :userIdsCount")
List<Conversation> findByUserIds(@Param("userIds") List<String> userIds,
                                 @Param("userIdsCount") Integer userIdsCount);
0 голосов
/ 30 июня 2020

Вы можете написать собственный запрос в своем классе Repository, как указано ниже:

@Repository
public interface YourRepository extends JpaRepository<ConversationModel, Integer> {

 @Query(nativeQuery = true, value = "select c from Converation c WHERE c.userId = :userId ORDER BY userId DESC LIMIT 1")
    Optional<ConversationModel> findByUserId(@Param("userId") String userId);

}

Теперь точное соответствие userId будет возвращено из уровня базы данных. Сделайте это необязательным и проверьте, является ли возвращаемое значение not null при использовании на уровне сервиса.

Для получения нескольких записей вам нужно будет использовать IN в Выполните запрос, как указано ниже

 @Query("select c from Converation c WHERE c.userIds IN :userIds")
    List<ConversationModel> findByUserIds(@Param("userIds") List<String> userIds);

Надеюсь, это решит вашу проблему.

0 голосов
/ 30 июня 2020

Убедитесь, что идентификаторы пользователей в столбце user_ids расположены в алфавитном порядке. Так, например, когда пользователь b входит в диалог с идентификатором 3, столбец user_ids, который был 'user-a, user- c', становится 'user-a, user-b, user- c'.

Затем убедитесь, что, когда вы хотите получить преобразование на основе точного совпадения участников, идентификаторы пользователей в аргументе вашего запроса также упорядочены в алфавитном порядке. Тогда выбор может быть

select c from Converation c WHERE c.userIds = :userIds

Теперь будут найдены только точные совпадения.

...