#hibernate #hashmap #filtering #jpa-2.0 #h2
#переход в спящий режим #хэш-карта #фильтрация #jpa-2.0 #h2
Вопрос:
Я использую 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() доступны в документе здесь
Я пропускаю какую-то конфигурацию? Есть идеи?
Заранее спасибо
Ответ №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());
Я подумал, что если Criterions 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 для отслеживания проблем
https://hibernate.atlassian.net/browse/HHH-6103
И разместили вопрос на форуме пользователей Hibernate
https://forum.hibernate.org/viewtopic.php?f=1amp;t=1010411
В конце мне пришлось решить проблему, используя коллекцию встраиваемых объектов, содержащих те же записи, что и карта. Я был вынужден реализовать equals и hashCode во встраиваемом, чтобы удовлетворить тому же поведению.
Комментарии:
1. Спасибо за ваш ответ. Это работает в EclipseLink. Довольно странно, что они не замечают эту серьезную ошибку в своем трекере проблем, особенно когда указано, что она работает в их собственном документе. Поскольку JPA2, похоже, не является приоритетом спящего режима, я предпочитаю изменить.
2. @Manu Я использую Hibernate последние 6 месяцев, это всего лишь одна из проблем, с которыми я столкнулся. Попробуйте наследование одной таблицы, если хотите настоящей головной боли. Похоже, они больше заботятся о Hibernate, чем о поддержке JPA. В этом случае вам лучше использовать эталонную реализацию: EclipseLink. Их обязанность — поддерживать стандарт.
3. Я только что получил электронное письмо от системы отслеживания проблем гибернации, в котором говорится, что это было исправлено в Hiberante 4.1.6.
4. Только один вопрос — как запись в emp_phone ссылается на таблицу employee? Разве это не должно быть — @CollectionTable(name=»emp_phone» , JoinColumn=@JoinColumn(name=»Employee_id»))?