Запрос Hibernate сравнивает разделенную запятыми строку со списком - PullRequest
0 голосов
/ 28 января 2020

Проблема

Я разрабатываю приложение с пружинной загрузкой, которое должно считывать данные из базы данных oracle.
Сама база данных находится вне моего контроля, и я не могу изменить ее структуру.
Я использую spring-data-jpa в сочетании с hibernate для взаимодействия с базой данных.
У меня есть следующая сущность:

@Entity
@Table(name = "mytable")
public class MyEntity {
    @Id
    @Column
    private String entityId;

    // This column might have a value like "abc,xyz,a1b" in the db
    @Column
    private String associatedIds;
}

Столбец associatedIds содержит значения, разделенные запятыми ( например. abc,xyz,a1b).
Пользователи моего приложения имеют разрешение только на чтение строк, по крайней мере, 1 associatedId, к которым они имеют доступ. Идентификаторы, к которым они имеют доступ, представлены как List<String> allowedIds.

Чтобы пользователи могли получать только те данные, которые им разрешены, мне нужно ограничить свои запросы. У меня возникают проблемы при создании запроса, отвечающего моим требованиям.
Если бы у пользователей был доступ только к одному идентификатору, я мог бы просто использовать следующий запрос, используя простой оператор LIKE:

@Query(value = "SELECT e FROM MyEntity e WHERE e.associatedIds LIKE '%:allowedId%'")
Page<MyEntity> findAllByAllowedId(@Param("allowedId") String allowedId, Pageable pageable);

То запроса недостаточно для моего варианта использования, поскольку пользователи имеют доступ к нескольким идентификаторам.

Я придумал несколько способов решения этой проблемы, но не смог полностью реализовать ни один из них.

Подход 1: "IN LIKE"

Первым подходом был бы такой запрос:

@Query(value = "SELECT e FROM MyEntity e WHERE e.associatedIds IN (LIKE '%:allowedIds%')")
Page<MyEntity> findAllByAllowedIds(@Param("allowedIds") List<String> allowedIds, Pageable pageable);

Это не работает, потому что, насколько я знаю, там нет запроса, объединяющего IN и LIKE.

Подход 2: «AttributeConverter»

Следующее, что я попробовал, было преобразовать разделенную запятыми строку в List<String>, используя следующий преобразователь:

@Converter
public class StringArrayToStringConverter implements AttributeConverter<List<String>, String> {
    @Override
    public String convertToDatabaseColumn(List<String> strings) {
        return strings == null ? null : StringUtils.join(strings, ',');
    }

    @Override
    public List<String> convertToEntityAttribute(String s) {
        if(StringUtils.isBlank(s)) {
            return Collections.emptyList();
        }
        return Arrays.asList(s.split(","));
    }
}

This преобразование работает, но не позволяет мне строить запросы, как если бы столбец был списком, потому что база данных остается неизменной.

Подход 3: "@Formula"

Другим возможным решением может быть аннотация @Formula в hibernate. Там может быть способ создать виртуальный столбец, который можно искать в соответствии с моими требованиями. Я не имею ни малейшего понятия, с чего начать, поскольку я никогда раньше не использовал эту аннотацию и не совсем понимаю, как типы списков работают в oracle. Еще одна вещь, которую стоит рассмотреть, - можете ли вы на самом деле выполнять hql-запросы к столбцам формулы.

Подход 4: «Несколько OR»

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

SELECT e FROM MyEntity e WHERE e.allowedIds LIKE'%allowedIds.get(0)%' OR e.allowedIds LIKE'%allowedIds.get(1)%' etc..

При таком подходе я также не знаю, как мне это реализовать в spring-data-jpa.

Я был бы очень признателен за некоторые рекомендации о том, как реализовать эти решения или предложения, о которых я не думал.

1 Ответ

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

Как насчет использования функции регулярных выражений regexp_substr:

@Query(value = "SELECT e FROM MyEntity e WHERE e.associatedIds IN ( SELECT regexp_substr(:allowedId,'[^,]+', 1, level) FROM dual connect by regexp_substr(:allowedId, '[^,]+', 1, level) IS NOT NULL \n#pageable\n" ,nativeQuery=true )
Page<MyEntity> findAllByAllowedId(@Param("allowedId") String allowedId, Pageable pageable);
...