Атрибуты статической метамодели JPA / Hibernate не заполнены - PullRequest
22 голосов
/ 04 октября 2010

Я хотел бы использовать JPA2 Criteria API с объектами метамодели, что кажется довольно простым:

...
Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
... albm.get(JPAAlbum_.theme) ... ;

, но этот Root.get всегда выдает NullPointerException.JPAAlbum_.theme был автоматически сгенерирован Hibernate и выглядит как

public static volatile SingularAttribute<JPAAlbum, JPATheme> theme;

, но, очевидно, никогда не заполняется.

Я пропустил шаг в инициализации платформы?

РЕДАКТИРОВАТЬ: вот фрагмент того, как я использую JPA и метамодель, когда она падает:

    CriteriaBuilder cb = em.getCriteriaBuilder();

    CriteriaQuery<JPAAlbum> cq = cb.createQuery(JPAAlbum.class) ;
    Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
    cq.where(cb.equal(albm.get(JPAAlbum_.theme).get(JPATheme_.id),
                        session.getTheme().getId())) ;

(JPAAlbum_ - это класс, поэтому я просто import раньше) исвязанная трассировка стека:

Caused by: java.lang.NullPointerException
    at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
    at net.wazari.dao.jpa.WebAlbumsDAOBean.getRestrictionToAlbumsAllowed(WebAlbumsDAOBean.java:55)

РЕДАКТИРОВАТЬ 2:

В руководстве JBoss EntityManager я вижу, что

при переходе в спящий режимEntityManagerFactory создается, он будет искать канонический класс метамодели для каждого из управляемых типизированных, о которых он знает, и, если он найдет их, он вставит в них соответствующую информацию метамодели, как описано в [Спецификация JPA 2, раздел 6.2.2,pg 200]

Я также могу проверить с помощью

     for (ManagedType o : em.getMetamodel().getManagedTypes()) {
            log.warn("___") ;
            for (Object p : o.getAttributes()) {
                log.warn(((Attribute)p).getName()) ;
            }
        }

, что Hibernate знает о моей метамодели, имена атрибутов написаны, однако

   log.warn("_+_"+JPAPhoto_.id+"_+_") ;

остается отчаянно пустым ...

EDIT3 : вот сущность JPAAlbum и ее класс метамодели .

Что еще можно сказать о моей конфигурации ...

  • Я использую Hibernat 3.5.6-Final (согласно META-INF / MANIFEST.MF),

  • развернуть на Glassfish 3.0.1

  • из Netbeans 6.9.1 ;

  • и приложение использует EJB 3.1 ,

Надеюсь, это поможет!

EDIT 4:

к сожалению, тест JUnit приводит к тому же исключению:

java.lang.NullPointerException
    at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
    at net.wazari.dao.test.TestMetaModel.foo(TestMetaModel.java:55)

