Declared metamodel attributes work fine BUT Inherited Metamodel Attributes are NULL. Why?

dira picture dira · Nov 2, 2010 · Viewed 8.3k times · Source

I am not able to run the following test:-

@Test
public void test() {
    EntityManager em = entityManagerFactory.createEntityManager();
    em.getTransaction().begin();

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Project> query = builder.createQuery(Project.class);

    Root<Project> project = query.from(Project.class);

    Path<String> name = project.get(Project_.name);
    Assert.assertNotNull(name);

    Path<EntityLifeCycleImpl> lifeCycle = project.get(Project_.lifeCycle); // problem is here, throws NullPointer
    Assert.assertNotNull(lifeCycle);
}

it throws NullPointerException at project.get(Project_.lifeCycle) line. Why?

java.lang.NullPointerException
    at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)

PersistenceEntityBase.java

import org.hibernate.annotations.GenericGenerator;
        @MappedSuperclass
@Access(AccessType.PROPERTY)
public abstract class PersistentEntityBase {

protected String identifier;

protected EntityLifeCycleImpl lifeCycle = new EntityLifeCycleImpl();

protected PersistentEntityBase() {
}

@Id
@GeneratedValue(generator="generator") 
@GenericGenerator(name="generator", strategy="guid", parameters = {})
@Column(name="identifier")
public String getIdentifier() {
    return identifier;
}

public void setIdentifier(String identifier) {
    this.identifier = identifier;
}

@Embedded
public EntityLifeCycleImpl getLifeCycle() {
    return lifeCycle;
}
public void setLifeCycle(EntityLifeCycleImpl lifeCycle) {
    this.lifeCycle = lifeCycle;
}

}

Project.java

@Entity
@Table(name="project")
@Access(AccessType.PROPERTY)
public class Project extends PersistentEntityBase {

    private String name;

    protected Project() {
    }

    public Project(String name) {
        this();
        this.name = name;
    }

    @Column(name="name", nullable=false, unique=true)
    public String getName() {
        return name;
}
public void setName(String name) {
    this.name = name;
}

}

EntityLifeCycleImpl.java

@Embeddable
public class EntityLifeCycleImpl implements EntityLifeCycle {
    private String createdBy;
    private Date createdDate;
       @Column(name="created_by")
public String getCreatedBy() {
    return createdBy;
}
public void setCreatedBy(String createdBy) {

    this.createdBy = createdBy;
}

@Column(name="created_date")
public Date getCreatedDate() {
    return createdDate;
}
public void setCreatedDate(Date createdDate) {
    this.createdDate = createdDate;
}

}

PersistentEntityBase_.java (Generated using Hibernate Metamodel Generator)

   @StaticMetamodel(PersistentEntityBase.class)
public abstract class PersistentEntityBase_ {
    public static volatile SingularAttribute<PersistentEntityBase, EntityLifeCycleImpl> lifeCycle;
        public static volatile SingularAttribute<PersistentEntityBase, String> identifier;
     }

Project_.java

    @StaticMetamodel(Project.class)
public abstract class Project_ extends PersistentEntityBase_ {
    public static volatile SingularAttribute<Project, String> name;
    }

EntityLifeCycleImpl_.java

   @StaticMetamodel(EntityLifeCycleImpl.class)
public abstract class EntityLifeCycleImpl_ {
    public static volatile SingularAttribute<EntityLifeCycleImpl, String> createdBy;
    public static volatile SingularAttribute<EntityLifeCycleImpl, Date> createdDate;
}

persistence.xml (only relavent part)

<class>com.comp.timetracking.entity.PersistentEntityBase</class>
<class>com.comp.timetracking.entity.Project</class>

EDIT: I use hibernate-entitymanager.3.6.0.Final and hibernate-jpamodelgen.1.0.0.Final.

EDIT 2 @Pascal
I think Hibernate EM 3.6.0.Final allows us to define @Embedded annotated field at @Entity level but it denies such field at @MappedSuperclass level. What do you say?

As I cannot see "file upload option" here, I've uploaded the TestCase in my esnips account. Download the maven-based-project and run SingularAttributeTest.java. And checkout the console output

ERROR main metamodel.MetadataContext:413 - Unable to locate static metamodel field : timetracking.entity.Employee_#lifeCycle

Click on "Download embedded-singular-attribute.zip" link to download the file WITHOUT installing the download manager. (If you click on Download link with Green arrow, you'll have to install the download manager!!)

Answer

Pascal Thivent picture Pascal Thivent · Nov 2, 2010

I tested your classes and test case with EclipseLink (I removed the Hibernate specific parts) and the test passes. But it indeed fails with Hibernate 3.5.6. Looks like a bug in Hibernate.

By the way, you are missing a Temporal annotation in your Embeddable

@Column(name = "created_date")
@Temporal(TemporalType.DATE)
public Date getCreatedDate() {
    return createdDate;
}

And you are not supposed to declare PersistentEntityBase in the persistence.xml (a mapped super class is not an entity).