JPQL Native Query using SUM and COUNT - java

I am trying to run the following code:
public BigDecimal valuate(String searchTerms, String categoryPath) {
Query query = em.createNativeQuery("SELECT SUM(maxBidAmount) / COUNT(maxBidAmount) FROM Item WHERE MATCH(title) AGAINST(':searchTerms') AND categoryPath=':categoryPath'", Double.class);
query.setParameter("searchTerms", searchTerms);
query.setParameter("categoryPath", categoryPath);
double value = (double) query.getSingleResult();
return new BigDecimal(value);
}
When I do so, I get the following exception:
Exception Description: Missing descriptor for [class java.lang.Double].
When I remove Double.class, I get a different exception.
So, I'm just wondering the correct method of using COUNT and SUM with JPQL.

IF the SQL is valid, you do not need to specify the Double.class in the query def - just use em.createNativeQuery(SQLString);
The return type is used when you want the JPA provider to build an entity from the results, but in this case you want the raw data.

Native query is SQL, not JPQL, and you use those keywords just like any SQL for your RDBMS.
Looks like your JPA provider doesn't accept Double as a result class (some only allow the result class to be an Entity).
DataNucleus JPA certainly allows non-Entity result classes.

Related

Correct cast in ResultTransformer

I am using Hibernate result transformer for fetching list of results from the database as follows:
Query query = em.createNativeQuery("SELECT 1 as myTrueBoolean, 2 as myInt")
query.unwrap(org.hibernate.Query.class).setResultTransformer‌​(Transformers.aliasT‌​oBean(myDataClass));
query.fetchResultList()
Than I could define a data class as follows:
class MyDataClass {
boolean myTrueBoolean;
int myInt;
}
The problem is that the transformer will not cast correctly the data as it will assign BigInteger as boolean (IllegalArgumentException occurred while calling setter for property [MyDataClass.myTrueBoolean (expected type = boolean)]; target = MyDataClass, property value = [0]]) and the same with assigning BigInteger as int. This wouldn't be a problem with general Hibernate entities.
EDIT:
I am not looking for explanation why it does not work. I am looking for a way to making it work :-) I need this for my native queries. Is there maybe a way of implementing own transformer which would achieve this?
For native SQL queries you get whatever the database returns. That is kind of the point with native SQL queries. For MySQL it returns BigInteger ,but for MsSQL it can return Integer.
Change your class to
class MyDataClass {
Number myTrueBoolean;
Number myInt;
}
Another option is to use JPA queries. They will return types of values from your entities.

Writing a custom HSQLDialect to change Hibernate return type for uniqueResult()

I'm using Hibernate method uniqueResult() to get the count of records in a table
"SELECT COUNT(*) AS C FROM TABLE";
In tests we use HSQLDB in-memory or localhost databases and uniqueResult() returns a java.math.BigInteger for count(*). But in the real environment we use DB2 where uniqueResult() returns java.lang.Integer. So when I run my tests I get a ClassCastException java.math.BigInteger cannot be cast to java.lang.Integer on the following code:
SQLQuery q = getSession(false).createSQLQuery(query);
Integer count = (Integer) q.uniqueResult();
There is a fix I found to specify the return type by adding the addScalar() method:
SQLQuery q = getSession(false).createSQLQuery(query).addScalar("C", Hibernate.INTEGER);
But is there a way I can make the default behavior for the return type change across the board? Perhaps by writing a custom HSQLDialect that would specify all count values be returned as an Integer?
Try using select count(somecolumn) instead of select count(*). You may find the data type returned is the same type for both databases. I think the returned type is related to the type of the column used in the count expression.

Hibernate - How to execute named native query without mapping to a specific class

