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

发布于 2024-09-26 15:17:49 字数 3625 浏览 8 评论 0原文

我想将 JPA2 Criteria API 与元模型对象一起使用,这似乎非常简单:

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

但是这个 Root.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 )和相关的堆栈跟踪:

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 还可以使用通常的查询

编辑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 的正确位置)

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

I would like to use JPA2 Criteria API with metamodel objects, which seems to be pretty easy:

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

but this Root.get always throws a NullPointerException. JPAAlbum_.theme was automatically generated by Hibernate and looks like

public static volatile SingularAttribute<JPAAlbum, JPATheme> theme;

but it's obviously never populated.

Am I missing a step in the initialization of the framework ?

EDIT: here is a snippet of how I use JPA and the metamodel when it's crashing:

    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_ is a class, so I just import before) and the associated 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)

EDIT 2:

In the JBoss EntityManager guide, I can see that

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]

I could also verify with

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

that Hibernate is aware of my metamodel, the attribute names are written, however

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

remains desperately empty ...

EDIT3: here is the JPAAlbum entity and its metamodel class.

What else can I tell about my configuration ...

  • I use Hibernat 3.5.6-Final (according to META-INF/MANIFEST.MF),

  • deploy on Glassfish 3.0.1

  • from Netbeans 6.9.1;

  • and the application relies on EJB 3.1,

I hope it will help !

EDIT 4:

unfortunately, the JUnit test leads to the same exception:

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

A much simpler project is available here/tarball. It only contains my entities and their metamodel, plus a JUnit test (foo crashes with metamodel, bar is okay with the usual Query.

EDIT 5:

You should be able to reproduce the problem by downloading the tarball, building the project:

ant compile
or
ant dist

and start the JUnit test net.wazari.dao.test.TestMetaModel

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

(edit runTest.sh to point CLASSPATH to the right location of your JUnit4-5 jar)

All the hibernate dependencies I use should be included in the archive.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

夜灵血窟げ 2024-10-03 15:17:50

类和metaModel应该在同一个包中,即

夹实体:

  • Eje
  • Eje_Element
  • Element_
  • 文件

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

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;
}

The class and the metaModel should be in the same package, i.e.

Folder entities:

  • Eje
  • Eje_
  • Element
  • Element_

I attached one example of the metamodel code

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;
}
清浅ˋ旧时光 2024-10-03 15:17:50

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

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

简短版本:

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

更长的版本:

我使用的是不简单的名字。

首先是实体:

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;
}

我故意使用非“标准”名称的原因之一事情是在一开始就清除这些巫术设置问题......以便尝试并尽早失败......与稍后失败(希望在质量检查中,而不是在生产中)问题相比,因为有人重命名了一个属性当时看起来很良性。

Debbie's answer got me half of the way there.

There is another "name matching" voodoo gotcha.

Short version:

The "names" have to match between Model and the MetaModel for the "properties".

Longer version:

I was using non-easy-peezy names.

First the entities:

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<>();

}

and

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

}

Note, my somewhat not default names. pay attention to the OnetoMany and ManyToOne object names.

Now, my meta-models:

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 :(  */
}

but this (below) will work, because I'm magic voodoo'ing the same names:

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;
}

and the other one:

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 */
}

But this does work:

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;
}

One of the reasons I purposely use non "standard" names for things is to flush out these voodoo setup issues at the beginning......in order to try and fail early...vs a failLater (hopefully in QA, not in production) issue(s) because someone renamed a property which seems benign at the time.

送你一个梦 2024-10-03 15:17:49

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

I had the same issue and it was fixed by putting the Model and Model_ class into the same package.

蓝海似她心 2024-10-03 15:17:49

我有一个在 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,而 Product 又扩展了 PersistentObject,则 SpecialProduct_ 应该扩展 Product_,而 Product_ 又扩展 PersistentObject_)。

一旦我遵循了规范中的所有规则(以上只是一个摘要,完整版本请参阅规范的第 6.2.1.1 节),我就不再遇到异常了。

