HQL, can I parameterize the FROM clause? - java

I have this HQL query:
Query q = em.createQuery (
"DELETE FROM Annotation a WHERE a.id IN ( " +
" SELECT ja.id FROM :entityName an JOIN an.annotations ja)"
);
and I'm being told: QuerySyntaxException: unexpected token: : near line 1
Do I have any hope of making the entity name after FROM a parameter? I have a list of entities to send to this query and I'm afraid that string concatenation is too slow.

You can't substitute the Entity name the parameters work for entity properties not instead.
You could select the entities ids to be deleted with one query and then pass them to a second delete query, but for READ_COMMITED transaction isolation you might still end up with someone else inserting one child entity that would have matched your query. SERIALIZABLE will solve this issue.

Related

How to paginate custom queries with group by?

I have the following JPQL query being mapped to a custom projection. The query is working correctly until I set the size attribute at the pagination object.
#Query(value = "SELECT EXTRACT(WEEK FROM e.time) AS week "
+ "COUNT(r.id) AS total FROM MyEntity e "
+ "LEFT JOIN e.relationship r GROUP BY week")
Page<WeeklyReport> findWeeklyReport(Pageable pageable);
Hibernate fails with the error java.sql.SQLSyntaxErrorException: Unknown column 'week' in 'group statement' after running the following query:
select count(entity0_.id) as col_0_0_ from entity entity0_
left outer join relationship relationship1_ on entity0_.id=relationship1_.tide_id
group by week
How can I have the same query without my group name break the pagination above?
The error does not happen if the size is not defined.

How to map result got from native query in hibernate?

I have a query that has more columns then what my entity class has.
In order to not let hibernate complaints, I have to add an annotation to the field like
#Transient
private Integer count;
But by doing this makes hibernate not able to map count. Let's say my query is
session.createSQLQuery("SELECT p.*, count(p.id), sqrt(1+2) as distance FROM post p group by p.id")
I know the query doesn't make any logical sense. This is just for example. The columns return from query above will have everything in post and two extra columns, count and distance. I wanted to map the result to my entity with count and distance are annotated with #Transient, or if there's a better way to map the result. I'm more than happy to do so. The goal is not to do this in an entity but a class with mapped result. I've tried calling addEntity() but doesn't seem to help.
You can use Result Set Transformer to achieve this.
Step 1 ) Create a new DTO class with all the fields that you query going to return
Step 2 ) Add the below line
setResultTransformer( Transformers.aliasToBean(DTO.class))
Example :
List resultWithAliasedBean = session.createQuery(
"SELECT p.*, count(p.id), sqrt(1+2) as distance FROM post p group by p.id")
.setResultTransformer(Transformers.aliasToBean(DTO.class))
.list();
DTO dto = (DTO) resultWithAliasedBean.get(0);
Note : Make sure the field names in the DTO class match the column name which your query is returning.
I see that you are using Hibernate so Yathish answer works fine.
But if you want to do it with JPA spec then you can use Result Set Mapping
Query q = em.createNativeQuery(
"SELECT c.id, c.name, COUNT(o) as orderCount, AVG(o.price) AS avgOrder " +
"FROM Customer c " +
"JOIN Orders o ON o.cid = c.id " +
"GROUP BY c.id, c.name",
"CustomerDetailsResult");
#SqlResultSetMapping(name="CustomerDetailsResult",
classes={
#ConstructorResult(targetClass=com.acme.CustomerDetails.class,
columns={
#ColumnResult(name="id"),
#ColumnResult(name="name"),
#ColumnResult(name="orderCount"),
#ColumnResult(name="avgOrder", type=Double.class)})
})
There you have to specifiy the mappin of the columns from the SQL result set to the DTO.
And if you think this is to complicated there is a open source project called QLRM (Query Lanaguage Result Mapper) that mapps any SQL statement to a POJO.
http://simasch.github.io/qlrm/
And last but not least if you will do extensive SQL processing why not have a look at jOOQ: https://www.jooq.org/

Delete from table on same select same table mariadb using jpa

