Specifying a list of fields in HQL doesn't seem to work - spring

I have the following HQL in Hibernate using Spring MVC.
List<Colour>list=session.createQuery("from Colour order by colourId desc")
.setFirstResult((currentPage-1)*rowsPerPage)
.setMaxResults(rowsPerPage).list();
It works and returns a list of rows from the colour table (actually operates upon the Colour entity (POJO) that I can understand) in Oracle 10g.
What if I need to retrieve a list fields, I'm trying the following.
List<Colour>list=session.createQuery("colourId, colourName, colourHex from Colour order by colourId desc")
.setFirstResult((currentPage-1)*rowsPerPage)
.setMaxResults(rowsPerPage).list();
It ends with an excpetion
java.lang.IllegalArgumentException: node to traverse cannot be null!
In some articles, it was mentioned that the following version of HQL should (or may) work
List<Colour>list=session.createQuery("select colourId, colourName, colourHex from Colour order by colourId desc")
.setFirstResult((currentPage-1)*rowsPerPage)
.setMaxResults(rowsPerPage).list();
but unfortunately, it also didn't work for me. Using the createSQLQuery() method to execute native SQL would work but I want to stick to the createQuery() method with HQL unless it's absolutely necessary. How can I specify a list of fields in HQL?

I agree with yorkw's comment. If you select properties in your query then you cannot ask for a List<Colour> object to be returned from a call to .list().
Instead you should do this
List<Object[]> rows = session.createQuery("select c.colourId, c.colourName, c.colourHex " +
" from Colour c " +
" order by c.colourId desc").list();
Then iterate over the list object and instantiate your objects. Or whatever you need to do.
for ( Object[] row : rows ) {
Long colourId = (Long)row[0];
// ... etc
}

Why don't you try creating a map? Something like this:
SELECT NEW MAP( colour.colourId AS id
, colour.colourName AS name ...)
FROM Colour colour
ORDER BY colour.colourId
I use the alias for Colour "colour" so hibernate knows from which entity is the property I am referencing, I am implying all those properties are from the same entity, if not, then check your referencing!

Related

Spring And Kotlin Query

