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.
我遇到了同样的问题,它通过将Model
和Model_
类放入同一个包中来解决。
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:
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).
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>
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
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 .
The class and the metaModel should be in the same package, ie
Folder entities:
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;
}
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.
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
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.