Selecting multiple columns and set it to list of DTOs - mysql

I want to get multiple columns from database in a single query and set it to the corresponding DTO object fields.
Error message:
java.lang.IllegalStateException: No data type for node:
org.hibernate.hql.internal.ast.tree.IdentNode
+-[IDENT] IdentNode: 'payment' {originalText=payment}
Query:
TypedQuery<Object[]> query = entityManager.createQuery("SELECT
payment, createdOn,responseMessage FROM PaymentLog log WHERE log.id
=:personId", Object[].class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<Object[]> results = query.getResultList();
for (Object[] log : results) {
paymentTransaction.setAmount(log[0].toString());
paymentTransaction.setDate(log[1].toString());
paymentTransaction.setDescription(log[2].toString());
transactionList.add(paymentTransaction);
}
P.S. I know I can use JPA constructor expression. But as I have to add the DTOs in a list of DTO(i.e. transactionList), so is there a way with JPA construction expression where I can do that by running the query only one time instead in a loop for every single DTO?

You can have the JPA provider transform the result set for you by means of a constructor expression:
http://www.objectdb.com/java/jpa/query/jpql/select#Result_Classes_Constructor_Expressions_
https://en.wikibooks.org/wiki/Java_Persistence/JPQL#Constructors
This requires that the specified class has a constructor matching the select expression. This would then look something like the below:
TypedQuery<PaymentTransaction> query = entityManager.createQuery("SELECT new PaymentTransaction (log.payment, log.createdOn, log.responseMessage ) FROM PaymentLog log WHERE log.id
=:personId", PaymentTransaction.class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<PaymentTransaction> results = query.getResultList();
In JPA 2.1 you can also so like the below:
https://en.wikibooks.org/wiki/Java_Persistence/Querying#ConstructorResult_.28JPA_2.1.29

What you could do is:
TypedQuery<PaymentLog> query = entityManager.createQuery("SELECT log FROM PaymentLog log WHERE log.id =:personId", PaymentLog.class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<PaymentLog> results = query.getResultList();
for (PaymentLog log : results) {
paymentTransaction.setAmount(log.getPayment());
paymentTransaction.setDate(log.getCreatedOn());
paymentTransaction.setDescription(log.getResponseMessage());
transactionList.add(paymentTransaction);
}
It is not a good idea to select everything from the database if you are not going to use it. If the selected fields were the only columns in the table then approach above works.
If you had a lot more columns in the table, the previous would still work, but this might be better:
TypedQuery<PaymentTransaction> query = entityManager.createQuery("SELECT new PaymentTransaction (log.payment, log.createdOn, log.responseMessage) FROM PaymentLog log WHERE log.id =:personId", PaymentTransaction.class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<PaymentTransaction> results = query.getResultList();
The above query will return an already created list of PaymentTransactions. You have to note that the class PaymentTransactionshould have a constructor that accept these fields in the given order. Otherwise it will cause an exception

Related

Convert a silmple Mysql to JPQL Queries

Good Morning everyone, after lookin everwhere, i come to you to ask for a help.
So i have a this mysql query
SELECT * FROM USER WHERE WORPLACE ='TECHNICIAN',
And here is my JQPL query
SELECT U FROM USER U WHERE U.WORPLACE =:+TECHNICIAN
But some how i get this error on Glassfish
java.lang.IllegalStateException: Query argument Technician not found in the list of parameters provided during query execution.
Here is the code of my arraylist on the managed Bean
public List<Users> getListUsers() {
return this.userService.getTechnicians("Technician");
}
So, what do i want specificlly. I want a query that can sort in a table list of user where there workplace are 'Technician'.
Thanks for your help and have a nice day.
In JPQL named query parameters start with a : followed by the name. So, if you want to call your query parameter Technician you need to reference it as :Technician in your query.
You can then call the setParameter method on the Query interface with the parameter name and its value.
Here's an example using your query:
public List<User> getTechnicians(String technician) {
TypedQuery<User> q = getEntityManager().createQuery("SELECT u FROM User u WHERE u.poste =:Technician", User.class );
q.setParameter("Technician", technician);
return q.getResultList();
}

Hibernate3 criteria query selecting too many fields

I want to write a simple query to retrieve a list of USER with a simple restriction on CUSTOMER joined table.
I'm only interested by the USER entity.
If I write it using HPQL :
public List<Users> getAssociatedAdminObs(Integer pCustId) {
Criteria crit = getCriteriaForObsAdmin("USER");
crit.createCriteria("clients").add(Restrictions.eq("idCustomer", pCustId));
return crit.list();
StringBuilder hqlQuery = new StringBuilder().append("select u from Users as u join u.customers as c where c.idCustomer=:idCustomer");
Query q = getSessionAndManageFilter().createQuery(hqlQuery.toString());
q.setInteger("idCustomer", pCustId);
return q.list();
}
The SQL generated only Select all the fields from USER entity, as expected.
Now if I write it through Hibernate criteria API :
public List<Users> getAssociatedAdminObs(Integer pCustId) {
Criteria crit = getSession().createCriteria(Users.class);
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
crit.createCriteria("customers").add(Restrictions.eq("idCustomer", pCustId));
return crit.list();
}
The SQL generated Select all the fields from USER entity but also from the CUSTOMER entity.
I'm using hibernate-core 3.3.1.GA.
I know I can use projection to work around the issue but my object will be transient, I also validated it works using a subquery to do my restrictions.
But I'm not happy with using workarounds and I do not understand why it would behave differently between the 2 code examples ?
It doesn't make sense to me to select fields outside of the asked entity.

EclipseLink fails to fetch a scalar Boolean value

The following JPA criteria query succeeds on Hibernate (4.2.7 final).
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Boolean>criteriaQuery=criteriaBuilder.createQuery(Boolean.class);
Root<UserTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(UserTable.class));
criteriaQuery.multiselect(root.get(UserTable_.enabled));
ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(criteriaBuilder.lower(criteriaBuilder.trim(root.get(UserTable_.emailId))), criteriaBuilder.lower(criteriaBuilder.trim(parameterExpression))));
List<Boolean> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, "admin").getResultList();
for(Boolean o:list)
{
System.out.println("enabled : "+o);
}
It is simply meant to return a scalar Boolean value from MySQL database. The corresponding column is of type TINYINT(1) in MySQL.
It generate the following SQL statement.
SELECT
usertable0_.enabled as col_0_0_
FROM
social_networking.user_table usertable0_
WHERE
lower(trim(BOTH FROM usertable0_.email_id))=lower(trim(BOTH FROM ?))
The same query fails on EclipseLink (2.5.1) in which case it returns nothing (no error, no exception). It however, generates a correct SQL statement as follows.
SELECT enabled
FROM projectdb.user_table
WHERE (LOWER(TRIM(email_id)) = LOWER(TRIM(?)))
bind => [admin]
The corresponding JPQL like so,
SELECT u.enabled
FROM UserTable u
WHERE lower(trim(u.emailId))=lower(trim(:emailId))
also doesn't get the value in question.
It however, works with additional columns as shown below.
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]>criteriaQuery=criteriaBuilder.createQuery(Object[].class);
Root<UserTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(UserTable.class));
criteriaQuery.multiselect(root.get(UserTable_.enabled), root.get(UserTable_.firstName));
ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(criteriaBuilder.lower(criteriaBuilder.trim(root.get(UserTable_.emailId))), criteriaBuilder.lower(criteriaBuilder.trim(parameterExpression))));
List<Object[]> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, userName).getResultList();
One extra column root.get(UserTable_.firstName) is added and the return type of CriteriaQuery is changed from CriteriaQuery<Boolean> to CriteriaQuery<Object[]>.
The column is defined as follows in the corresponding entity.
#Basic(optional = false)
#NotNull
#Column(name = "enabled")
private Boolean enabled; //Getter and setter.
Why doesn't it given a Boolean value in EclipseLink?
This is due to EclipseLink bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=340089 which causes EclipseLink to be unable to distinguish the value returned from the SQL query from the construct it uses internally to indicate there were no results. Selecting another value is the only workaround, but it seems simple enough that not many people hit it or just workaround it without commenting or voting for the bug.