How can I achieve this query
select *
from table t
where name like '%Ami%'
order by (name = 'Ami') desc, length(col);
(just the sort part)
Using springframework Sort..
What I tried is
Sort.by(Sort.Direction.DESC, "name") // But I need to sort by name = 'Ami'
Sort.by(Sort.Direction.DESC, "name" = 'Ami'") // throws an error
JpaSort.unsafe(Sort.Direction.DESC, "name" = 'Ami'") // throws an error
Looks like the documentation has an example almost identical to your question:
https://docs.spring.io/spring-data/jpa/docs/2.4.5/reference/html/#jpa.query-methods.sorting
However, using Sort together with #Query lets you sneak in
non-path-checked Order instances containing functions within the ORDER
BY clause. This is possible because the Order is appended to the given
query string. By default, Spring Data JPA rejects any Order instance
containing function calls, but you can use JpaSort.unsafe to add
potentially unsafe ordering.

Spring JPA with native query and data projection mapping the wrong columns into the projected interface

I've got a bit of a bizarre problem that I can't figure out why it's happening. I'm sure I did something wrong, because this is my first time using a data projection and I've never had such problems using DTOs.
Pretty much I have a SELECT statemen that is returning certain columns of various data types. And I have an interface that I'm passing to the JPA Repository so it can do the interface mapping. But instead of mapping the results based on the column name (eg. 'accountnum' -> getAccountnumber()), it's mapping the columns in alphabetical order. So if 'date_of_order' is the first in the SELECT statement, its value will be returned by getAccountnumber().
I have a projected interface that looks something like this:
public interface FlatSearchResult {
String getAccountnumber();
UUID getTrackingId;
Date getDateOfOrder;
}
My model has three tables something like this:
ACCOUNT
- account_id : uuid (pkey)
- accountnumber : string
ORDERS
- order_id : uuid (pkey)
- date_of_order : timestamp
- account_id : uuid (fkey)
TRACKING
- tracking_id : uuid (pkey)
- order_id : uuid (fkey)
There's other columns in each of those tables, but they're not relevant.
I have a repository defined with a simple query:
public interface OrderTrackingRepository extends JpaRepository<Account, UUID> {
#Query( nativeQuery = true,
value = "SELECT o.date_of_order, a.accountnumber, t.tracking_id " +
"FROM account as a " +
"INNER JOIN orders as o USING (account_id) " +
"INNER JOIN tracking as t USING (tracking_id) " +
"WHERE a.accountnumber = :acctnum")
<T> Collection<T> findOrderInfoForAccount(#Param("acctnum") acctNumber, Class<T> type);
}
When I call this method, the correct rows are returned by the query. But instead of mapping using the column name (eg. date_of_order to getDateOfOrder()), it is mapping based on the order of the columns in the SELECT statement to the alphabetically-ordered methods in the interface.
So:
SELECT date_of_order, accountnumber, tracking_id
Results in:
getAccountNumber() -> date_of_order
getDateOfOrder() -> accountnumber
getTrackingId() -> tracking_id
It will consistently return in this fashion, so it's not a transient issue.
As a temporary workaround, I've reordered the columns in my SELECT statement. But I would rather not have to do this since it's like iterating through a result set and relying on column position, which just makes me twitchy....
How can I get Spring JPA to map from the result set to my interface? Do I need to annotate my projection interface's methods with something to tell Spring what column name it's referring to?
My database is Postgres. I'm using Spring 5.0.2.RELEASE and Spring-Boot 2.0.0.M7. I can adjust either of those to newer versions if needed, but nothing older. I'm using C3P0 0.9.5.2 for my connection pooling, and postgres-9.2-1002.jdbc4. All my other dependencies (hibernate, etc) are what is pulled in by this version of Spring-Boot.
Not sure if this is the correct solution because it only fits 80% of the description. But it is too long for a comment. So here we go.
I think you misunderstood #osamayaccoub or the documentation. Your property name is fine. But the columns in your select should match the java convention.
So the first attempt to fix that would be
value = "SELECT o.date_of_order as dateOfOrder, a.accountnumber as accountNumber, t.tracking_id as trackingId "
Note: This might actually work, but might break later, so read on, even if it does work
But Postgres converts everything that isn't double quoted into lower case (Oracle and MySql do similar stuff though details vary, don't know about other DBs yet). So you really should use:
value = "SELECT o.date_of_order as \"dateOfOrder\", a.accountnumber as \"accountNumber\", t.tracking_id as \"trackingId\" "
This probably doesn't work, because the Hibernate version you are using has a bug in that it converted everything to lower case.
So you should upgrade to the latest Hibernate version 5.3.13 which has the issue fixed.
This bug fix interestingly might break the version without the double quotes.
But it should work again with this PR for this Spring Data JPA issue.
The part I don't understand is, why stuff gets assigned using the column order.
I had the same problem and i solved by odering the query columns alphabetically.
In you case:
public interface OrderTrackingRepository extends JpaRepository<Account, UUID> {
#Query( nativeQuery = true,
value = "SELECT a.accountnumber, o.date_of_order, t.tracking_id " +
"FROM account as a " +
"INNER JOIN orders as o USING (account_id) " +
"INNER JOIN tracking as t USING (tracking_id) " +
"WHERE a.accountnumber = :acctnum")
<T> Collection<T> findOrderInfoForAccount(#Param("acctnum") acctNumber, Class<T> type);
}
So you will get:
getAccountNumber() -> accountnumber
getDateOfOrder() -> date_of_order
getTrackingId() -> tracking_id
Hibernate sorts the query in alphabetic order so you have to change the select to: "SELECT a.accountnumber, o.date_of_order, t.tracking_id ..." and the interface's getters should follow the same alphabetic order.

SimpleJpaRepository Count Query

I've modified an existing RESTful/JDBC application i have to work with new features in Spring 4... specifically the JpaRepository. It will:
1) Retrieve a list of transactions for a specified date. This works fine
2) Retrieve a count of transactions by type for a specified date. This is not working as expected.
The queries are setup similarly, but the actual return types are very different.
I have POJOs for each query
My transactions JPA respository looks like:
public interface MyTransactionsRepository extends JpaRepository<MyTransactions, Long>
//My query works like a charm.
#Query( value = "SELECT * from ACTIVITI_TMP.BATCH_TABLE WHERE TO_CHAR(last_action, 'YYYY-MM-DD') = ?1", nativeQuery = true )
List< MyTransactions > findAllBy_ToChar_LastAction( String lastActionDateString );
This returns a list of MyTransactions objects as expected. Debugging, i see the returned object as ArrayList. Looking inside the elementData, I see that each object is, as expected, a MyTransactions object.
My second repository/query is where i'm having troubles.
public interface MyCountsRepository extends JpaRepository<MyCounts, Long>
#Query( value = "SELECT send_method, COUNT(*) AS counter FROM ACTIVITI_TMP.BATCH_TABLE WHERE TO_CHAR(last_action, 'YYYY-MM-DD') = ?1 GROUP BY send_method ORDER BY send_method", nativeQuery = true )
List<MyCounts> countBy_ToChar_LastAction( String lastActionDateString );
This DOES NOT return List as expected.
The object that holds the returned data was originally defined as List, but when I inspect this object in Eclipse, I see instead that it is holding an ArrayList. Drilling down to the elementData, each object is actually an Object[2]... NOT a MyCounts object.
I've modified the MyCountsRepository query as follows
ArrayList<Object[]> countBy_ToChar_LastAction( String lastActionDateString );
Then, inside my controller class, I create a MyCounts object for each element in List and then return List
This works, but... I don't understand why i have to go thru all this?
I can query a view as easily as a table.
Why doesn't JPA/Hibernate treat this as a simple 2 column table? send_method varchar(x) and count (int or long)
I know there are issues or nuances for how JPA treats queries with counts in them, but i've not seen anything like this referenced.
Many thanks for any help you can provide in clarifying this issue.
Anthony
That is the expected behaviour when you're doing a "group by". It will not map to a specific entity. Only way this might work is if you had a view in your database that summarized the data by send_method and you could map an entity to it.