Доступен гораздо более простой проект здесь / tarball . Он содержит только мои сущности и их метамодель, плюс тест JUnit (foo падает с метамоделью, bar в порядке с обычным Query.

EDIT5:

Вы сможете воспроизвести проблему, загрузив tarball , собрав проект:

ant compile
or
ant dist

и запустив тест JUnit net.wazari.dao.test.TestMetaModel

 CLASSPATH=`sh runTest.sh` java org.junit.runner.JUnitCore  net.wazari.dao.test.TestMetaModel

(отредактируйте runTest.sh, чтобы указать CLASSPATH на правильное расположение вашего JUnit4-5 jar)

Все используемые мной спящие зависимости должны быть включены в архив.

Ответы [ 8 ]

46 голосов
/ 04 февраля 2011

У меня была та же проблема, и она была устранена путем помещения классов Model и Model_ в один и тот же пакет.

10 голосов
/ 10 ноября 2011

У меня было приложение Java EE 6, использующее EclipseLink на GlassFish с несколькими классами @StaticMetamodel, и все работало нормально.Когда я переключился на Hibernate 4 на JBoss 7, я тоже начал получать эти NPE.Я начал исследовать и нашел эту страницу:

http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/metamodel.html

Она цитирует спецификацию JPA 2, раздел 6.2.1.1, которая определяет, как должны создаваться статические классы метамодели.Например, я узнал, прочитав спецификацию, что «в будущем выпуске этой спецификации будет предоставлен выбор различных пакетов».У меня были классы метамодели в разных пакетах, и они хорошо работали на EclipseLink, но это дополнительная функция, поскольку текущий стандарт указывает следующее:

  • Классы метамодели должны находиться в том же пакете, что и классы сущностейони описывают;
  • Они должны иметь то же имя, что и классы сущностей, которые они описывают, после чего следует подчеркивание (например, Product - это сущность, Product_ - это класс метамодели);
  • Если сущность наследуетот другого объекта или от сопоставленного суперкласса, его класс метамодели должен наследоваться от класса метамодели, который описывает его непосредственный суперкласс (например, если SpecialProduct расширяет Product, который расширяет PersistentObject, то SpecialProduct_ должен расширять Product_, который должен расширять PersistentObject _).

После того, как я выполнил все правила в спецификации (приведенное выше является лишь кратким описанием, см. Раздел 6.2.1.1 спецификации для полной версии), я перестал получать исключения.

кстати, вы можете сделатьЗагрузите спецификацию здесь: http://jcp.org/en/jsr/detail?id=317 (нажмите «Страница загрузки» для окончательного выпуска, выберите загрузку спецификации для оценки, примите соглашение и загрузите файл «Спецификация SR-000317 2.0» - persistence-2_0-окончательный spec.pdf).

8 голосов
/ 07 ноября 2010

Я не могу воспроизвести проблему.Я использовал некоторые из ваших сущностей (упрощенные версии JPAAlbum, JPATheme и JPATagTheme, без каких-либо интерфейсов), сгенерировал классы метамодели и просто прошел следующий элементарный тестовый метод (работающий внутри транзакции):

@Test
public void foo() {
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<JPAAlbum> query = builder.createQuery(JPAAlbum.class);

    Root<JPAAlbum> album = query.from(JPAAlbum.class);

    Assert.assertNotNull(album.get(JPAAlbum_.theme)); // no problem here

    query.where(builder.equal(album.get(JPAAlbum_.theme).get(JPATheme_.id), 1L));

    List<JPAAlbum> results = em.createQuery(query).getResultList();
}

FWIW, вот сгенерированный SQL:

select
    jpaalbum0_.ID as ID32_,
    jpaalbum0_.AlbumDate as AlbumDate32_,
    jpaalbum0_.Description as Descript3_32_,
    jpaalbum0_.Nom as Nom32_,
    jpaalbum0_.Picture as Picture32_,
    jpaalbum0_.Theme as Theme32_ 
from
    Album jpaalbum0_ 
where
    jpaalbum0_.Theme=1

Протестировано с Hibernate EntityManager 3.5.6-Final, Hibernate JPAModelGen 1.1.0.Final, вне любого контейнера.

Мое предложение было бы сначала попытаться воспроизвести (если это можно воспроизвести) проблему в контексте теста JUnit.

PS: Как примечание, я не буду хранить сгенерированные классы в VCS.


Обновление: Вот persistence.xml, который вы можетеиспользовать в контексте тестирования:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  version="2.0">
  <persistence-unit name="MyPu" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <class>com.stackoverflow.q3854687.JPAAlbum</class>
    <class>com.stackoverflow.q3854687.JPATheme</class>
    <class>com.stackoverflow.q3854687.JPATagTheme</class>

    <exclude-unlisted-classes>true</exclude-unlisted-classes>

    <properties>
      <!-- Common properties -->
      <property name="javax.persistence.jdbc.driver" value="${jdbc.driver}" />
      <property name="javax.persistence.jdbc.url" value="${jdbc.url}" />
      <property name="javax.persistence.jdbc.user" value="${jdbc.user}" />
      <property name="javax.persistence.jdbc.password" value="${jdbc.password}" />

      <!-- Hibernate specific properties -->
      <property name="hibernate.dialect" value="${jdbc.dialect}" />
      <!--
      <property name="hibernate.show_sql" value="true"/>
      -->
      <property name="hibernate.format_sql" value="true" />
      <property name="hibernate.hbm2ddl.auto" value="update" />   
    </properties>
  </persistence-unit>
</persistence>
0 голосов
/ 24 апреля 2019

2019-04-24

Обычная проблема для незаполненных атрибутов класса метамодели - это когда классы метамодели находятся в другом пакете, чем соответствующие управляемые классы.

Последняя спецификация JPA 2.2 по-прежнему требует, чтобы ваши классы метамодели были в том же пакете, что и ваши соответствующие управляемые классы.

Ссылка : Страница 238, §6.2.1.1 Каноническая метамодель

0 голосов
/ 09 ноября 2018

Если ничто из вышеперечисленного не решает эту проблему NPE, вы также можете проверить, используете ли вы List в отношениях сущностей вместо Set.

Я обнаружил, что с помощью List необходимо объявить ListAttribute вместо SetAttribute.в метамодели, в противном случае, это вызывает исключение NullPointerException, и если вы не видите трассировки всего стека, вы не заметите, что метамодель не была инициализирована вашей спецификацией JPA.

0 голосов
/ 17 августа 2018

Класс и метамодель должны находиться в одном пакете, т.е.

Сущности папок:

  • Eje
  • Eje_
  • Элемент
  • Элемент_

Я приложил один пример кода метамодели

import javax.annotation.Generated;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
import java.util.Date;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Eje.class)
public abstract class Eje_ {
    public static volatile SingularAttribute<Eje, Integer> id;
    public static volatile SingularAttribute<Eje, String> name;
    public static volatile SingularAttribute<Eje, Integer> users;
    public static volatile SingularAttribute<Eje, Date> createdAt;
    public static volatile SingularAttribute<Eje, Date> updatedAt;
    public static volatile SetAttribute<Eje, FactorCritico> factorCriticos;
}
0 голосов
/ 08 августа 2018

