有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java JPA/Hibernate静态元模型属性未填充NullPointerException

我想将JPA2标准API用于元模型对象,这似乎非常简单:

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

但是这个根。get总是抛出一个NullPointerExceptionJPAAlbum_.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)和关联的stacktrace:

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:

在JBossEntityManager指南中,我可以看到

When the Hibernate EntityManagerFactory is being built, it will look for a canonical metamodel class for each of the managed typed is knows about and if it finds any it will inject the appropriate metamodel information into them, as outlined in [JPA 2 Specification, section 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 entity及其metamodel class

关于我的配置,我还能说些什么

  • 我使用Hibernat3.5.6-Final(根据META-INF/MANIFEST.MF),

  • 部署在Glassfish上3.0.1

  • 来自Netbeans6.9.1

  • 应用程序依赖于EJB3.1

我希望这会有帮助

编辑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)

有一个简单得多的项目here/tarball它只包含我的实体和它们的元模型,加上一个JUnit测试(foo与元模型崩溃,bar与通常的查询无关)

编辑5:

您应该能够通过下载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将类路径指向JUnit4-5JAR的正确位置)

我使用的所有hibernate依赖项都应该包含在存档中


共 (6) 个答案

  1. # 1 楼答案

    我有一个JavaEE6应用程序,在GlassFish上使用EclipseLink,创建了一些@StaticMetamodel类,一切都很好。当我在JBoss7上切换到Hibernate 4时,我也开始使用这些NPE。我开始调查,发现了这个页面:

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

    它引用了JPA2规范第6.2.1.1节,该节定义了静态元模型类应该如何构建。例如,通过阅读规范,我发现“本规范的未来版本将提供不同包的选项”。我在不同的包中使用了元模型类,它在EclipseLink上运行良好,但这是一个额外的功能,正如当前的标准所示:

    • 元模型类应该和它们描述的实体类在同一个包中
    • 它们应该与它们描述的实体类具有相同的名称,后跟下划线(例如,产品是实体,产品是元模型类)
    • 如果一个实体从另一个实体或映射的超类继承,其元模型类应该从描述其直接超类的元模型类继承(例如,如果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-final-spec.pdf)

  2. # 2 楼答案

    类和元模型应该在同一个包中,即

    文件夹实体:

    • 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;
    }
    
  3. # 3 楼答案

    我也有同样的问题,通过将ModelModel_类放在同一个包中解决了这个问题

  4. # 4 楼答案

    仅供参考,我遇到了一个例子,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等于null

    砰的一声

  5. # 5 楼答案

    如果将模型和模型放在同一个包中不起作用,我会提供另一种解决方案。您需要向构建SessionFactory或EntityManager的类添加一个init()方法:

    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
    }
    

    因此,当您从main方法或单元测试运行应用程序时,需要首先调用HibernateSessionFactory.init();。然后NullPointerException神奇地消失,应用程序开始工作

    当通过方法参数传递SingularAttribute时,这种奇怪的行为似乎会发生

    归功于@CanÜNSAL,他在这个问题上解决了所有问题:Hibernate/JPA - NullPointerException when accessing SingularAttribute parameter

  6. # 6 楼答案

    我无法重现这个问题。我使用了您的一些实体(简化版的JPAAlbumJPAThemeJPATagTheme,没有任何接口),生成了元模型类,以下基本测试方法(在事务内部运行)刚刚通过:

    @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进行测试。最后,在任何容器外

    我的建议是,首先尝试在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>