我想将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 Specification]中所述。 ,第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 entity及其metamodel class。
我还能告诉我的配置......
我使用Hibernat 3.5.6-Final (根据META-INF / MANIFEST.MF),
部署在Glassfish 3.0.1
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)
一个更简单的项目可用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
将CLASSPATH指向JUnit4-5 jar的正确位置)
我使用的所有hibernate依赖项都应该包含在存档中。
答案 0 :(得分:46)
我遇到了同样的问题,并通过将Model
和Model_
类放入同一个包中来解决此问题。
答案 1 :(得分:10)
我在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上运行良好,但它是一个额外的功能,因为当前的标准表明了以下内容:
一旦我遵循规范中的所有规则(以上只是摘要,请参阅完整版规范的第6.2.1.1节),我就停止了例外。
顺便提一下,您可以在此处下载规范:http://jcp.org/en/jsr/detail?id=317(点击最新版本的“下载页面”,选择下载评估规范,接受协议并下载文件“SR-000317 2.0规范“ - persistence-2_0-final-spec.pdf)。
答案 2 :(得分:8)
我无法重现这个问题。我使用了一些实体(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测试上下文中重现(如果可重现)问题。
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>
答案 3 :(得分:0)
如果将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 accessing SingularAttribute parameter
答案 4 :(得分:0)
仅供参考,我遇到了一种情况,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 :(得分:0)
类和metaModel应该在同一包中,即
文件夹实体:
我附上了元模型代码的一个示例
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;
}
答案 6 :(得分:0)
如果以上方法都不能解决此NPE问题,您还可以检查是否在实体关系中使用列表,而不是使用列表。
我发现使用List的需要在元模型中声明ListAttribute而不是SetAttribute,否则,它会引发NullPointerException,并且如果您没有看到整个堆栈跟踪,则不会注意到该元模型没有通过以下方式初始化您的JPA规范。
答案 7 :(得分:0)
2019-04-24
对于未填充的元模型类属性,通常的问题是元模型类与相应的托管类位于不同的包中。
最新的 JPA 2.2 规范仍然需要将元模型类与相应的托管类放在同一包中。
Reference:第238页,第6.2.1.1节规范元模型
答案 8 :(得分:0)
Debbie's answer使我到达了一半。
还有另一个“名称匹配”伏都教的陷阱。
简短版本:
对于“属性”,“名称”必须在模型和元模型之间匹配。
长版:
我使用的是非易风的名字。
首先是实体:
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;
}
我故意使用非“标准”名称的原因之一是在开始时就清除了这些伏都教设置问题...为了尽早尝试并失败... vs failLater(希望在质量检查,而不是生产中的问题),因为有人重命名了当时看起来不错的属性。