0

I'm learning Criteria API. I am trying to rewrite all my JPQL queries to Criteria API queries.

I use sakila database (http://dev.mysql.com/doc/sakila/en/), MySQL database and HibernatePersistence JPA provider.

I have got stuck in rewriting following query:

SELECT c FROM Customer c 
JOIN c.addressId a 
JOIN a.cityId ct 
WHERE ct.countryId IN (SELECT co FROM Country co WHERE co.country = 'Germany')

It works fine, but the same in Criteria API doesn't work:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> c = cb.createQuery(Customer.class);     
Root<Customer> cust = c.from(Customer.class);
Join<Customer, Address> address = cust.join(Customer_.addressId);
Join<Address, City> city = address.join(Address_.cityId);

Subquery<Country> sq = c.subquery(Country.class);
Root<Country> country = sq.from(Country.class);
sq.select(country).where(cb.equal(country.get(Country_.country), "Germany"));
c.where(cb.in(city.get(City_.countryId)).value(country));

I have got the following exception:

javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:277)
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler$3.getResultList(CriteriaQueryCompiler.java:254)
    at net.bean.java.sakila.test.SakilaQueries.inTest(SakilaQueries.java:100)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:82)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:88)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:2062)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1859)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1838)
    at org.hibernate.loader.Loader.doQuery(Loader.java:906)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:348)
    at org.hibernate.loader.Loader.doList(Loader.java:2548)
    at org.hibernate.loader.Loader.doList(Loader.java:2534)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2364)
    at org.hibernate.loader.Loader.list(Loader.java:2359)
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:495)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:357)
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:195)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1194)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:268)
    ... 29 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'generatedAlias3' in 'where clause'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2323)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79)
    ... 44 more

The query which is produced by Hibernate looks like:

select 
customer0_.customer_id as customer1_5_, 
customer0_.active as active2_5_, 
customer0_.address_id as address_8_5_, 
customer0_.create_date as create_d3_5_, 
customer0_.email as email4_5_, 
customer0_.first_name as first_na5_5_, 
customer0_.last_name as last_nam6_5_, 
customer0_.last_update as last_upd7_5_, 
customer0_.store_id as store_id9_5_ 

from customer customer0_ 
inner join address address1_ on customer0_.address_id=address1_.address_id 
inner join city city2_ on address1_.city_id=city2_.city_id 

where city2_.country_id=generatedAlias3;

Thank you in advance.

  • @MahmoudGamal: it is a valid JPQL query. JPQL ain't the same thing as SQL. – JB Nizet Dec 22 '13 at 13:21
  • @JBNizet - Sorry didn't notice that. I thought it is a normal SQL query. – Mahmoud Gamal Dec 22 '13 at 13:24
  • Not much experienced with Criteria subqueries, but shouldn't it be `c.where(city.get(City_.countryId).in(sq.getSelection()));`. Also, your naming is extremely confusing. Don't name `xxxId` something that is not an ID. `Customer.addressId` should be `Customer.address`. `Address.cityId` should be `Address.city`. `City.countryId` should be `City.country`. `Country.country` should be `Country.name`. – JB Nizet Dec 22 '13 at 13:32
  • http://stackoverflow.com/questions/4483576/jpa-2-0-criteria-api-subqueries-in-expressions shows that it should be `c.where(cb.in(city.get(City_.countryId)).value(sq));` – JB Nizet Dec 22 '13 at 13:39
  • I know that may naming is confusing. I don't like "Id" postfixed either. Java classes were generated from schema using Netbeans tool. – CodeGrinder Dec 22 '13 at 16:17
  • I have check the last option: c.where(cb.in(city.get(City_.countryId)).value(sq)); It works awsome. Thank you! – CodeGrinder Dec 22 '13 at 16:37
  • The last option works for me. Thank You. – CodeGrinder Dec 22 '13 at 20:48

0 Answers0