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.aliasToBean(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.
Related
I have a database table where one of the columns contain an enum
Entity file:
#Entity
#Table(name = "error_log_entry")
public class ErrorLogEntryEntity extends AbstractPersistable<UUID> {
#Id
private UUID id;
#Column(name = "priority")
#Enumerated(EnumType.STRING)
private ErrorLogEntryPriorityType priority;
}
Enum file
public enum ErrorLogEntryPriorityType {
INFO,
WARN,
DANGER
}
Now I am trying to write a query that counts the number of rows where the enum is equal to "DANGER"
public interface ErrorLogEntryRepository extends JpaRepository<ErrorLogEntryEntity, UUID> {
List<ErrorLogEntryEntity> findByChangedUserId(UUID userId);
#Query(nativeQuery = true, value = "select count(*) from error_log_entry where priority = 'DANGER'")
ErrorLogEntryPriorityType getErrorCount();
}
However, this causes the following error
java.lang.ClassCastException: class java.math.BigInteger cannot be cast to class project.core.types.ErrorLogEntryPriorityType
I am a bit new to spring, but from the documentation, this problem seems to occur because I am casting on an incompatible type. I am not 100% sure.
Any suggestions on how to make this query work?
As mentioned in the comments, the error is because the return type of the query method you've defined is incorrect. You're not looking to get back an instance of ErrorLogEntryPriorityType, you need to get back a numerical value - the count of how may records match the query.
Having said that, there's an even easier, cleaner solution - use Spring Data's "derived query method" and you don't even need to write the #Query yourself. For example, this should work as you intend:
long countByPriority(ErrorLogEntryPriorityType priority);
Note there is no #Query annotation; Spring Data understands the mapping of your entity and generates the query for you based on the method name and parameter types.
This method is also more flexible; the application can use it to count all records with any of the values for priority, not just DANGER. Generally speaking, it's good to have the low-level repository methods be building blocks, like Lego. The application can then assemble a wide variety of "things" from those basic building blocks, such as a service method that counts how many DANGER log records there are.
I suggest you read all the reference docs for Spring Data repositories, starting here.
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.
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.
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.
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.