I am trying to build a subquery for a QueryDSL select that uses an exists clause in a BooleanExpression.
The data model is such that there are projects which contain a single media. Media can have many dimensions. I am looking to select projects that have certain dimensional data and am using a subquery to accomplish that.
The subquery looks like this:
QProject project = QProject.project;
QMedia media = QMedia.media;
Predicate subExpression = JPAExpressions.selectOne()
.from(media)
.innerJoin(media.dimensions)
.where(project.media.id.eq(media.id),
dimension.dimensionType.id.eq(Long.valueOf(inputDimensionType))).exists();
I store this as a predicate but, when I try to utilize it inside of a parent query I get an error: antlr.NoViableAltException: unexpected token: elements
The generated portion that is causing the error looks something like this (from the hibernate.hql.internal logs):
... and ((exists (select 1
from com.app.model.Media media
inner join elements(media.dimensions)
That is the error that happens when I plug it into a master query like this:
JPAQuery<ResponseCurve> query = new JPAQuery<>(this.entityManager);
query.select().from(project)
.where(project.state.eq(inputState))
.where(subExpression);
There are two possible issues with this query:
You're trying to reference alias dimensions, but you never associated it with the join to media.dimensions
You're dereferencing dimension.dimensionType without a join. For identifier values this is possible, for any other property, it is not.
What about:
QProject project = QProject.project;
QMedia media = QMedia.media;
QDimension dimension = QDimension.dimension;
Predicate subExpression = JPAExpressions.selectOne()
.from(project.media, media)
.innerJoin(media.dimensions, dimension )
.on(dimension.dimensionType.id.eq(Long.valueOf(inputDimensionType))
Related
In my webapp I need to create a query engine module where the user selects in the view what columns and filters he want and get the datas related to these queries.
So, I have to build SQL dynamic queries with this kind of format :
SELECT {columns} FROM MainTable FULL OUTER JOIN SecondTable ON ... FULL OUTER JOIN ThirdTable ON ... WHERE {filters}
The columns and filters are known at runtime. (read-only)
Actually, I have a big SQL Server view (Mapped entity in Java) which make 3 FULL OUTER JOIN of others SQL server views. And I build the query in the source code by parsing keywords as AND, OR, number, date, text, ...
Finally, I return a response in the front-end module a table with datas.
But, I'm facing performance issues (scalability) with this method and I'm looking for a more efficient way to do that.
Is it possible to split the SELECT query mentionned above in subqueries to improve performances ? Or there is better design approach for that ?
Here the request I actually use (names changed) with Hibernate :
SELECT * FROM BigView "+WHERE+" OPTION(ROBUST PLAN)
(BigView is aggregation of 4 views : RootTable, Table2, Table3, Table4)
Btw, I know "+WHERE+" is a bad practice but this is not the concern of this topic
I thought using this instead (delete BigView in SQL Server and the related entity) but it actually does same thing even if there is a little performance gain with the columns restriction :
SELECT ROW_NUMBER() OVER (ORDER BY id) AS rownum, tmp.* FROM ( SELECT '' AS id, "+columns+" "+
"FROM RootTable FULL OUTER JOIN "+
"Table2 ON RootTable.RT_ID = Table2.RT_N FULL OUTER JOIN "+
"Table3 ON RootTable.RT_ID = Table3.RT_ID FULL OUTER JOIN "+
"Table4 ON RootTable.RT_ID = Table4.RT_N "+
WHERE+" ) AS tmp
My previous deleted question was not enough focused so I hope this one is correct.
EDIT : I add this filter (WHERE clause) to show what can be requested for example :
RT_ID>'10' AND ( Table2_Topic LIKE '%test%' OR Table3_Date=CONVERT(datetime, '24/09/2020', 103) OR ( Table3_N is not null and Table2_ID<>0 ) )
I have some specifications that I am combining with "and":
Specification.where(predicate1).and(predicate2).and(predicate3);
One of them has distinct set :
query.distinct(true);
Another one makes an order by on a column that is in a join.
query.orderBy(builder.desc(bJoin.get("orderbyColumn")));
This fails with a SQLGrammarException stating that order by column should be in distinct.
So basically we have entity A, the main entity, and some nested entity B, we select from A but want to order by B, and in generated sql it only selects columns from A. The only way I found to make it work (= making it select from B as well) is to replace the join by a fetch :
Fetch < A, B > bFetch = root.fetch("joinCol", JoinType.INNER);
Join < A, B > bJoin = (Join < A, B > ) bFetch;
that worked for some time, was testing locally in H2, but then after some time started getting another error :
org.hibernate.QueryException: query specified join fetching, but the
owner of the fetched association was not present in the select list
I solved it somehow in my local pointing to H2 by requiring some columns to not be null, but in real server using PostgreSQL, it's not working at all, getting that error for all cases when a fetch is present.
My question is : what is the right way to use distinct along with orderby on a nested entity that is not fetched? Is my solution with fetch ok and it just needs to be fixed (and if so how?) or I should go for another option entirely?
For the actual query I am using this method :
findAll(Specification<>, Pageable)
Isn't there a way to have distinct wrapping the whole query with order by (some sort of subquery?) and bypassing all this nightmare? Have it generate a query like this:
select distinct colA1, colA2, coAl3 from (select colA1, colA2, coAl3
from A inner join B b on ........ order by b.colB1)
Do I need to convert my specification to predicate manually and do something else with it to try to solve my issues (some kind of hybrid approach)?
Any pieces of advice will be greatly appreciated.
I encountered same error but actually it was not error :)
findAll(Specification<>, Pageable) this method throws 2 different queries.
First one is count query where you have to be careful.
Second is the rows query where you actually did it.
You can check the query type with code below
if (query.getResultType() != Long.class && query.getResultType() != long.class){
root.fetch("entity1");
}
I have defined my models in JPA and am writing some queries for my application and I am using JOOQ generated classes to join all the tables together to check if the requested resources actually belong to the requesting user.
However, when I do this I get the following warning:
Ambiguous match found for ID. Both "alias_4548634"."ID" and "alias_47496750"."ID" match.
java.sql.SQLWarning: null
at org.jooq.impl.Fields.field(Fields.java:132) ~[jooq-3.11.10.jar:?]
... etc
This is my code
db.select(countField)
.from(thing)
.where(JThing.THING.thingBucket().bucket().organization().customer().ID.in(idList))
.orderBy(countField)
This is the SQL it generates
SELECT
count(PUBLIC.THING.ID) AS count
FROM (
PUBLIC.THING
LEFT OUTER JOIN (
PUBLIC.THING_BUCKET AS alias_72652126
LEFT OUTER JOIN (
PUBLIC.BUCKET AS alias_4548634
LEFT OUTER JOIN (
PUBLIC.ORGANISATION AS alias_43016761
LEFT OUTER JOIN PUBLIC.CUSTOMER AS alias_47496750
ON alias_43016761.CUSTOMER_ID = alias_47496750.ID
)
ON alias_4548634.ORGANISATION_ID = alias_43016761.ID
)
ON alias_72652126.ID = alias_4548634.ID
)
ON PUBLIC.THING.THING_BUCKET_ID = alias_72652126.ID
)
WHERE alias_47496750.ID IN (81353)
ORDER BY count
Given that JOOQ is generating the SQL I'd expect it to be able to understand it without throwing an error. What am I missing? How do I do configure/query/whatever to resolve the SQLWarning?
UPDATE
After playing around I've identified the source of the issue.
THING_BUCKET is sub-type of BUCKET so that THING_BUCKET.ID = BUCKET.ID
if I rewrite the query to I get the same results, but without the error
SELECT
count(PUBLIC.THING.ID) AS count
FROM (
PUBLIC.THING
LEFT OUTER JOIN (
PUBLIC.BUCKET AS alias_4548634
LEFT OUTER JOIN (
PUBLIC.ORGANISATION AS alias_43016761
LEFT OUTER JOIN PUBLIC.CUSTOMER AS alias_47496750
ON alias_43016761.CUSTOMER_ID = alias_47496750.ID
)
ON alias_4548634.ORGANISATION_ID = alias_43016761.ID
)
ON PUBLIC.THING.BUCKET_ID = alias_4548634.ID
)
WHERE alias_47496750.ID IN (81353)
ORDER BY count
So what I would like to be able to do is go
db.select(countField)
.from(thing)
.where(JThing.THING.bucket().organization().customer().ID.in(idList))
.orderBy(countField)
and join my THING directly to the BUCKET rather then the THING_BUCKET, but I do not know how to accomplish this with the generated classes.
This looks like a bug that has been fixed in jOOQ 3.14, see #8659, #10603
From your description, looks like BUCKET and THINK_BUCKET being described as subtypes in the db level, are getting confused by the jooq generated classes.
A short term fix may be, to cut the hierarchical relation in the db level, regenerate and see what happens.
I am have a problem where i need to join two tables using the LEAST and GREATEST functions, but using JPA CriteriaQuery. Here is the SQL that i am trying to duplicate...
select * from TABLE_A a
inner join TABLE_X x on
(
a.COL_1 = least(x.COL_Y, x.COL_Z)
and
a.COL_2 = greatest(x.COL_Y, x.COL_Z)
);
I have looked at CriteriaBuilder.least(..) and greatest(..), but am having a difficult time trying to understand how to create the Expression<T> to pass to either function.
The simplest way to compare two columns and get the least/greatest value is to use the CASE statement.
In JPQL, the query would look like
select a from EntityA a join a.entityXList x
where a.numValueA=CASE WHEN x.numValueY <= x.numValueZ THEN x.numValueY ELSE x.numValueZ END
and a.numValueB=CASE WHEN x.numValueY >= x.numValueZ THEN x.numValueY ELSE x.numValueZ END
You can code the equivalent using CriteriaBuilder.selectCase() but I've never been a big fan of CriteriaBuilder. If requirements forces you to use CriteriaBuilder then please let me know and I can try to code the equivalent.
CriteriaBuilder least/greatest is meant to get the min/max value of all the entries in one column. Let's say you want to get the Entity that had the alphabetically greatest String name. The code would look like
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(EntityX.class);
Root<EntityX> root = query.from(EntityX.class);
Subquery<String> maxSubQuery = query.subquery(String.class);
Root<EntityX> fromEntityX = maxSubQuery.from(EntityX.class);
maxSubQuery.select(cb.greatest(fromEntityX.get(EntityX_.nameX)));
query.where(cb.equal(root.get(EntityX_.nameX), maxSubQuery));
I created a sample Spring Data JPA app that demonstrates these JPA examples at
https://github.com/juttayaya/stackoverflow/tree/master/JpaQueryTest
It turns out that CriteriaBuilder does support calling LEAST and GREATEST as non-aggregate functions, and can be accessed by using the CriteriaBuilder.function(..), as shown here:
Predicate greatestPred = cb.equal(pathA.get(TableA_.col2),
cb.function("greatest", String.class,
pathX.get(TableX_.colY), pathX.get(TableX_.colZ)));
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.