sqlexception index out of bounds with correct sql-statement

i've got an sql statement that works pretty well. but on implementing in my webapp working with play 2.1 i get this error:
javax.persistence.PersistenceException: Query threw SQLException:Column Index out of range, 0 < 1.
i found this question here: Error executing MySQL query via ebean using RawSql
but then i got other exceptions.
i'm trying to get tagged threads that contains a list of tags (same as stack overflow does).
here the sql statement
SELECT t.topic
FROM topic t
WHERE 3 = (SELECT COUNT( DISTINCT ta.id )
FROM topic_tag tt
INNER JOIN tag ta ON ta.id = tt.tag_id
WHERE ta.name IN ('children', 'spain','new')
AND tt.topic_id = t.id )
in play i do this:
RawSql rawSql = RawSqlBuilder.unparsed(sqlString).create();
result = find.setRawSql(rawSql).findList();
then, i got the out of bounds exception. after that i try to set column mappings:
RawSql rawSql = RawSqlBuilder.unparsed(sqlString)
.columnMapping("t.topic","topic")
.columnMapping("t.id","id")
.columnMapping("ta.name","tagList.name")
.columnMapping("ta.id","tagList.id")
.create();
now i get a null pointer exception. probably because ebean can't create a query from that.
here some code from my models:
#Entity
public class Topic extends Model{
#Id
public Long id;
#Required
public String topic;
#ManyToMany
public List<Tag> tagList;
}
#Entity
public class Tag extends Model {
#Id
public long id;
#Required
public String name;
}
after a lot of trying and frustrating i hope that somebody got a hint or a solution for this.
I just wasted few hours with similar problem, I actually managed to solve it by only mapping id field for certain kind of model and selecting lesser amount of fields, other values were automatically loaded after that - So basically, error occurred if I tried to select values like:
.. select e.id, e.name, e.description from exampleTable e .. and use mappings like:
RawSql rawSql = RawSqlBuilder.parse(sql)
// map the sql result columns to bean properties
.columnMapping("e.id", "exampleModel.id")
.columnMapping("e.name", "exampleModel.name")
.columnMapping("e.description", "exampleModel.description")
.create();
When I changed to select only e.id and map:
RawSql rawSql = RawSqlBuilder.parse(sql)
// map the sql result columns to bean properties
.columnMapping("e.id", "exampleModel.id")
.create();
It loaded also e.name and e.description to model values and errors disappeared.
(Of course my own query had several joins and were bit more complicated than this, but basics are the same.)
So to summarize: when this problem occurs, check that you are not loading anything twice (columnMapping), use System.out.println(""); or similar to check which values are already loaded for your model. Remember to also check annotations such as "#JoinColumn" which might load more data under same model - just based on given e.id value. If you dont select and set e.id as columnMapping value, then you might need to list all needed fields separately as .. e.name, e.description ..
Hopefully these findings helps someone out.

