JPQL В предложении с @Convert

#postgresql #hibernate #spring-data-jpa #jpql

#postgresql #переход в спящий режим #spring-data-jpa #jpql

Вопрос:

В ненормализованной базе данных PostgreSQL: две таблицы «Документ» и «Запись»

Документ содержит идентификатор и столбец recodeIds, разделенный запятыми

 --------------------------------------
| id (VARCHAR) | recordIds (VARCHAR) |
--------------------------------------
| 1            | 3,1                 |
| 2            | 2                   |
--------------------------------------
  

Запись имеет идентификатор и имя

 ---------------------------------
| id (VARCHAR) | name (VARCHAR) |
---------------------------------
| 1            | X              |
| 2            | Y              |
| 3            | Z              |
---------------------------------
  

DocumentModel с @Convert

 @Entity
@Table(name = "Document")
public class DocumentModel {
    @Id
    @Column(name = "id")
    private String id;

    @Column(name = "recodeIds")
    @Convert(converter = RecordsConverter.class)
    private Set<String> records;
}
  

RecordsConverter

 public class RecordsConverter implements AttributeConverter<Set<String>, String> {
    @Override
    public String convertToDatabaseColumn(Set<String> recordTypes) {
        return String.join(",", recordTypes);
    }
    @Override
    public Set<String> convertToEntityAttribute(String recordTypes) {
        return Arrays.stream(recordTypes.split(",")).collect(Collectors.toSet());
    }
}
  

JPQL Теперь я пытаюсь использовать записи в @Query

 @Query("SELECT D.id, R.name)"
      " FROM DocumentModel D"
      " JOIN RecordModel R"
      " ON R.id IN D.records"
      " WHERE D.id = :docId"
  

Это работает только в том случае, если идентификаторы записей имеют одно значение (пример: docId = 2)

Это не работает, если идентификаторы записей имеют несколько значений (пример: docId = 1), даже если:

  • Вложенный ВЫБОР не работает
  • В предложении с и без () не работает
  • ЧЛЕН предложения не работает
  • Полученный собственный SQL напрямую не работает
  • @Формула не работает

И, похоже, проблема в том, что записи должны заключаться в одинарные кавычки, разделенные запятыми, а не только разделяться запятыми, поскольку они являются строками. (В этом случае работает собственный SQL-запрос)

Вопрос: Как заставить это работать, используя JPQL (не собственный запрос) без изменения RecordsConverter и, надеюсь, без спецификаций JPA?

Комментарии:

1. Лучший способ решить эту проблему — нормализовать вашу модель данных. Хранение значений, разделенных запятыми, в одном столбце с самого начала является огромной ошибкой

2. Вы правы, но клиент отказался :/

3. @AhmedGhoneim Работает ли это в sql-запросе, аналогичном вашему JPQL?

4. Нет. Он работает только со значениями, заключенными в одинарные кавычки, разделенные запятыми

5. Вы могли бы создать представление, представляющее нормализованную версию document таблицы. Затем вы используете обычное объединение, чтобы объединить его с Record таблицей

Ответ №1:

Похоже, на меня повлияла ошибка гибернации

В качестве обходного пути используйте обертки коллекции:

 @Data
@Builder
public class MyCollection implements Serializable {
    private final Set<String> collection;
}
  

и измените свой конвертер атрибутов:

 public class MyConverter implements AttributeConverter<MyCollection, String> {
    @Override
    public String convertToDatabaseColumn(MyCollection myCollection) {
        return String.join(",", myCollection.getCollection());
    }
    @Override
    public MyCollection convertToEntityAttribute(String myCollection) {
        return MyCollection.builder()
                .collection(Arrays.stream(myCollection.split(",")).collect(Collectors.toSet()))
                .build();
    }
}
  

и вы моделируете:

 @Entity
@Table(name = "MY_MODEL")
public class MyModel {
    @Column(name = "MY_COLLECTION")
    @Convert(converter = MyConverter.class)
    private MyCollection myCollection;
}
  

Это сработало для меня.