Creating queries using JPQL -- Query Syntax Exception - java

When I run the method: dao.query("SELECT p FROM Profile p WHERE p.group = :id ORDER BY p.datestamp :key", map); I get the following error:
org.hibernate.hql.ast.QuerySyntaxException:
unexpected token: : near line 1,
column 93 [SELECT p FROM Profile p
WHERE p.group = :id ORDER BY
p.datestamp :key]
Following is the query method implemenation; anyone see what is wrong?
public List<?> query(String criteria, HashMap<String, ?> args) {
Query sqlQuery = this.em.createQuery(criteria);
Set<String> keys = args.keySet();
Iterator<String> iter = keys.iterator();
while (iter.hasNext()) {
String key = iter.next();
sqlQuery.setParameter(key, args.get(key));
}
return sqlQuery.getResultList();
}

You cannot use parameters to specify sorting direction, because parameter cannot be used in arbitrary places of the query. From JPA spec:
Input parameters can only be used in the WHERE clause or HAVING clause of a query.
So, in JPA 1.0 you have to build query string with appropriate ORDER clause manually.
In JPA 2.0 you can use Criteria API to construct dynamic queries.

I think you need a comma after ORDER BY p.datestamp and before :key

Related

Translating HQL Query to executable SQL Query with list parameter

I am writing a util function to get the total record count based on any HQL that I get passed in without loading all data. The passed in HQL might be pretty complex with lots of selects, joins, where conditions, groupings and sortings. For that I want to wrap the query with a SELECT COUNT(*) FROM (<ORIGINAL_QUERY>) x. I found out, that this is not possible with HQL, because Hibernate does not allow subqueries in FROM elements.
Now, I am trying to translate this random HQL query which might have some named parameters (some of them might be simple parameters, some might be lists) to an executable SQL statement without inlining the parameter values. This seems to work with simple parameters, but does not work with list parameters.
The hqlString and the namedParameterMap comes from somewhere outside:
final String hqlString = "...";
final Map<String, Object> namedParametersMap = ...;
Code to translate HQL to SQL (and ParameterTranslations:
final QueryTranslatorFactory translatorFactory = new ASTQueryTranslatorFactory();
final SessionFactoryImplementor factory = getSessionFactory();
final QueryTranslator translator = translatorFactory.createQueryTranslator(hqlString, hqlString, Collections.EMPTY_MAP, factory, null);
translator.compile(Collections.EMPTY_MAP, false);
final String sqlString = translator.getSQLString();
final ParameterTranslations parameterTranslations = translator.getParameterTranslations());
Code to create a SQLQuery, set the Parameters and execute the Query:
SQLQuery sqlQuery = getCurrentSession().createSQLQuery(sqlString);
((Set<?>) parameterTranslations.getNamedParameterNames()).forEach(parameterName -> {
for (int position : parameterTranslations.getNamedParameterSqlLocations(parameterName)) {
sqlQuery.setParameter(position, namedParametersMap.get(parameterName));
}
});
final Long rowCount = ((BigInteger) query.uniqueResult()).longValueExact();
This query works an expected:
final String hqlString = "SELECT p.firstname, p.surname FROM Person p WHERE p.id = :param1";
final Map<String, Object> namedParametersMap = Maps.newHashMap(ImmutableMap.<String, Object>of("param1", Integer.valueOf(1));
This query does not work:
String hqlString = "SELECT p.firstname, p.surname FROM Person p WHERE p.id IN (:param1)";
final Map<String, Object> namedParametersMap = Maps.newHashMap(ImmutableMap.<String, Object>of("param1", Lists.newArrayList(Integer.valueOf(1)));
Exception:
org.hibernate.exception.SQLGrammarException: could not extract ResultSet at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79) at org.hibernate.loader.Loader.getResultSet(Loader.java:2313) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2096) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2072) at org.hibernate.loader.Loader.doQuery(Loader.java:941) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:352) at org.hibernate.loader.Loader.doList(Loader.java:2813) at org.hibernate.loader.Loader.doList(Loader.java:2796) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2625) at org.hibernate.loader.Loader.list(Loader.java:2620) at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:322) at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1996) at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:322) at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125) at org.hibernate.internal.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:966) at HQLQueryUtils.getCount(HQLQueryUtils.java:350)
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = bytea HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts. Position: 301 at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2433) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2178) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:306) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365) at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:155) at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:118) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70) ... 49 more
Environment:
Java 8
Hibernate 5.1.8
Postgres-JDBC 42.2.2
PostgreSQL Server 10.5
In HQL you can use query parameter and set Collection with setParameterList method.
Query q = session.createQuery("SELECT entity FROM Entity entity WHERE name IN (:names)");
q.setParameterList("names", names);
You can use direct sql
String sql="your query"
Query query = sessionFactory.getCurrentSession().createSQLQuery(sql);
query.setParameter("paramterName", parameterValue);
List<Object[]> resultSet = query.list();
List<YouClass > data= new ArrayList<>();
for (Object[] row : resultSet) {
YouClass yourObject=new YouClass ();
yourObject.setDate((Date) row[0]);
yourObject.setAmount((BigDecimal) row[1]);
data.add(yourObject);
}
Suppose Yourclass has two element date and amount