I need delete from table on operation of same table .JPA query is
DELETE FROM com.model.ElectricityLedgerEntity a
Where a.elLedgerid IN
(SELECT P.elLedgerid FROM
(SELECT MAX(b.elLedgerid)
FROM com.model.ElectricityLedgerEntity b
WHERE b.accountId='24' and b.ledgerType='Electricity Ledger' and b.postType='ARREARS') P );
I got this error:
with root cause org.hibernate.hql.ast.QuerySyntaxException: unexpected
token: ( near line 1, column 109 [DELETE FROM
com.bcits.bfm.model.ElectricityLedgerEntity a Where a.elLedgerid IN (
SELECT P.elLedgerid FROM ( SELECT MAX(b.elLedgerid) FROM
com.bcits.ElectricityLedgerEntity b WHERE b.accountId='24'
and b.ledgerType='Electricity Ledger' and b.postType='ARREARS') P ) ]
at
org.hibernate.hql.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
at
org.hibernate.hql.ast.QuerySyntaxException.convert(QuerySyntaxException.java:47)
at
org.hibernate.hql.ast.ErrorCounter.throwQueryException(ErrorCounter.java:82)
at
org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:284)
Same query is running on mysql terminal ,but this is not working with jpa .Can any one tell me how i can write this query using jpa .
I don't understand why do you use Pbefore the last parenthesis...
The following code is not enough ?
DELETE FROM com.model.ElectricityLedgerEntity a
Where a.elLedgerid IN
(SELECT MAX(b.elLedgerid)
FROM com.model.ElectricityLedgerEntity b
WHERE b.accountId='24' and b.ledgerType='Electricity Ledger' and
b.postType='ARREARS')
Edit for bypassing mysql subquery limitations :
The new error java.sql.SQLException: You can't specify target table 'LEDGER' for update in FROM clause
is known in mysql when you use it with JPA. It's one MySQL limitation.
A recent stackoverflow question about it
In brief, you cannot "directly" updated/deleted a table that you query in a select clause
Now I understand why your original query did multiple subqueries seemingly not necessary (while it was useful for mysql) and had a "special" syntax.
I don't know tricks to solve this problem in JPA (I don't use the MySQL DBMS for a long time now).
At your place, I would do two queries. The first where you select the expected max elLedgerid and the second where you could delete line(s) with the id retrieved in the previous query.
You should not have performance issues if your sql model is well designed, the sql indexes well placed and the time to access to the database is correct.
You cannot do this in a single query with Hibernate. If you want to delete the max row(s) with Hibernate you will have to do so in two steps. First, you can find the max entry, then you can delete using that value in the WHERE clause.
But the query you wrote should actually run as a raw MySQL query. So why don't you try executing that query as a raw query:
String sql = "DELETE FROM com.model.ElectricityLedgerEntity a " +
"WHERE a.elLedgerid IN (SELECT P.elLedgerid FROM " +
"(SELECT MAX(b.elLedgerid) FROM com.model.ElectricityLedgerEntity b " +
"WHERE b.accountId = :account_id AND b.ledgerType = :ledger_type AND " +
" b.postType = :post_type) P );";
Query query = session.createSQLQuery(sql);
query.setParameter("account_id", "24");
query.setParameter("ledger_type", "Electricity Ledger");
query.setParameter("post_type", "ARREARS");
Just want to extend existing answer:
In brief, you cannot "directly" updated/deleted a table that you query in a select clause
This was lifted with starting from MariaDB 10.3.1:
Same Source and Target Table
Until MariaDB 10.3.1, deleting from a table with the same source and target was not possible. From MariaDB 10.3.1, this is now possible. For example:
DELETE FROM t1 WHERE c1 IN (SELECT b.c1 FROM t1 b WHERE b.c2=0);

how to change this query in Hibernate? comma separated list in one column

Previous output
SQL query -
select
employeeid,FamilyPay,IsActive,
stuff((
select ',' + u.IndividualPay
from yourtable u
where u.IndividualPay = IndividualPay
order by u.IndividualPay
for xml path('')
),1,1,'') as IndividualPay
from yourtable
group by EmployeeID,FamilyPay,IsActive
output :-
Desired output
Can someone help me changing this in Hibernate query ?
It would be a misuse of HQL to replicate that SQL query. HQL is a query language for Hibernate entities, not a replacement for SQL, thus you need to work at the level of Hibernate entities.
You can use #Formula to directly bind the subquery result to a entity field:
#Formula("stuff(( select ',' + u.IndividualPay from yourtable u where u.IndividualPay = IndividualPay order by u.IndividualPay for xml path('') ),1,1,'')")
private String individualPay;
When Hibernate generates the query, it will insert this SQL fragment into the generated SQL.
It is more important to define your entity correctly. The HQL should be simple.

java.lang.String cannot be cast HQL

Updated
Error says:
ava.lang.String cannot be cast to com.test.test.classes.TblTaxType
what is happening is when I add the tag select distinct taxtcode error is appearing. But when I removed the select tag like FROM tblTaxType tbl_tax_type WHERE bfnsCode = ? everything is fine. What is the cause? this is my code:
String hql = "SELECT DISTINCT TAXT_CODE FROM tbl_tax_type WHERE BFNS_CODE = ?";
try {
setSession(HibernateUtil.getSession());
#SuppressWarnings("unchecked")
List <TblTaxType> resultList = getSession().createSQLQuery(hql)
.setString(0, bfnsCode)
.list();
Your entity is probably named TblTaxType, not tblTaxType. Case matters.
Side note: don't name sql an HQL query. SQL and HQL are different languages.
Solved it using GROUP BY instead by using DISTINCT.
String hql = "FROM TblTaxType tbl_tax_type WHERE bfnsCode = ? GROUP BY taxtCode";
Your query returns TAXT_CODE, this field is a property of your TblTaxType entity, so you can't cast one property (string) in your main entity. This is the reason of your error.
If you need complete entity you must change your query but DISTINCT is not useful in this case because if you extract complete entity, there's ID field (different for each row). If you want a first element, you can add in your query ORDER BY clause with LIMIT 1 (is MySql).
A solution with GROUP BY works only if you use MySql as DBMS because if you have Sql Server the correct behaviour of field list / group by is: a field in field list must be in GROUP BY cluse or must be in aggregate function.

Categories