Converting Object to Class object

in my Spring MVC project i m using Hibernate, by using Criteria API i am applying Group BY and Order BY clause. Query get executed on DB successfully and it brings result also but its an array of Object--
Here is code of Criteria API
Criteria criteria = session.createCriteria(DashboardSubindicatorSubmission.class, "DashboardSubindicatorSubmission")
.setProjection(Projections.projectionList()
.add(Projections.sum("InputValue").as("InputValue"))
.add(Projections.groupProperty("fkAccademicYearId"))
.add(Projections.groupProperty("fkAssessmentPlanID"))
.add(Projections.groupProperty("fkSubindicatorID"))
.add(Projections.groupProperty("InputTitle")))
.addOrder(Order.asc("fkAccademicYearId"))
.addOrder(Order.asc("fkAssessmentPlanID"))
.addOrder(Order.asc("InputTitle"));
List<DashboardSubindicatorSubmission> dashboardSubindicatorSubmissionList = (List<DashboardSubindicatorSubmission>)criteria.list();
session.flush();
transaction.commit();
return dashboardSubindicatorSubmissionList;
I am casting criteria.list() to List<DashboardSubindicatorSubmission> but when i try to do dashboardSubindicatorSubmissionList.get(i) on controller it gives me exception java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to mkcl.accreditation.model.DashboardSubindicatorSubmission.
i come to know that, though i m casting it to List<DashboardSubindicatorSubmission> still its an list of object[] thats why i cant do dashboardSubindicatorSubmissionList.get(i) because it returns me object of DashboardSubindicatorSubmission. (Correct me if i am wrong)
So how can i convert my result into list of DashboardSubindicatorSubmission class?
Does setResultTransformer() helps me in this case?
You have two options. When you use projections, Hibernate doesn't know how to respect each field because it uses the name of each field to build objects and he doesn't know the names yet.
Thus, your first option is to name the fields grouped so that they match the names of object properties. This is necessary even if the string you use in projection is already the name of the object field. Something like:
.add(Projections.groupProperty("fkAccademicYearId"), "fkAccademicYearId") // same value
.add(Projections.groupProperty("fkAssessmentPlanID"), "other") // other value
The second option is to do what you yourself suggested, create your own implementation of ResultTransformer. I reckon this a interesting option if you want to extract other object of this query, as when you make a report.

Table with a foreign key

how can I build a table of "orders" containing "IdOrder", "Description" and "User"?... the "User" field is a reference to the table "Users", which has "IdUser" and "Name". I'm using repositories.
I have this repository:
Repository<Orders> ordersRepo = new OrderRepo<Orders>(unitOfWork.Session);
to return all Orders to View, I just do:
return View(ordersRepo.All());
But this will result in something like:
IdOrder:1 -- Description: SomeTest -- User: UserProxy123ih12i3123ih12i3uh123
-
When the expected result was:
IdOrder:1 -- Description: SomeTest -- User: Thiago.
PS: I don't know why it returns this "UserProxy123ih12i3123ih12i3uh123". In Db there is a valid value.
The View:
It is showed in a foreach (var item in Model).
#item.Description
#item.User //--> If it is #item.User.Name doesn't work.
What I have to do to put the Name on this list? May I have to do a query using LINQ - NHibernate?
Tks.
What type of ORM are you using? You mention "repositories" but does that mean LinqToSql, Entity Framework, NHibernate, or other?
It looks like you are getting an error because the User field is not loaded as part of the original query. This is likely done to reduce the size of the result set by excluding the related fields from the original query for Orders.
There are a couple of options to work around this:
Set up the repository (or context, depending on the ORM) to include the User property in the result set.
Explicitly load the User property before you access it. Note that this would be an additional round-trip to the database and should not be done in a loop.
In cases where you know that you need the User information it would make sense to ensure that this data in returned from the original query. If you are using LinqToSql take a look at the DataLoadOptions type. You can use this type to specify which relationships you want to retrieve with the query:
var options = new DataLoadOptions();
options.LoadWith<Orders>(o => o.User);
DataContext context = ...;
context.LoadOptions = options;
var query = from o in context.Orders
select o;
There should be similar methods to achive the same thing whatever ORM you are using.
In NHibernate you can do the following:
using (ISession session = SessionFactory.OpenSession())
{
var orders = session.Get<Order>(someId);
NHibernateUtil.Initialize(orders.User);
}
This will result in only two database trips (regardless of the number of orders returned). More information on this can be found here.
In asp.net MVC the foreign key doesn't work the way you are using it. I believe you have to set the user to a variable like this:
User user = #item.User;
Or you have to load the reference sometimes. I don't know why this is but in my experience if I put this line before doing something with a foreign key it works
#item.UserReference.load();
Maybe when you access item.User.Name the session is already closed so NHib cannot load appropriate user from the DB.
You can create some model and initialize it with proper values at the controller. Also you can disable lazy loading for Orders.User in your mapping.
But maybe it is an other problem. What do you have when accessing "#item.User.Name" from your View?

Resources