How does a native sql query bind fields to an entity instead of map

I tried to run native sql query with resulttransformer (AliasToBeanResultTransformer), it gives error like below.
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: com.ozpas.ozentegre.entity.EntDevirlog cannot be cast to java.util.Map
at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)
at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:78)
By the way, my native sql query does not include all fields in the entity ( EntDevirlog ), there are only some fields in that entity. shall the query include all fields in the entity ?
as i understood, hibernate transforms result into a map object instead EntDevirlog entity. It uses PropertyAccessMapImpl. how can i solve this problem to get the result as a list ( arraylist ) ? thanks.
Session session = HibernateUtilMikro.getSessionFactory().openSession();
List<EntDevirlog> results = new ArrayList<EntDevirlog>();
Transaction tx = null;
String sql = "mynativequery";
SQLQuery query = session.createSQLQuery(sql);
query.setParameter("tarih", tarih);
query.setParameter("srmkodu", srmkodu);
query.setParameter("s1", EnumPanoislemtipleri.islem1.getValue());
query.setParameter("s2", EnumPanoislemtipleri.islem2.getValue());
query.setResultTransformer(new AliasToBeanResultTransformer(EntDevirlog.class));
results = query.list();
tx.commit();
Just use the quotes for the aliases
"select firstName as \"firstName\",
lastName as \"lastName\" from Employee"
Read for a more deeply explanation here:
mapping Hibernate query results to custom class?

Order by attribute of foreign entity in ORMLite

How can I build a query in ORMLite so that I can use the orderBy function (using either the one with the raw string or the parametrized one) referencing an attribute of a different entity than the one of the dao I'm building the query from? My query is built like that:
// Inner query for performances
QueryBuilder<Performance, String> performancesQB = performanceDao.queryBuilder();
performancesQB.selectColumns("performance_id");
SelectArg performanceSelectArg = new SelectArg();
performancesQB.where().lt("date", performanceSelectArg);
// Outer query for Order objects, where the id matches in the performance_id
// from the inner query
QueryBuilder<Order, String> ordersQB = orderDao.queryBuilder();
ordersQB.where().isNull("user_id").and().in("performance_id", performancesQB);
ordersQB.orderByRaw("performances.date DESC");
pastOrdersQuery = ordersQB.prepare();
And the exception I'm getting whenever I try to execute this query is:
android.database.sqlite.SQLiteException: no such column: performances.date:,
while compiling: SELECT * FROM `orders` WHERE
(`user_id` IS NULL AND `performance_id` IN
(SELECT `performance_id` FROM `performances` WHERE `date` < ? ) )
ORDER BY performances.date DESC
The only solution I see here is writing a raw query myself using a JOIN instead of a nested select. May this be a good solution?
ORMLite now supports simple JOIN queries. Here the docs on the subject:
http://ormlite.com/docs/join-queries
So your query would now look something like:
QueryBuilder<Performance, String> performancesQB = performanceDao.queryBuilder();
SelectArg performanceSelectArg = new SelectArg();
performancesQB.where().lt("date", performanceSelectArg);
performancesQB.orderBy("date", false);
// query for Order objects, where the id matches
QueryBuilder<Order, String> ordersQB = orderDao.queryBuilder();
ordersQB.join(performancesQB).where().isNull("user_id");
pastOrdersQuery = ordersQB.prepare();

