How to resolve ambiguous match when chaining generated Jooq classes - java

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.

Related

Design approach for a SQL query builder with Java

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 ) )

Spring Data JPA Specifications - Distinct + order by column in join

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");
}

QueryDSL how do I create a SubQuery with its own joins?

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))

Error Only from Java Spark ORA-00918: column ambiguously defined

I am trying to run few SQL queries using Java Spark libraries.
All SQLs are running fine except one:
(SELECT CMM.BENEFIT, (length(CMM.HIERARCHY) - length(replace(CMM.HIERARCHY,'>','')) + 1) BNFT_CMPNT_LVL_NBR, LBCX.SERVICE_DEF_TGT_CD, LBCX.SERVICE_DEF_DESC, LBCX.BENEFIT_CMPNT_DESC, LBCX.SERVICE_DEF_TGT_CD, CMM.HIERARCHY FROM EHUB_PROD_RAW.cs90_master_mapping CMM INNER JOIN EHUB_PROD_RAW.wpd_spider_benefit_hierarchy WSBH ON CMM.HIERARCHY = WSBH.HIERARCHY INNER JOIN EHUB_PROD_RAW.legacy_bnft_cmpnt_xref LBCX ON CMM.BENEFIT = LBCX.BENEFIT_CMPNT_NM)
Same query runs fine from SQL Developer!
I am guessing something with hidden char or quotes.
Any guidance please.
You are selecting the same column twice:
( select
cmm.benefit,
( length(cmm.hierarchy) - length(replace(cmm.hierarchy,'>','') ) + 1 ) bnft_cmpnt_lvl_nbr,
lbcx.service_def_tgt_cd,
lbcx.service_def_desc,
lbcx.benefit_cmpnt_desc,
--lbcx.service_def_tgt_cd, <-- this one is duplicated
cmm.hierarchy
from
ehub_prod_raw.cs90_master_mapping cmm
inner join ehub_prod_raw.wpd_spider_benefit_hierarchy wsbh on cmm.hierarchy = wsbh.hierarchy
inner join ehub_prod_raw.legacy_bnft_cmpnt_xref lbcx on cmm.benefit = lbcx.benefit_cmpnt_nm
)
If you need it twice in the result set, then you should use aliases. I think SQL Developer will auto-alias it by appending a _1 to one of the ambiguous columns.

Hibernate query with subquery in WHERE clause and multiple joins

I have been trying to get Hibernate to generate me a query with a subquery in its where clause. I've used this answer as a base to help me going, but this question mentioned only one table.
However, this is what I would need (in SQL):
SELECT [...]
FROM a
LEFT OUTER JOIN b on a.idb = b.idb
LEFT OUTER JOIN c on b.idc = c.idc
[...]
LEFT OUTER JOIN k out on j.idk = k.idk
WHERE k.date = (SELECT max(date) from k in where in.idk = out.idk) OR k.date is null
As I am not very used to using Hibernate, I'm having trouble specifying these inner joins while navigating in the inner constraints.
I was able to re-create the initial criteria as in the linked answer, but I can't seem to join the criteria and the rootCriteria.
If the entities are properly joined with #ManyToOne annotations, simply joining the criteria to the previous table will be enough to propagate the criteria to the whole query.
The following code seems to work properly to add the WHERE clause I'm looking for.
DetachedCriteria kSubquery = DetachedCriteria.forClass(TableJPE.class,"j2");
kSubQuery = kSubQuery.createAlias("k","k2");
kSubQuery.setProjection(Projections.max("k2.date"));
kSubQuery = kSubQuery.add(Restrictions.eqProperty("j.id", "j2.id"));
rootCriteria.add(Restrictions.disjunction()
.add(Subqueries.propertyEq("k.date",kSubQuery))
.add(Restrictions.isNull("k.date")));

Categories