java - makigas - Atributos de metamodelos estáticos JPA/Hibernate no rellenados-NullPointerException
makigas hibernate (7)
Me gustaría usar la API de criterios JPA2 con objetos de metamodelo, que parece ser bastante fácil:
...
Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
... albm.get(JPAAlbum_.theme) ... ;
pero este Root.get siempre lanza una NullPointerException
. JPAAlbum_.theme
fue generado automáticamente por Hibernate y se parece a
public static volatile SingularAttribute<JPAAlbum, JPATheme> theme;
pero obviamente nunca se ha poblado.
¿Me falta un paso en la inicialización del framework?
EDITAR: aquí hay un fragmento de cómo uso JPA y el metamodelo cuando se bloquea:
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_
es una clase, así que solo importé antes) y el seguimiento de pila asociado:
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)
EDIT 2:
En la guía de JBoss EntityManager, puedo ver que
Cuando se está construyendo el EntityManagerFactory de Hibernate, buscará una clase de metamodelo canónica para cada uno de los tipos tipificados administrados que se conoce y si encuentra alguna, inyectará la información de metamodelo adecuada en ellos, como se describe en [Especificación JPA 2, sección 6.2. .2, pág. 200]
También pude verificar con
for (ManagedType o : em.getMetamodel().getManagedTypes()) {
log.warn("___") ;
for (Object p : o.getAttributes()) {
log.warn(((Attribute)p).getName()) ;
}
}
que Hibernate conoce mi metamodelo, los nombres de los atributos están escritos, sin embargo
log.warn("_+_"+JPAPhoto_.id+"_+_") ;
permanece desesperadamente vacío ...
EDIT3 : aquí está la entidad JPAAlbum y su clase de metamodelo .
¿Qué más puedo decir acerca de mi configuración ...
Uso Hibernat 3.5.6-Final (de acuerdo con META-INF / MANIFEST.MF),
desplegar en Glassfish 3.0.1
de Netbeans 6.9.1 ;
y la aplicación se basa en EJB 3.1 ,
Espero que te ayude!
EDITAR 4:
desafortunadamente, la prueba JUnit conduce a la misma excepción:
java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
at net.wazari.dao.test.TestMetaModel.foo(TestMetaModel.java:55)
Un proyecto mucho más simple está disponible here / tarball . Solo contiene mis entidades y su metamodelo, más una prueba de JUnit (foo se bloquea con el metamodelo, la barra está de acuerdo con la consulta habitual).
EDIT 5:
Debería poder reproducir el problema descargando el tarball , creando el proyecto:
ant compile
or
ant dist
e inicie la prueba de JUnit net.wazari.dao.test.TestMetaModel
CLASSPATH=`sh runTest.sh` java org.junit.runner.JUnitCore net.wazari.dao.test.TestMetaModel
(edite runTest.sh
para apuntar a CLASSPATH a la ubicación correcta de su tarro JUnit4-5)
Todas las dependencias de hibernación que utilizo deben incluirse en el archivo.
La clase y el metamodelo deben estar en el mismo paquete, es decir,
Entidades de carpeta:
- Eje
- Eje_
- Elemento
- Elemento_
Adjunto un ejemplo del código del metamodelo
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;
}
No puedo reproducir el problema. JPAAlbum
algunas de sus entidades (versiones simplificadas de JPAAlbum
, JPATheme
y JPATagTheme
, sin ninguna interfaz), generé las clases de metamodelos y el siguiente método de prueba rudimentario (que se ejecuta dentro de una transacción) simplemente pasa:
@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, aquí está el SQL generado:
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
Probado con Hibernate EntityManager 3.5.6-Final, Hibernate JPAModelGen 1.1.0.Final, fuera de cualquier contenedor.
Mi sugerencia sería intentar primero reproducir (si es reproducible) el problema en un contexto de prueba JUnit.
PD: Como nota al margen, no almacenaría las clases generadas en el VCS.
Actualización: aquí hay un persistence.xml
que puede usar en un contexto de prueba:
<?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..q3854687.JPAAlbum</class>
<class>com..q3854687.JPATheme</class>
<class>com..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>
Ofrezco una solución alternativa si poner el Modelo y el Modelo_ en el mismo paquete no funciona. Debe agregar un método init () a su clase que genere SessionFactory o 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
}
Entonces, cuando ejecuta su aplicación desde el método principal o una prueba de unidad, necesita llamar a HibernateSessionFactory.init();
primero. Entonces la NullPointerException desaparece mágicamente y la aplicación funciona.
Este extraño comportamiento parece ocurrir cuando se pasa un SingularAttribute
a través del parámetro del método.
El crédito es para @Can ÜNSAL que lo descubrió todo en esta pregunta: Hibernate / JPA - NullPointerException al acceder al parámetro SingularAttribute
Para su información, encontré un caso en el que Hibernate crea un atributo de metamodelo pero nunca lo inicializa, lo que lleva a una NullPointerException
cuando intento usarlo.
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 genera una declaración de atributo de label
en ambas clases:
@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;
}
... e inicializará Upper_.label
pero dejará Lower_.label
igual a nulo.
Boom
Si nada de lo anterior resuelve este problema de NPE, también puede verificar si está usando Lista en sus relaciones de Entidades en lugar de Establecer.
Descubrí que es necesario usar List''s para declarar ListAttribute en lugar de SetAttribute en el metamodelo, de lo contrario, provocará una NullPointerException y si no ve el seguimiento completo de la pila, no notará que el metamodelo no fue inicializado por su especificación JPA .
Tuve el mismo problema y se solucionó al colocar la clase Model
y Model_
en el mismo paquete.
Tuve una aplicación Java EE 6 que utiliza EclipseLink en GlassFish con algunas clases de @StaticMetamodel creadas y todo funcionaba bien. Cuando cambié a Hibernate 4 en JBoss 7, también comencé a obtener estas NPE. Comencé a investigar y encontré esta página:
http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/metamodel.html
Cita la especificación JPA 2, sección 6.2.1.1, que define cómo se deben construir las clases de metamodelo estático. Por ejemplo, descubrí leyendo la especificación que "la opción de paquetes diferentes se proporcionará en una versión futura de esta especificación". Tuve las clases de metamodelos en diferentes paquetes y funcionó bien en EclipseLink, pero es una característica adicional, ya que el estándar actual indica lo siguiente:
- Las clases de metamodelos deben estar en el mismo paquete que las clases de entidad que describen;
- Deben tener el mismo nombre que las clases de entidad que describen, seguidas de un guión bajo (por ejemplo, Producto es la entidad, Producto_ es la clase de metamodelo);
- Si una entidad hereda de otra entidad o de una superclase asignada, su clase de metamodelo debería heredar de la clase de metamodelo que describe su superclase inmediata (por ejemplo, si SpecialProduct extiende Product, que extiende PersistentObject, entonces SpecialProduct_ debe extender Product_ que debería extender PersistentObject_).
Una vez que seguí todas las reglas en la especificación (lo anterior es solo un resumen, consulte la sección 6.2.1.1 de la especificación para la versión completa), dejé de recibir las excepciones.
Por cierto, puede descargar la especificación aquí: http://jcp.org/en/jsr/detail?id=317 (haga clic en "Descargar página" para la versión final, elija descargar la especificación para la evaluación, acepte la acuerdo y descargue el archivo "SR-000317 2.0 Especificación" - persistencia-2_0-final-spec.pdf).