criteria query ORDER BY yields error. Is this an SQL-SERVER limitation? How could I order by correctly on a complicated criteria query?

I have the following criteria query:
String cat = "H";
Criteria criteria = currentSession().createCriteria(this.getPersistentClass()).
add(Restrictions.ne("category", cat)).
createAlias("employees", "emp").
createAlias("emp.company", "company");
Disjunction disjunction = Restrictions.disjunction();
for(Region r: regions){
disjunction.add(Restrictions.eq("company.region", r));
}
criteria.add(disjunction);
if(status != null) {
criteria.add(Restrictions.eq("status", status));
}
if (period != null) {
criteria.add(Restrictions.eq("period", period));
}
criteria.setProjection(Projections.groupProperty("id")) //this line was added to try to "fix" the error, but it still happened.
criteria.addOrder(Order.asc("id"));
I guess a query that explains my criteria query could be:
select n.* from NOMINATION n
join NOMINEE i on n.NOM_ID = i.NOM_ID
join EMPLOYEE e on e.EMP_ID = i.EMP_ID
join COMPANY c on c.COMPANY_CODE = e.COMPANY_CODE
where n.CATEGORY_CODE!='H' and (c.REGION_ID = ? or c.REGION_ID = ? or c.REGION_ID = ?) and n.STATUS_ID = ? and n.PERIOD_ID = ?
order by n.NOM_ID
What I am trying to do here, is pretty confusing but for the most part it works except when I add this specific line (though the query works fine):
criteria.addOrder(Order.asc("id"));
and then I get error:
java.sql.SQLException: Column "NOMINATION.NOM_ID" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.
Which I suspect is something that has to do with SQL-SERVER. I am already grouping by id. So what am I doing wrong here, or should I just use HQL?
Your current query seems to be a simple Query which doesn't have any group function used or not a group by query. According to your current requirements you do not have to use this line.
criteria.setProjection(Projections.groupProperty("id")).addOrder(Order.asc("id"));
Or you have to modify your sql statements.

Why EclipseLink Query cache only works when I use query.getSingleResult()?

My entity has a named query which looks like this:
#NamedQuery(name = "Person.find", query = "select p from Organization p where p.name=:NAME")
In my code I want to set the query cache hint:
query.setHint("eclipselink.cache-usage", "CheckCacheThenDatabase");
If I try to get whole result list:
List<Person> result = query.getResultList();
EclipseLink throws an exception:
Exception [EclipseLink-6124] (Eclipse Persistence Services - 1.0.1 (Build 20080905)): org.eclipse.persistence.exceptions.QueryException
Exception Description: Required query of class org.eclipse.persistence.queries.ReadAllQuery, found class org.eclipse.persistence.queries.ReadObjectQuery
If I try to get only a single result, it works:
Person person = query.getSingleResult();
If I remove the query hint, then getResultList() works also.
I don't understand the exception - isn't it saying that it is specifically expecting getResultList()?? What am I doing wrong?
EclipseLink documentation says:
"EclipseLink does not support the cache usage for native queries or queries that have complex result sets such as returning data or multiple objects."
Also the documentation says:
"CheckCacheThenDatabase – You can configure any read-object query to check the cache completely before you resort to accessing the database."
So the behaviour seems to be ok, I just found the exception misleading.
EDIT: Try something like this in the entity definition, that should be enough: (Coded on the web page so there can be errors)
Entity
#Cache(expiry = 3600000, // 1 hour size = 10000)
#NamedQueries({
#NamedQuery(name = "Person.findByPK", query = "select p from Person p " +
"where p.name=:NAME",
hints = {
#QueryHint(name = QueryHints.CACHE_USAGE,
value = CacheUsage.CheckCacheThenDatabase),
#QueryHint(name = QueryHints.QUERY_TYPE, value = QueryType.ReadObject)
}
})

Categories