代码之家  ›  专栏  ›  技术社区  ›  Kevin

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

  •  22
  • Kevin  · 技术社区  · 14 年前

    我想将JPA2标准API与元模型对象一起使用,这看起来非常简单:

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

    但是这个根总是抛出一个 NullPointerException . JPAAlbum_.theme 是由休眠自动生成的,看起来像

    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:

    在jboss EntityManager指南中,我可以看到

    在构建Hibernate EntityManagerFactory时,它将为每一个托管类型的is knowled寻找一个规范化的元模型类,如果找到任何一个,它将向它们注入适当的元模型信息,如[JPA 2规范,第6.2.2节,第200页]所述。

    我也可以用

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

    Hibernate知道我的元模型,但是属性名会被写入

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

    仍然空无一人…

    编辑3 这里是 JPAAlbum entity 及其 metamodel class .

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

    • 我用冬眠 3.5.6决赛 (根据META-INF/manifest.mf)

    • 部署在Glassfish上 3.0.1

    • 来自NETBeBeS 6 .1 ;

    • 应用程序依赖EJB 三点一 ,

    希望能有所帮助!

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

    您应该能够通过下载 塔尔鲍尔 ,建设项目:

    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-5 JAR的正确位置)

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

    8 回复  |  直到 14 年前
        1
  •  46
  •   buræquete Naveen Kocherla    7 年前

    我也有同样的问题,通过 Model Model_ 类到同一个包中。

        2
  •  10
  •   Vítor E. Silva Souza    13 年前

    我有一个JavaEE 6应用程序,在GalasFISH上使用了Eclipse,创建了一些@ StistMeta模型类,所有的工作都很好。当我切换到JBoss7上的Hibernate4时,我也开始使用这些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)。

        3
  •  8
  •   Pascal Thivent    14 年前

    我不能复制这个问题。我使用了你的一些实体(简化版 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测试环境中重现(如果可以重现)这个问题。

    附言:我不会将生成的类存储在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>
    
        4
  •  0
  •   Community Mike Kinghan    7 年前

    如果将模型和模型放在同一个包中不起作用,我会提供另一种解决方案。您需要将一个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 when accessing SingularAttribute parameter

        5
  •  0
  •   Archie    6 年前

    仅供参考,我遇到了这样一个情况: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(); }
    }
    

    休眠生成 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 等于零。

    繁荣 .

        6
  •  0
  •   Jorge Santos Neill    6 年前

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

    文件夹实体:

    • 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;
    }
    
        7
  •  0
  •   Eduardo    6 年前

    如果上面的任何内容都不能解决这个NPE问题,您还可以检查是否在实体关系中使用列表而不是集合。

    我发现使用list的,需要在元模型中声明listattribute而不是setattribute,否则会引发nullpointerException,如果看不到整个堆栈跟踪,您将不会注意到元模型没有被JPA规范初始化。

        8
  •  0
  •   Oliver    5 年前

    2019-04-24

    对于未填充的元模型类属性,通常的问题是当元模型类与相应的托管类位于不同的包中时。

    最新的, JPA 2.2 规范仍然要求将元模型类与相应的托管类放在同一个包中。

    Reference :第238页,§6.2.1.1规范元模型