Использовать критерии гибернации для фильтрации ключей и значений в Map

У меня есть следующий сохраненный класс:

public class Code {

  @ElementCollection(targetClass = CodeValue.class)
  @MapKeyClass(CodeProperty.class)
  @JoinTable(name="code_properties")
  @CreateIfNull( value = false )
  private Map<CodeProperty,CodeValue> propertiesMap =
      new HashMap<CodeProperty, CodeValue>();

  ...
}

public class CodeProperty {
    private String name;
    ...
}

public class CodeValue {
    private String value;
    ...
}

И я пытаюсь получить список кода, отфильтрованный некоторыми свойствами, которые у меня есть в propertiesMap (например, коды, где свойство с именем "color" имеет значение "зеленый".

Я использую следующие базовые критерии:

Criteria criteria = currentSession()
    .createCriteria(Code.class, "code")
    .setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);

Когда я пытаюсь выполнить фильтр коллекции (как предложено здесь):

criteria.createAlias("code.properties", "p");
criteria.add(Restrictions.eq("p.foo", "test1"));
criteria.setFetchMode("code.properties", FetchMode.JOIN);
criteria.list();

Я получаю следующую ошибку:

org.hibernate.QueryException: could not resolve property: foo of: com.example.CodeValue

Это означает, что я действительно не понимаю, почему, hibernate считает, что code.properties является CodeValue вместо карты!!!

Я также попытался получить доступ к этому полю, не создавая псевдоним, и таким образом hibernate, похоже, обращается к правильному классу Code. Я использовал properties.indeces (как предложено здесь):

criteria.add(Restrictions.eq("properties.indeces", "foo"));
criteria.list();

Но с этим я получаю следующую ошибку:

could not resolve property: properties.indeces of: com.example.Code

Может кто-нибудь помочь мне понять, что случилось? Каким будет правильный запрос критериев, чтобы найти коды с зеленым цветом?

Вы можете проверить проект Github, демонстрирующий эту проблему.

Спасибо

Ответ 1

Я не думаю, что вы можете сделать это с помощью устаревшего API-интерфейсов Hibernate и существует небольшое ограничение с внедрением Hibernate с использованием API-критериев JPA. Один из способов решить эту проблему:

Получите List<CodeProperty>, запросив этот объект непосредственно с вашими требованиями к предикату.

List<CodeProperty> propertyKeys = entityManager
  .createQuery( "FROM CodeProperty p WHERE p.name = :name" )
  .setParameter( "name", "foo" )
  .getResultList();

Запросите свой объект Code, используя критерии JPA.

// setup the query
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Code> query = cb.createQuery( Code.class );
Root<Code> root = query.from( Code.class );
MapJoin<Code, CodeProperty, CodeValue> mapRoot = root.joinMap( "propertiesMap" );
query.select( root ).where( mapRoot.key().in( propertyKeys ) );

// get results
List<Code> results = entityManager.createQuery( query ).getResultList();

Я попытался объединить их, используя:

query
  .select( root )
  .where ( cb.eq( mapRoot.key().get( "name" ), "foo" ) )

Проблема заключается в том, что это приводит к исключению NotYetImplemented: (.

Ответ 3

Пожалуйста, подтвердите, используете ли вы индексы или индексы. Правильное использование выглядит следующим образом:

criteria.createAlias("code.properties", "p");
criteria.add(Restrictions.eq("p.indices", "foo"));
criteria.list();

В качестве альтернативы вы можете использовать

eq("p." + CollectionPropertyNames.COLLECTION_INDICES)  

в Ограничении.

Пожалуйста, дайте мне знать, если он все еще не работает для вас.

Ответ 4

Существуют различные проблемы с вашим подходом: Просмотр папки MapTest в вашем проекте github Я могу посоветовать вам всегда вызывать имена переменных как имена столбцов и использовать те же имена в критериях. Критерии - это карты, первым аргументом которых является имя атрибута, а второй аргумент - значение атрибута. Для многих-многих отношений вы можете попробовать:

  @JoinTable(
      name="docente_classe",
      joinColumns=
          @JoinColumn(name="id_dipendente", referencedColumnName="id_dipendente"),
      inverseJoinColumns=
          @JoinColumn(name="id_classe", referencedColumnName="id_classe")
  )

Это аннотация JPA, я не знаю, компилируется ли этот проект.

https://github.com/tredue1/School_Manager/blob/master/code/SM/src/main/java/agc2/it/sm/gestioneDocente/DocenteJpa.java

Для запроса по многим отношениям смотрите этот пост:

jpa критерии для многих и многих отношений.