Object as parameter results in exception

#1

Hi,

   I've got a simple JPA2 Criteria query which fails when doing an object equality. I verified this against EclipseLink and over there, it works. I'll Attach some code snippets to help figure it out:

 

@Entity()
public class Product{
   .
   .
   .
    @ManyToOne
    private Compamy owningCompany;
}

@Entity()
public class Company{
   .
   .
   .
    @Id
    private Long id; 
}


and the query:

 

 @Override
 public T fetch(Company company, Long primaryKey) {
  mLogger.info("Fetching type: {} with id {} with company: {}", new Object[]{managedClass, primaryKey, company});
  CriteriaBuilder qb = getEntityManager().getCriteriaBuilder();
  CriteriaQuery<T> c = qb.createQuery(managedClass);
  Root<T> p = c.from(managedClass);
  c.select(p);
  c.where(
    qb.equal(p.get("id"), primaryKey),
    qb.and(qb.equal(p.get("owningCompany"), company)));
  TypedQuery<T> q = getEntityManager().createQuery(c);
  List<T> resultList = q.getResultList();
  if(resultList.size() > 0){
   return resultList.get(0);
  }
  return null;
 }

The error I get is:

Caused by: com.objectdb.o.UserException: Unsupported query operator '@'
 at com.objectdb.o.MSG.d(MSG.java:61)
 at com.objectdb.o.TKR.t(TKR.java:383)
 at com.objectdb.o.TKR.r(TKR.java:149)
 at com.objectdb.o.TKI.<init>(TKI.java:53)
 at com.objectdb.o.QSP.<init>(QSP.java:77)
 at com.objectdb.o.QPR.H(QPR.java:979)
 at com.objectdb.o.QPR.<init>(QPR.java:102)
 at com.objectdb.o.QRC.<init>(QRC.java:116)
 at com.objectdb.o.QRM.UR(QRM.java:242)
 at com.objectdb.o.MST.UR(MST.java:878)
 at com.objectdb.o.WRA.UR(WRA.java:286)
 at com.objectdb.o.WSM.UR(WSM.java:113)
 at com.objectdb.o.STC.r(STC.java:418)
 at com.objectdb.o.SHN.ah(SHN.java:468)
 at com.objectdb.o.SHN.J(SHN.java:146)
 at com.objectdb.o.HND.run(HND.java:133)
 at java.lang.Thread.run(Thread.java:680)

Looking at the log:

[2011-04-28 04:18:07 #1900 query.manager]
<queryRequest query="SELECT $1 FROM Product $1 WHERE ($1.id=8) AND $1.owningCompany=com.x.y.z.Company@540ef9ef" args="null" transactionId="-1" />

So it seems, that it is doing a 'Company.toString()' on the instance instead of resolving the id? Have I misunderstood something again? Under eclipselink, this did work (going to PostgreSQL).

If I change my path expressions to point to basic java types (path expression pointing to id):

 

 @Override
 public T fetch(Company company, Long primaryKey) {
  mLogger.info("Fetching type: {} with id {} with company: {}", new Object[]{managedClass, primaryKey, company});
  CriteriaBuilder qb = getEntityManager().getCriteriaBuilder();
  CriteriaQuery<T> c = qb.createQuery(managedClass);
  Root<T> p = c.from(managedClass);
  c.select(p);
  c.where(
    qb.equal(p.get("id"), primaryKey),
    qb.and(qb.equal(p.get("owningCompany").get("id"), company.getId())));
  TypedQuery<T> q = getEntityManager().createQuery(c);
  List<T> resultList = q.getResultList();
  if(resultList.size() > 0){
   return resultList.get(0);
  }
  return null;
 }

 

I don't know if it's a bug, I just need help :)

 

Thanks

 

ObjectDB Version: 2.2.2_04

#2

This is an interesting question. It is unclear if your query is valid since objects such as a Company instance are expected to be passed to the query as parameters. Maybe EclipseLink defines an implicit parameter to resolve this query, and maybe this is what ObjectDB should do.

Using a parameter is also recommended for the primary key. In the current form the primary key is used as a literal, which means that the query has to be compiled every time and the query program cache cannot be used. Maybe ObjectDB should optimize such queries replacing literals with parameters automatically, whenever possible.

Currently, to solve the problem and improve efficiency please consider:

  • Building the query (the CriteriaQuery and the TypedQuery) once with two parameters.
  • Running the query by using the same prepared TypedQuery every time, setting two arguments.
ObjectDB Support
ObjectDB - Fast Object Database for Java (JPA/JDO)
#3

Yes that makes complete sense. Still migrating an application from JPA1 to JPA2 - and tat the same time making it run with ObjectDB.

So my solution now, is:

 

 @Override
 public T fetch(Company company, Long primaryKey) {
  mLogger.info("Fetching type: {} with id {} with company: {}", new Object[]{managedClass, primaryKey, company});
  CriteriaBuilder qb = getEntityManager().getCriteriaBuilder();
  CriteriaQuery<T> c = qb.createQuery(managedClass);
  Root<T> p = c.from(managedClass);
  c.select(p);

  ParameterExpression<Long> idParam = qb.parameter(Long.class);
  ParameterExpression<Company> companyParam = qb.parameter(Company.class);
 
  c.where(qb.equal(p.get("id"), idParam),
    qb.and(
      qb.equal(p.get("owningCompany"), companyParam)));
   
  TypedQuery<T> q = getEntityManager().createQuery(c);
  q.setParameter(idParam, primaryKey);
  q.setParameter(companyParam, company);
 
  List<T> resultList = q.getResultList();
  if(resultList.size() > 0){
   return resultList.get(0);
  }
  return null;
 }

 

So you recommend us to hold on to the TypedQuery? I see the value in not having to repeatedly execute the Criteria definitions. So ObjectDB would indeed cache this query the way it is?

Please ignore my original post - the literals are a pretty bad idea! Most of our queries will still be named queries, just our 'search' queries will be coded as Criteria queries.

#4

Your original post is of a great value since it highlights an important point and introduces the idea of replacing literals with parameters automatically - this is something that ObjectDB should do as optimization in next versions.

Your new implementation is good and since repeating invocations generate the same query - it will not be compiled by ObjectDB on every call and cache will be used.

Additional optimization can be achieved by:

  • Reusing a CriteriaQuery - creating it once per EntityManagerFactory.
  • Reusing a TypedQuery - creating it once per EntityManager (relevant when using long term EntityManager).

but the effect of this micro optimization is expected to be small, and irrelevant for criteria queries which are dynamic (i.e. the query structure changes between invocations).

ObjectDB Support
ObjectDB - Fast Object Database for Java (JPA/JDO)
#5

Are there any plans to implement the automatic replacement feature? I've run into the same issue but cannot rewrite the queries as they are created and handled by a third party library (I only pass the entity classes). My only option at the moment is to switch back to eclipselink/rdbms no

 

 

ObjectDB version:  2.2.3_09

 

#6

This is now implemented in version 2.2.4. Please try it.

ObjectDB Support
ObjectDB - Fast Object Database for Java (JPA/JDO)
#7

I can now confirm that it works! Thank you very much for the prompt resolution.
 


Post Reply

To post a reply and/or subscribe to update notifications - please login