1

I have a very curious problem when I deploy my Java EE application to Glassfish. I have an Eclipse EAR project that references a web project (containing a servlet), and an EJB project (that has one EJB) and a JPA Project (that has one @Entity). In my servlet I call the EJB, which in turn performs the following query:

final CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
final CriteriaQuery<Country> criteriaQuery = criteriaBuilder.createQuery(Country.class);
final Root<Country> root = criteriaQuery.from(Country.class);

On the third line of the above code I get an exception:

Severe: java.lang.IllegalArgumentException: Not an entity: class <package>.entity.Country
at org.hibernate.jpa.internal.metamodel.MetamodelImpl.entity(MetamodelImpl.java:203)
at org.hibernate.jpa.criteria.QueryStructure.from(QueryStructure.java:139)
at org.hibernate.jpa.criteria.CriteriaQueryImpl.from(CriteriaQueryImpl.java:173)
at <package>.CountrySessionBean.getCountries(CountrySessionBean.java:35)
...

I have debugged the above mentioned Hibernate class, namely MetamodelImpl, and I see that it has registered my Entity (in the entities Map). The class name is exactly the same as the one I'm asking for (net...Country). However, this class is different from the following code which I place in the servlet:

final Class<?> countryClass = Country.class;

I say it is "different", because the 'declaredConstructors' field and the 'declaredPublicMethods' are different. In the servlet these fields are 'null' (strange, see Country.java below!), but in the MetamodelImpl class these fields contain the constructors and public methods that I have defined in the entity. Because MetamodelImpl uses a Map, it cannot find the entity amongst the registered classes because the "class" is different (i.e. stored at a different location in memory), and of course it throws the IllegalArgumentException. I have checked that what is deployed to Glassfish only ever contains one Country.class file, so this is really confusing why there are two different Class instances of the same class, and that they are different from each other!

Anyone have any idea?

For reference: Hibernate v4.3.6, EJB v3.2, Glassfish v4.1, JPA v2.1.

My Country.java entity is below. As you can see, it contains a constructor and public methods, so it astonishes me that in the servlet the class does NOT have 'declaredConstructors' or 'declaredPublicMethods'. How can this be?

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

@Entity
public class Country implements Serializable {
private static final long serialVersionUID = -3119088680575312729L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

@Pattern(regexp = ".[\\p{L} \\.\\[\\]\\?\\-\\(\\)]{1,30}+")
@NotNull
private String name;

public Country() {
    super();
}

public String getName() {
    return this.name;
}

public void setName(final String name) {
    this.name = name;
}

public Integer getId() {
    return this.id;
}

public void setId(final Integer id) {
    this.id = id;
}

@Override
public String toString() {
    final StringBuilder builder = new StringBuilder();

    builder.append(this.name);
    builder.append(" (");
    builder.append(this.id);
    builder.append(")");

    return builder.toString();
}
}
Raoul
  • 133
  • 3
  • 15
  • jpa is a mess sometimes. 1. Declare the entities in the persistence.xml, or 2. ensure that they are in the same jar as the ejb – maress Oct 07 '14 at 15:04
  • Thanks. I've tried this also, to not avail by creating the most simple entity in the EJB project with just an empty public constructor an id and a getter/setter for it. Then in the EJB code I check the class and it is a different Class than the one registered in the MetamodelImpl. It just doesn't make sense that a class I define with a constructor and getters and setters at runtime doesn't have any constructor or method when I refer to it with MyEntity.class. Where have these declarations gone? – Raoul Oct 08 '14 at 06:53

2 Answers2

2

Solved! This link helped, particularly the comment about the persistence.xml.

I still don't understand how you could ever have two different Class instances for one class, but it indeed seems just to be a particular way if you have separate EJB, WEB and JPA projects, which is as follows:

  1. Add the persistence.xml NOT to the JPA project (or the project containing the @Entity instances), but rather add it to the web project (I added it to src/META-INF, although there's other possibilities, which I haven't yet tested).

  2. In the persistence.xml, you have to mention the classes with "fqn-of-Class", EVEN though the classes should be picked up by the annotation. The setting "exclude-unlisted-classes (true) is not relevant. It works with and without this.

Doing this means the query now works without the annoying IllegalArgumentException stating the Entity is "not an Entity".

Tested on JBoss EAP 6.3, Java 7, JPA 2.1, EJB 3.1, although I assume it will also work on Glassfish with EJB 3.2.

Raoul
  • 133
  • 3
  • 15
0

Have you checked your persistence.xml ? Is the entity class explicitely stated or the exclude-unlisted-classes property set to false ?

Or perhaps multiple definitions of the entity class in the classpath (what could explain the presence of an instance registered in the metamodel but not resolved) ?

bdulac
  • 1,686
  • 17
  • 26
  • Thanks, but I've tried that also: `code` org.hibernate.jpa.HibernatePersistenceProvider MySQLResource net..ejb.MyEntity true `code` And yes, there is only one MyEntity class file in the deployed application. I also thought there would be two, but it is not the case. I looked in all folders and jars. It just doesn't make sense that there are two Class instances of the same class. – Raoul Oct 08 '14 at 06:55
  • Have you tried to explicitly call the [Metamodel#entity(...)](http://docs.oracle.com/javaee/6/api/javax/persistence/metamodel/Metamodel.html#entity(java.lang.Class))] method with your class object in order to check the way hibernate resolves it ? – bdulac Oct 08 '14 at 08:21
  • I've tried it, but it is the same result. MyEntity is not an Entity. MetamodelImpl.entity(...) just looks up the entity in a map, and uses the MyEntity class as a key. However, as I mention above, the entity registered MetamodelImpl (in the entities Map) is correct (it has a constructor and public methods), but if I inspect MyEntity.class in the debugger I see that it is a "different" MyEntity class that has no constructors and no public methods. It is as if Hibernate has registered the correct entity, but at runtime (in the EJB), the MyEntity.class is a wrong class with no methods! Strange! – Raoul Oct 08 '14 at 08:51
  • This says it all: java.lang.ClassCastException: net.abc.jpa.entity.Country cannot be cast to net.abc.jpa.entity.Country. Get this if I iterate over the entities defined in MetamodelImpl & retrieve the class that IS registered, and then try to run the query as in the code above. Literally there are 2 Class instances (package is same) that are different from each other. This is why MetamodelImpl looking it up in a Map doesn't return a result, and states "Not an entity" for Country.class. I tried a completely new entity, defined & used only in the EJB project. Same problem. Astonishing! – Raoul Oct 08 '14 at 09:12
  • This probably because you have two different versions of the class solvable by the ClassLoader. These should have a serialVersionUID. Try to regenerate the constant to have a more explicit message. Do you have a sharp idea of what is available to the ClassLoader ? I suspect you have multiple ClassLoaders becaucause of your environment. You could also compare the ClassLoader of the register EntityClass and of your local class instance... – bdulac Oct 08 '14 at 10:43
  • Thanks for your persistence (pun intended) @bdulac, however, I've checked the deployment folder where the contents of the EAR file are, and there is only one version of the entity class, and this is in the custom-jpa.jar (resulting from the jpa project). Neither the EJB nor the WEB project include this jar or its class file. I've now deployed on JBoss with exactly the same result. Both the EJB and WEB project have a Maven dependency to the JPA project, but the dependency is of "compile" scope. I've also removed all PrimeFaces code and its jar, so that's also not the culprit. Very frustrating! – Raoul Oct 08 '14 at 13:06
  • Solved! Thanks @bdulac, see separate answer. – Raoul Oct 08 '14 at 15:19