顺便说一下,您可以在这里下载规范: http://jcp.org/en/ jsr/detail?id=317(最终版本点击“下载页面”,选择下载评估规范,接受协议并下载文件“SR-000317 2.0 规范”- persistence-2_0-最终规范.pdf)。

I had a Java EE 6 application using EclipseLink on GlassFish with some @StaticMetamodel classes created and everything was working fine. When I switched to Hibernate 4 on JBoss 7, I started getting these NPEs too. I started investigating and I found this page:

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

It quotes the JPA 2 specification, section 6.2.1.1 which defines how the static metamodel classes should be built. For example, I found out by reading the spec that "the option of different packages will be provided in a future release of this specification". I had the metamodel classes in different packages and it worked fine on EclipseLink, but it's an extra feature, as the current standard indicates the following:

  • Metamodel classes should be in the same package as the entity classes they describe;
  • They should have the same name as the entity classes they describe, followed by an underscore (e.g. Product is the entity, Product_ is the metamodel class);
  • If an entity inherits from another entity or from a mapped superclass, its metamodel class should inherit from the metamodel class that describes its immediate superclass (e.g. if SpecialProduct extends Product, which extends PersistentObject, then SpecialProduct_ should extend Product_ which should extend PersistentObject_).

Once I followed all the rules in the spec (the above is just a summary, please refer to section 6.2.1.1 of the spec for the complete version), I stopped getting the exceptions.

By the way, you can download the specification here: http://jcp.org/en/jsr/detail?id=317 (click on "Download page" for the final release, choose to download the specification for evaluation, accept the agreement and download the file "SR-000317 2.0 Specification" - persistence-2_0-final-spec.pdf).

灯下孤影 2024-10-03 15:17:49

我无法重现该问题。我使用了您的一些实体(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.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>

I can't reproduce the issue. I used some of your entities (simplified versions of JPAAlbum, JPATheme and JPATagTheme, without any interfaces), generated the metamodel classes and the following rudimentary test method (running inside a transaction) just passes:

@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, here is the generated 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

Tested with Hibernate EntityManager 3.5.6-Final, Hibernate JPAModelGen 1.1.0.Final, outside any container.

My suggestion would be to first try to reproduce (if reproducible) the problem in a JUnit test context.

PS: As a side note, I wouldn't store generated classes in the VCS.


Update: Here is a persistence.xml that you can use in a testing context:

<?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>
獨角戲 2024-10-03 15:17:49

2019-04-24

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

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

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

2019-04-24

The usual issue for unpopulated metamodel class attributes, is when the metamodel classes are in a different package than the corresponding managed classes.

The latest, JPA 2.2 specification still requires to have your metamodel classes in the same package as your corresponding managed classes.

Reference: Page 238, §6.2.1.1 Canonical Metamodel

娇柔作态 2024-10-03 15:17:49

如果将 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 - 访问 SingularAttribute 参数时出现 NullPointerException

I offer an alternative solution if putting the Model and Model_ in the same package does not work. You need to add one init() method to your class that builds the SessionFactory or 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
}

So when you run your application from main method or a unit test you need to call HibernateSessionFactory.init(); first. Then the NullPointerException magically disappears and the application works.

This strange behaviour seems to happen when you pass a SingularAttribute around via method parameter.

Credit goes to @Can ÜNSAL who figured it all out in this question: Hibernate/JPA - NullPointerException when accessing SingularAttribute parameter

你在看孤独的风景 2024-10-03 15:17:49

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

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

If nothing of above resolve this NPE issue, you also can check whether you are using List in your Entities relationships instead of Set.

I found out that using List's it is needed to declare ListAttribute instead of SetAttribute in the metamodel, otherwise, it provoke a NullPointerException and if you don't see the whole stack trace you will not notice that the metamodel was not initialized by your JPA specification.

夜声 2024-10-03 15:17:49

仅供参考,我遇到了一种情况,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< /code> 等于 null。

繁荣

FYI, I encountered a case where Hibernate creates a metamodel attribute but never initializes it, leading to a NullPointerException's when trying to use it.

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 generate a label attribute declaration in both classes:

@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;
}

...and it will initialize Upper_.label but leave Lower_.label equal to null.

Boom.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文