Bug 1127919

Summary: [HHH-9256] Receiving "Error in named query: org.hibernate.QueryException: could not resolve property xxx" if annotating on property not on field
Product: [JBoss] JBoss Enterprise Application Platform 6 Reporter: Gary Hu <ghu>
Component: HibernateAssignee: Gail Badner <gbadner>
Status: CLOSED NOTABUG QA Contact: Martin Simka <msimka>
Severity: high Docs Contact: Russell Dickenson <rdickens>
Priority: high    
Version: 6.2.0CC: msimka, smarlow
Target Milestone: ---   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2014-08-08 21:25:55 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
hibernate.test.01121653.namedquery.zip none

Description Gary Hu 2014-08-07 20:25:21 UTC
The following error occurs in @NamedQuery if the annotations are put on property(getter) not on the field(member variable).
ERROR org.hibernate.internal.SessionFactoryImpl - HHH000177: Error in named query: myNamedQuery
org.hibernate.QueryException: could not resolve property: xxx of: xxxx [from EntityA a where a.entityB.someId = :someId ]
at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:83)
at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:77)
at org.hibernate.persister.entity.AbstractEntityPersister.toType(AbstractEntityPersister.java:1968)
at org.hibernate.hql.internal.ast.tree.FromElementType.getPropertyType(FromElementType.java:313)
at org.hibernate.hql.internal.ast.tree.FromElement.getPropertyType(FromElement.java:490)
at org.hibernate.hql.internal.ast.tree.DotNode.getDataType(DotNode.java:616)
at org.hibernate.hql.internal.ast.tree.DotNode.prepareLhs(DotNode.java:267)
at org.hibernate.hql.internal.ast.tree.DotNode.resolve(DotNode.java:214)
at org.hibernate.hql.internal.ast.tree.DotNode.resolveFirstChild(DotNode.java:178)
at org.hibernate.hql.internal.ast.HqlSqlWalker.lookupProperty(HqlSqlWalker.java:578)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.addrExpr(HqlSqlBaseWalker.java:4719)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1260)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4426)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:3898)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2076)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:794)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:595)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:299)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:247)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:250)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:185)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:138)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:105)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:168)
at org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:1083)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:520)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1794)


Example code snippets and a workable reproducer could be found from https://hibernate.atlassian.net/browse/HHH-9256

Comment 1 Gail Badner 2014-08-07 22:10:14 UTC
This is not a bug.

The test does not specify an explicit access type, so the section of the JPA spec, "Default Access Type", applies. It says:

"All such classes in the entity hierarchy whose access type is defaulted in this way must be consistent in their placement of annotations on either fields or properties, such that a single, consistent default access type applies within the hierarchy."

EntityA does not comply with this as it has a field and a property annotated. 

The test will pass if the EntityA class is annotated with:

  @Access(value=AccessType.PROPERTY)

and the entityB field is explicitly annoated with:

  @Access(value=AccessType.FIELD)

Comment 2 Gary Hu 2014-08-08 14:36:16 UTC
Hi Gail,

The test case I provided didn't specify a consistent way of annotation. 

After making all of the annotations on the property(getter) in both EnityA and EntityB classes, the original error goes away. 

Thanks for pointing this out.

However, our customer actually runs into a different issue here.

Please note that the variable name of EntityB that is referenced in EntityA is in a camel case format. 
In my example,

        private EntityB entityB;
	
	@ManyToOne @JoinColumn(name="ENTITY_B_ID")
	public EntityB getEntityB() {
		return this.entityB;
	}

	public void setEntityB(EntityB entityB) {
		this.entityB = entityB;
	}

If this variable name is changed to something like:
  private EntityB eB; 
 // or anything starts with a single lower case letter + capital letter
 // such as eBenity, eBxx, etc
 //the @NamedQuery would be 
 //    query = "from EntityA s where s.eB.entityBId = :entityBId")
then the original error "could not resolve property" comes back again.

Comment 3 Gary Hu 2014-08-08 17:13:13 UTC
Created attachment 925253 [details]
hibernate.test.01121653.namedquery.zip

attached the updated test case project that 

1) make all of the annotations consistent on the property(getter)

2) change the variable name to
     private EntityB eB;
   in EntityA; and namedquery to 
       query = "from EntityA s where s.eB.entityBId = :entityBId")

This updated project could demonstrate that the following error would show after the changes:
   ERROR HHH000177: Error in named query: getEntityBById
org.hibernate.QueryException: could not resolve property: eB of: gss.hibernate.test.entity.annotations.EntityA [from gss.hibernate.test.entity.annotations.EntityA s where s.eB.entityBId = :entityBId]

Comment 4 Gail Badner 2014-08-08 21:17:33 UTC
This is not a bug either.

According to the JPA 2.1 spec, section "2.2 Persistent Fields and Properties":

"It is required that the entity class follow the method signature conventions for JavaBeans read/write properties (as defined by the JavaBeans Introspector
class) for persistent properties when property access is used."

and in the footnotes:

"[2] Specifically, if getX is the name of the getter method and setX is the name of the setter method, where X is a string, the name of the persistent property is defined by the result of java.beans.Introspector.decapitalize(X)."

According to the Javadoc for java.beans.Introspector.decapitalize(String name):

"Utility method to take a string and convert it to normal Java variable name capitalization. This normally means converting the first character from upper case to lower case, but in the (unusual) special case when there is more than one character and both the first and second characters are upper case, we leave it alone.

Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays as "URL". "

For this test, the query should be: 

"from EntityA s where s.EB.entityBId = :entityBId"