26

我想将 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 指南中,我可以看到

当构建 Hibernate EntityManagerFactory 时,它将为每个已知的托管类型查找规范元模型类,如果找到任何它会将适当的元模型信息注入其中,如 [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+"_+_") ;

仍然绝望地空着......

EDIT3:这里是JPAAlbum 实体及其元模型类

我还能说什么关于我的配置...

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

  • 在 Glassfish 3.0.1上部署

  • 来自 Netbeans 6.9.1

  • 并且该应用程序依赖于 EJB 3.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)

此处/ tarball提供了一个更简单的项目。它只包含我的实体及其元模型,以及一个 JUnit 测试(foo 与元模型崩溃,bar 与通常的 Query.

编辑 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以将 CLASSPATH 指向您的 JUnit4-5 jar 的正确位置)

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

4

9 回答 9

53

我遇到了同样的问题,通过将ModelandModel_类放入同一个包中解决了这个问题。

于 2011-02-03T21:43:56.853 回答
12

我有一个在 GlassFish 上使用 EclipseLink 的 Java EE 6 应用程序,并创建了一些 @StaticMetamodel 类,并且一切正常。当我在 JBoss 7 上切换到 Hibernate 4 时,我也开始获得这些 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 Specification”-persistence-2_0-final-spec.pdf)。

于 2011-11-10T14:32:43.653 回答
8

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

@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>
于 2010-11-07T15:06:01.573 回答
1

2019-04-24

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

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

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

于 2019-04-24T11:55:55.950 回答
1

如果将 Model 和 Model_ 放在同一个包中不起作用,我提供了一个替代解决方案。您需要向构建 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 access SingularAttribute parameter

于 2017-01-11T14:07:27.840 回答
0

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

文件夹实体:

  • 埃杰
  • 埃杰_
  • 元素
  • 元素_

我附上了元模型代码的一个示例

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;
}
于 2018-08-17T20:03:22.957 回答
0

仅供参考,我遇到了 Hibernate 创建元模型属性但从未初始化它的情况,导致NullPointerException尝试使用它时出现 's 。

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_.labelLower_.label保持等于null。

轰隆隆

于 2018-08-07T22:11:32.097 回答
0

黛比的回答让我成功了一半。

还有另一个“名称匹配”巫毒陷阱。

精简版:

“名称”必须在“属性”的模型和元模型之间匹配。

更长的版本:

我使用了不简单的名字。

首先是实体:

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.LinkedHashSet;
import java.util.Set;

@Entity
@Table(name = "DepartmentTable")
public class DepartmentJpaEntity implements Serializable {

    @Id
    @Column(name = "DepartmentKey", unique = true)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long departmentKey;
    @Column(name = "DepartmentName", unique = true)
    private String departmentName;
    @Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE")
    private OffsetDateTime createOffsetDateTime;
    @OneToMany(
            mappedBy = "parentDepartmentJpaEntity",
            cascade = CascadeType.PERSIST,
            orphanRemoval = true,
            fetch = FetchType.LAZY /* Lazy or Eager here */
    )

    private Set<EmployeeJpaEntity> employeeJpaEntities = new LinkedHashSet<>();

}

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;

@Entity
@Table(name = "EmployeeTable")
public class EmployeeJpaEntity implements Serializable {

    @Id
    @Column(name = "EmployeeKey", unique = true)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long employeeKey;

    @Column(name = "Ssn")
    private String ssn;

    @Column(name = "LastName")
    private String lastName;

    @Column(name = "FirstName")
    private String firstName;

    @Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE")
    private OffsetDateTime createOffsetDateTime;

    //region Navigation
    
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = DepartmentJpaEntity.class, cascade = CascadeType.PERSIST )
    @JoinColumn(name = "DepartmentForeignKey")
        private DepartmentJpaEntity parentDepartmentJpaEntity;
    //endregion

}

请注意,我有些不是默认名称。注意 OnetoMany 和 ManyToOne 对象名称。

现在,我的元模型:

import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(DepartmentJpaEntity.class)

public class DepartmentJpaEntity_ {
  public static volatile SingularAttribute<DepartmentJpaEntity, Long> departmentKey;
  public static volatile SetAttribute<DepartmentJpaEntity, EmployeeJpaEntity> employees; /* DOES NOT WORK :(  */
}

但这(下面)会起作用,因为我是魔法巫毒巫术相同的名字:

import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(DepartmentJpaEntity.class)

public class DepartmentJpaEntity_ {
  public static volatile SingularAttribute<DepartmentJpaEntity, Long> departmentKey;
  public static volatile SetAttribute<DepartmentJpaEntity, EmployeeJpaEntity> employeeJpaEntities;
}

另一个:

import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(EmployeeJpaEntity.class)

public class EmployeeJpaEntity_ {
  public static volatile SingularAttribute<EmployeeJpaEntity, Long> employeeKey;
  public static volatile SingularAttribute<EmployeeJpaEntity, DepartmentJpaEntity> parentDepartment;  /*does NOT work...its null at run time...no voodoo name matching */
}

但这确实有效:

import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(EmployeeJpaEntity.class)

public class EmployeeJpaEntity_ {
  public static volatile SingularAttribute<EmployeeJpaEntity, Long> employeeKey;
  public static volatile SingularAttribute<EmployeeJpaEntity, DepartmentJpaEntity> parentDepartmentJpaEntity;
}

我故意使用非“标准”名称的原因之一是在开始时清除这些巫毒设置问题......在生产中)问题,因为有人重命名了当时似乎是良性的属性。

于 2020-06-11T15:41:08.747 回答
0

如果以上都不能解决这个 NPE 问题,您还可以检查您是否在实体关系中使用 List 而不是 Set。

我发现使用 List 需要在元模型中声明 ListAttribute 而不是 SetAttribute,否则,它会引发 NullPointerException,如果您没有看到整个堆栈跟踪,您将不会注意到元模型未由您的 JPA 规范初始化.

于 2018-11-09T16:01:16.433 回答