HIBERNATE — JPA2 — H2 — Запрос @ElementCollections HashMap по ключу

Я использую hibernate-entitymanager 3.6.4.Final и базу данных h2 1.3.155.

Я использую H2Dialect.

У меня возникают проблемы с фильтрацией записей по элементам в коллекции @ElementCollection. Вот моя сущность

@Entity
public class Item {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @MapKeyColumn(name="name", length=50)
  @Column(name="value", length=100)
  protected Map<String, String> attributes;
  /*  etc.  */
}

Вот мой запрос:

Item item = em.createQuery("FROM Item i JOIN i.attributes a WHERE KEY(a)='myAttrName'").getSingleResult();

Вот сообщение об ошибке:

4971 [main] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 90022, SQLState: 90022
4971 [main] ERROR org.hibernate.util.JDBCExceptionReporter - Function "KEY" not found; SQL statement:
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute query

Это довольно странно, поскольку функции KEY() и VALUE() доступны в документе здесь

Я пропустил какую-то конфигурацию? Любая идея ?

заранее спасибо


person Manu    schedule 05.06.2011    source источник


Ответы (1)


У меня точно такая же проблема. После нескольких часов работы над проблемой, после отладки исходного кода Hibernate и повторной проверки примеров в книгах и спецификации JPA 2.0 я решил попробовать в EclipseLink.

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

@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name="emp_phone")
@MapKeyColumn(name="phone_type")
@Column(name="phone_num")
private Map<String, String> phoneNumbers;

Я мог убедиться, что это отлично работает с EclipseLink 2.1 и OpenJPA 2.1.0, но не работает в Hibernate 3.5.3, 3.6.1., 3.6.3.

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> criteria = builder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteria.from(Employee.class);
criteria.select(employeeRoot);
MapJoin<Employee, String, String> phoneRoot = employeeRoot.joinMap("phoneNumbers");

criteria.where(builder.equal(phoneRoot.key(), "HOME"));

System.out.println(entityManager.createQuery(criteria).getResultList());

Я подумал, что если Criteria API не сработает, возможно, я смогу сделать это с помощью именованного запроса. Интересно, что Hibernate не поддерживает ключевые слова KEY, VALUE или ENTRY, поэтому запросы оказались некорректными.

http://opensource.atlassian.com/projects/hibernate/browse/HHH- 5396

Вот что запускается:

String query = "SELECT e FROM Employee e JOIN e.phoneNumbers p WHERE KEY(p) IN ('HOME')";
System.out.println(entityManager.createQuery(query, Employee.class).getResultList());

В спящем режиме он генерирует следующий SQL-запрос:

   select
        employee0_.id as id0_,
        employee0_.name as name0_ 
    from
        Employee employee0_ 
    inner join
        emp_phone phonenumbe1_ 
            on employee0_.id=phonenumbe1_.Employee_id 
    where
        KEY(phonenumbe1_.phone_num) in (
            'HOME'
        )

Который явно деформирован.

Опять же, в EclipseLink и OpenJPA это работает.

Итак, очевидно, что-то не так с Hibernate. Я отправил сообщение об ошибке в Hibernate Jira Issue Tracker

https://hibernate.atlassian.net/browse/HHH-6103

И разместил вопрос на форуме пользователей Hibernate.

https://forum.hibernate.org/viewtopic.php?f=1&t=1010411

В конце концов мне пришлось решить проблему, используя набор встраиваемых объектов, содержащих те же записи, что и карта. Я был вынужден реализовать equals и hasHCode во встраиваемом коде, чтобы обеспечить такое же поведение.

person Edwin Dalorzo    schedule 06.06.2011
comment
Спасибо за Ваш ответ. Он работает в EclipseLink. Довольно странно, что они не замечают эту серьезную ошибку в своем трекере проблем, особенно когда указано, что она работает в их собственном документе. Поскольку JPA2, похоже, не является приоритетом гибернации, я предпочитаю изменения. - person Manu; 06.06.2011
comment
@Manu Я использую Hibernate последние 6 месяцев, это лишь одна из проблем, с которыми я столкнулся. Попробуйте однотабличное наследование, если хотите получить настоящую головную боль. Кажется, они заботятся о Hibernate больше, чем о поддержке JPA. В этом случае вам лучше использовать эталонную реализацию: EclipseLink. Их обязанность — поддерживать стандарт. - person Edwin Dalorzo; 06.06.2011
comment
Я только что получил электронное письмо от системы отслеживания проблем Hibernate, в котором говорится, что это было исправлено в Hiberante 4.1.6. - person Edwin Dalorzo; 08.08.2012
comment
Только один вопрос - как запись в emp_phone связана с таблицей сотрудников? Разве это не должно быть - @CollectionTable(name=emp_phone , joinColumn=@JoinColumn(name=Employee_id)) ? - person Nikhil; 21.10.2015