I have a very complicated query that doesn't convert to HQL, so I must use SQL. I put my query into a NamedNativeQuery annotation. It "should" return a list of two values (List<Object[2]>). When I try to run, the session fails to load the query because I haven't defined a mapping or result class. I cannot create a simple class to map these values to because the annotations have to go in a specific DAO project and the code that is using the queries exists in its own project. (The DAO project is more general-purpose and the entities in that project map directly to tables in our database. I cannot just create a small class in this project to map to my query result because it wouldn't fit the schema of this project)
Is there a way for me to map the query results to a more generic class, or even better is there some way for me to just get a List<Object[]> back without having to map at all to anything particular? This is extremely frustrating that Hibernate has to work in this particular way when all I want to do is execute a query and get the result back as an array.
This is the code that I use for storing the result of a SQL query in a List<Object[]>. Maybe you can adapt it to what you need:
public List<Object[]> executeSelectQuery(String sqlQuery) {
List<Object[]> cleanedResults = new ArrayList<Object[]>();
SQLQuery query = sessionFactory.getCurrentSession().createSQLQuery(sqlQuery);
List<Object[]> hibernateResults = query.list();
// Hibernate does not return always a List<Object[]>, but a list of strings or integers, so it is necessary to check the returned values
if (!hibernateResults.isEmpty()) {
if (hibernateResults.get(0) instanceof Object[]) {
cleanedResults = hibernateResults;
} else {
Object[] row;
// Use a 'for' because 'foreach' sometimes has casting exceptions converting to object
for (int i = 0; i < hibernateResults.size(); i++) {
row = new Object[1];
row[0] = hibernateResults.get(i);
cleanedResults.add(row);
}
}
}
return cleanedResults;
}
A NamedNativeQuery supports an addition annotation to allow you map the result to a POJO:
#SqlResultSetMapping(name="MyResult.Mapping", entities = {
#EntityResult(entityClass=MyResult.class, fields = {
#FieldResult(name="email", column="email"),
#FieldResult(name="name", column="name")
})
})
Then add resultSetMapping="MyResult.Mapping" to your NamedNativeQuery annotation.
I ended up not using NamedNativeQueries because they weren't working for me. Instead, I just used session.createSQLQuery with constant Strings, and it worked.
I probably didn't phrase my question properly. I could not use a #SqlResultSetMapping because I didn't have any classes to map to based on the structure of the code I am working with. My query has to come out as a List<Object[]> result, and there is no way to do that with NamedNativeQueries because Hibernate doesn't support it.

java.sql.SQLException: ORA-00932: inconsistent datatypes: expected NUMBER got BINARY

I have a method in Dao Class that returns List<Object[]> back and I am using named Query
public List<Object[]> getListByCustomer(Session session, int customerId, List<Integer> strIds) {
Query namedQuery = session.createSQLQuery(QueryConstants.EXPORT);
namedQuery.setParameter("customer", customerId);
namedQuery.setParameter("stringId", strIds);
List<Object[]> objects = namedQuery.list();
return objects;
}
I want to pass List<Integer> strIds in stringId into the named query as follows :
public class QueryConstants {
public static final String EXPORT =
"SELECT sv.NAME, sv.TYPE, sv.CLIENT_ADDRESS, sv.NAME_REDUNDANT, sv.DEPARTURE_DATE, s1.CODE,sv.STATE, sv.CODE "
+ "FROM VIEW sv, PROCESS p1, SET s1 "
+ "WHERE sv.R_ID = p1.R_ID and p1.ISSUER_ID = s1.USER_ID and sv.CUSTOMER_ID = :customer and sv.R_ID IN (:stringId)";
}
But I get ORA-00932: inconsistent datatypes: expected NUMBER got BINARY.
Also when I remove sv.R_ID IN (:stringId) from the query it works fine and
when I pass Integer(strIds) instead of List<Integer> strIds into the query it works fine.
I'm using Oracle 10g.
This is a very misleading error, and may root from different causes, for me I was setting a parameter that it was supposedly a number but at runtime it was setting null, hence it was binary. On a separate occasion got this error due to bean creation error in spring and was not setting the parameter correctly as well.
I think you just need to use
IN :stringId
instead of
IN (:stringId)
For JPA
namedQuery.setParameter("stringId", strIds);
is correct, but for Hibernate you should use
namedQuery.setParameterList("stringId", strIds);
I encountered this same exception and found the below reason for that -
In my entity, a field was mapped to a custom object (Parent child relationship - #ManyToOne). Later, the relationship annotation was removed by developer but the datatype was not changed.
After removing the #ManyToOne annotation, the #Column annotation should have been used with appropriate data type (Integer).
Case your param is list. If list is empty then raise error, you must check that list not empty to avoid error.
Case your param is single value. Let use TO_NUMBER(:your_param) to avoid error.
It working on me.
In my case, I was using HQL (in the repository of spring data) with an Entity mapped with #Enumerated (ORDINAL). I was trying to use the the enum object directly in the where clausule. The solution was to use TO_NUMBER(:your_param) as mentioned by the member above.
I got the same error but for a different reason. In my case it was due to the order the parameters were supplied being different from the order defined in the query. I assumed (wrongly) that because the parameters were named the order didn't matter. Sadly, it seems like it does.

Simple jpa question

I have to go through some code of a project to implement some missiong functionality.
It uses jpa.In some popj classes i have found
#Entity
#Table(name="TYPE")
#NamedQueries( {
#NamedQuery(name = "getTypes", query = "SELECT dct FROM Type dct")
})
I know that i can used to get all records by using this query.Does this query return all records in type table?
This query will return all the Type entities including subtypes, if any. And since I can't say if this there are any subtypes, I can't say if this query will be restricted to the TYPE table.
Yes, it does. It generates an SQL query that looks roughly like this:
SELECT [column list here] FROM type

Categories