Could not format node 'Value' for execution as SQL

I've stumbled upon a very strange LINQ to SQL behaviour / bug, that I just can't understand.
Let's take the following tables as an example: Customers -> Orders -> Details.
Each table is a subtable of the previous table, with a regular Primary-Foreign key relationship (1 to many).
If I execute the follow query:
var q = from c in context.Customers
select (c.Orders.FirstOrDefault() ?? new Order()).Details.Count();
Then I get an exception: Could not format node 'Value' for execution as SQL.
But the following queries do not throw an exception:
var q = from c in context.Customers
select (c.Orders.FirstOrDefault() ?? new Order()).OrderDateTime;
var q = from c in context.Customers
select (new Order()).Details.Count();
If I change my primary query as follows, I don't get an exception:
var q = from r in context.Customers.ToList()
select (c.Orders.FirstOrDefault() ?? new Order()).Details.Count();
Now I could understand that the last query works, because of the following logic:
Since there is no mapping of "new Order()" to SQL (I'm guessing here), I need to work on a local list instead.
But what I can't understand is why do the other two queries work?!?
I could potentially accept working with the "local" version of context.Customers.ToList(), but how to speed up the query?
For instance in the last query example, I'm pretty sure that each select will cause a new SQL query to be executed to retrieve the Orders. Now I could avoid lazy loading by using DataLoadOptions, but then I would be retrieving thousands of Order rows for no reason what so ever (I only need the first row)...
If I could execute the entire query in one SQL statement as I would like (my first query example), then the SQL engine itself would be smart enough to only retrieve one Order row for each Customer...
Is there perhaps a way to rewrite my original query in such a way that it will work as intended and be executed in one swoop by the SQL server?
EDIT:
(longer answer for Arturo)
The queries I provided are purely for example purposes. I know they are pointless in their own right, I just wanted to show a simplistic example.
The reason your example works is because you have avoided using "new Order()" all together. If I slightly modify your query to still use it, then I still get an exception:
var results = from e in (from c in db.Customers
select new { c.CustomerID, FirstOrder = c.Orders.FirstOrDefault() })
select new { e.CustomerID, Count = (e.FirstOrder != null ? e.FirstOrder : new Order()).Details().Count() }
Although this time the exception is slightly different - Could not format node 'ClientQuery' for execution as SQL.
If I use the ?? syntax instead of (x ? y : z) in that query, I get the same exception as I originaly got.
In my real-life query I don't need Count(), I need to select a couple of properties from the last table (which in my previous examples would be Details). Essentially I need to merge values of all the rows in each table. Inorder to give a more hefty example I'll first have to restate my tabels:
Models -> ModelCategoryVariations <- CategoryVariations -> CategoryVariationItems -> ModelModuleCategoryVariationItemAmounts -> ModelModuleCategoryVariationItemAmountValueChanges
The -> sign represents a 1 -> many relationship. Do notice that there is one sign that is the other way round...
My real query would go something like this:
var q = from m in context.Models
from mcv in m.ModelCategoryVariations
... // select some more tables
select new
{
ModelId = m.Id,
ModelName = m.Name,
CategoryVariationName = mcv.CategoryVariation.Name,
..., // values from other tables
Categories = (from cvi in mcv.CategoryVariation.CategoryVariationItems
let mmcvia = cvi.ModelModuleCategoryVariationItemAmounts.SingleOrDefault(mmcvia2 => mmcvia2.ModelModuleId == m.ModelModuleId) ?? new ModelModuleCategoryVariationItemAmount()
select new
{
cvi.Id,
Amount = (mmcvia.ModelModuleCategoryVariationItemAmountValueChanges.FirstOrDefault() ?? new ModelModuleCategoryVariationItemAmountValueChange()).Amount
... // select some more properties
}
}
This query blows up at the line let mmcvia =.
If I recall correctly, by using let mmcvia = new ModelModuleCategoryVariationItemAmount(), the query would blow up at the next ?? operand, which is at Amount =.
If I start the query with from m in context.Models.ToList() then everything works...
Why are you looking into only the individual count without selecting anything related to the customer.
You can do the following.
var results = from e in
(from c in db.Customers
select new { c.CustomerID, FirstOrder = c.Orders.FirstOrDefault() })
select new { e.CustomerID, DetailCount = e.FirstOrder != null ? e.FirstOrder.Details.Count() : 0 };
EDIT:
OK, I think you are over complicating your query.
The problem is that you are using the new WhateverObject() in your query, T-SQL doesnt know anyting about that; T-SQL knows about records in your hard drive, your are throwing something that doesn't exist. Only C# knows about that. DON'T USE new IN YOUR QUERIES OTHER THAN IN THE OUTER MOST SELECT STATEMENT because that is what C# will receive, and C# knows about creating new instances of objects.
Of course is going to work if you use ToList() method, but performance is affected because now you have your application host and sql server working together to give you the results and it might take many calls to your database instead of one.
Try this instead:
Categories = (from cvi in mcv.CategoryVariation.CategoryVariationItems
let mmcvia =
cvi.ModelModuleCategoryVariationItemAmounts.SingleOrDefault(
mmcvia2 => mmcvia2.ModelModuleId == m.ModelModuleId)
select new
{
cvi.Id,
Amount = mmcvia != null ?
(mmcvia.ModelModuleCategoryVariationItemAmountValueChanges.Select(
x => x.Amount).FirstOrDefault() : 0
... // select some more properties
}
Using the Select() method allows you to get the first Amount or its default value. I used "0" as an example only, I dont know what is your default value for Amount.