Между прочим, я столкнулся со случаем, когда Hibernate создает атрибут метамодели, но никогда не инициализирует его, приводя к NullPointerException при попытке его использовать.

public class Upper {
    public String getLabel() { return this.label; }
    public void setLabel(String label) { this.label = label; }
}

public class Lower extends Upper {
   @Override
   public String getLabel() { return super.getLabel(); }
}

Hibernate генерирует атрибут labelобъявление в обоих классах:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Upper.class)
public abstract class Upper_ {
    public static volatile SingularAttribute<Upper, String> label;
}

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Lower.class)
public abstract class Lower_ {
    public static volatile SingularAttribute<Lower, String> label;
}

... и оно инициализирует Upper_.label, но оставляет Lower_.label равным нулю.

Boom .

0 голосов
/ 11 января 2017

Я предлагаю альтернативное решение, если размещение Model и Model_ в одном пакете не работает.Вам нужно добавить один метод init () в ваш класс, который создает SessionFactory или EntityManager:

public class HibernateSessionFactory {
  private static SessionFactory factory;

  static {
    try {
        factory = new Configuration().configure().buildSessionFactory();
    } catch (Throwable ex) {
        throw new ExceptionInInitializerError(ex);
    }
  }

  public static SessionFactory getFactory() {
    return factory;
  }

  public static void init(){} //does nothing but elimating the NULLPOINTEREXCEPTION
}

Поэтому, когда вы запускаете свое приложение из основного метода или модульного теста, вам сначала нужно вызвать HibernateSessionFactory.init();.Затем исключение NullPointerException волшебным образом исчезает, и приложение работает.

Такое странное поведение возникает, когда вы передаете SingularAttribute через параметр метода.

Кредит переходит к @Can ÜNSAL, который рассчитал всев этом вопросе: Hibernate / JPA - исключение NullPointerException при доступе к параметру SingularAttribute

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...