jpa Критерии построения карт присоединения - PullRequest
5 голосов
/ 07 апреля 2011

У меня следующий запрос построителя критериев

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Object> critQuery = cb.createQuery();

    Root<Role> role = critQuery.from(Role.class);

    //create a join between role and permission
    MapJoin<Role,String,Permission> perm = role.joinMap("permissions");
    critQuery.multiselect(role.get("label"), perm.key(), perm.value());

    //this line throws NPE
    Query query = em.createQuery(critQuery);

Последняя строка выдает исключение нулевого указателя.

java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.prepareAlias(AbstractPathImpl.java:246)
at org.hibernate.ejb.criteria.path.AbstractPathImpl.render(AbstractPathImpl.java:253)
at org.hibernate.ejb.criteria.path.AbstractPathImpl.renderProjection(AbstractPathImpl.java:261)

Ответы [ 2 ]

9 голосов
/ 08 апреля 2011

У меня точно такая же проблема.После нескольких часов работы над этой проблемой, после отладки исходного кода 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());

В hibernate он генерирует следующий 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

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

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

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

2 голосов
/ 16 сентября 2013

Для тех, кто застрял в Hibernate 3.6, я смог присоединиться к карте и ограничить результаты предикатом. Это решение сопровождается существенными оговорками. Этот метод работает, только если интересующий вас ключ карты существует для каждой записи. Если этого не произойдет, вы потеряете записи, которые вы могли бы ожидать в наборе результатов.

Мой случай очень похож на пример Эдвина. У нас есть объект Person с картой текущих имен, где ключ карты - это тип имени, а значение - это объект Name. В моем случае я хотел получить текущее стандартное имя человека.

Root<Person> person = query.from(Person.class);
Join currentName = person.join("currentNames");

NameLookup standardName = NameLookup.lookup("ST");

Predicate useridMatches = criteria.equal(person.get("userid"), "user1");
Predicate isStandardName = criteria.equal(currentName.get("nameType"), standardName);
Predicate useridAndStandardName = criteria.and(useridMatches, isStandardName);
query.where(useridAndStandardName);

В JPQL мы обычно используем предложение WITH для ограничения объединения.

inner join person.currentNames currentStandardName 
with currentStandardName.nameType.id = :standardNameLookupId
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...