Writing Complex SQL Queries Using Spring Specificaitons - mysql

I'm having a complex SQL query which I want to execute in my Spring boot application. Following is a part of it's JPQL equivalent:
SELECT j FROM Job j WHERE
( (?6 = 0L) OR
0L IN (SELECT i12.filterFieldId FROM IjpPublishFilterFields i12 WHERE i12.jobId = j.id AND i12.fieldType LIKE 'GRADE') OR
(?6 IN (SELECT node.id FROM Grade node
INNER JOIN Grade parent ON ((node.lft BETWEEN parent.lft AND parent.rgt) AND
parent.id IN (SELECT i1.filterFieldId FROM IjpPublishFilterFields i1 WHERE **j.id** = i1.jobId AND i1.fieldType LIKE 'GRADE'))
WHERE node.organizationId = ?5 AND
node.isActive = true AND
parent.isActive = true )))
Since there are multiple other conditions which require to form 'Where' clause dynamically, I believe #Query annotation is out of the window.
After a lot of research, still I'm unable to convert this where clause to Specification.
e.g., I can return whole IjpPublishFilterFields object but can't return just i12.filterFieldId.
How could I write the where clause using JPA Specification?

I was able to solve this by using EnitityManager.createQuery() and formed this equivalent JPQL using simple String concatenation. Still I believe, we should be able to form this where caluse using Spring data JPA Specifications. So, further hints are welcomed.

There is nothing special about your query, so you should be able to translate this to something like criteriaBuilder.or( criteriaBuilder.eq(param, 0L), criteriaBuilder.literal(0L).in(subquery1), param.in(subquery2) ) where subquery1 and subquery2 are constructed roughly like this: Subquery<Long> subquery1 = criteriaQuery.subquery(Long.class); Root<IjpPublishFilterFields> r = subquery1.from(IjpPublishFilterFields.class); subquery1.select(r.get("filterFieldId")); ...

Related

how i can manipulate nested query j2ee hibernate

I have the following query, the problem that hibernate does not support nested after the 'from' I tried to create a view, but it did not work, I want to know how can I use hibernate to run this query correctly
SELECT sum(dc.nbrDefaut) def, a.nb control,c.id_of
FROM controlequalite c ,detailscontrole dc,
(select sum(nbreControlle) nb, id_monitrice
from controlequalite group by id_monitrice) a
where c.id = dc.id_controle
and c.id_monitrice = a.id_monitrice
and c.date >= '2016-03-25 00:00:00'
group by c.id_monitrice,c.id_of;
With a query like this, the simplest way is to use a native query. I'm assuming you are using JPA/Hibernate as ORM, so you can simply do like:
List<YourObject> resultList = yourEntityManager.createNativeQuery("nativeSQLhere", YourObject.class).getResultList();

INNER JOIN Results from Select Statement using Doctrine QueryBuilder

Can you use Doctrine QueryBuilder to INNER JOIN a temporary table from a full SELECT statement that includes a GROUP BY?
The ultimate goal is to select the best version of a record. I have a viewVersion table that has multiple versions with the same viewId value but different timeMod. I want to find the version with the latest timeMod (and do a lot of other complex joins and filters on the query).
Initially people assume you can do a GROUP BY viewId and then ORDER BY timeMod, but ORDER BY has no effect on GROUP BY, and MySQL will return random results. There are a ton of answers out there (e.g. here) that explain the problem with using GROUP and offer a solution, but I am having trouble interpreting the Doctrine docs to find a way to implement the SQL with Doctrine QueryBuilder (if it's even possible). Why don't I just use DQL? I may have to, but I have a lot of dynamic filters and joins that are much easier to do with QueryBuilder, so I wanted to see if that's possible.
Sample MySQL to Reproduce in Doctrine QueryBuilder
SELECT vv.*
FROM view_version vv
#inner join only returns where the result sets overlap, i.e. one record
INNER JOIN (
SELECT MAX(timeMod) maxTimeMod, viewId
FROM view_version
GROUP BY viewId
) version ON version.viewId = vv.viewId AND vv.timeMod = version.maxTimeMod
#join other tables for filter, etc
INNER JOIN view v ON v.id = vv.viewId
INNER JOIN content_type c ON c.id = v.contentTypeId
WHERE vv.siteId=1
AND v.contentTypeId IN (2)
ORDER BY vv.title ASC;
Theoretical Solution via Query Builder (not working)
I am thinking that the JOIN needs to inject a DQL statement, e.g.
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryMax = $viewVersionRepo->createQueryBuilder()
->addSelect('MAX(timeMod) AS timeModMax')
->addSelect('viewId')
->groupBy('viewId');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// I tried putting the query in a parenthesis, to no avail
->join('('.$queryMax->getDQL().')', 'version', 'WITH', 'vv.viewId = version.viewId AND vv.timeMod = version.timeModMax')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
My code (which may not match the above example perfectly) outputs:
SELECT e, view, contentType
FROM Gutensite\CmsBundle\Entity\View\ViewVersion e
INNER JOIN (
SELECT MAX(v.timeMod) AS timeModMax, v.viewId
FROM Gutensite\CmsBundle\Entity\View\ViewVersion v
GROUP BY v.viewId
) version WITH vv.viewId = version.viewId AND vv.timeMod = version.timeModMax
INNER JOIN e.view view
INNER JOIN view.contentType contentType
WHERE e.siteId = :siteId
AND view.contentTypeId IN (:contentTypeId)
ORDER BY e.title ASC
This Answer seems to indicate that it's possible in other contexts like IN statements, but when I try the above method in the JOIN, I get the error:
[Semantical Error] line 0, col 90 near '(SELECT MAX(v.timeMod)': Error: Class '(' is not defined.
A big thanks to #AdrienCarniero for his alternative query structure for sorting the highest version with a simple JOIN where the entity's timeMod is less than the joined table timeMod.
Alternative Query
SELECT view_version.*
FROM view_version
#inner join to get the best version
LEFT JOIN view_version AS best_version ON best_version.viewId = view_version.viewId AND best_version.timeMod > view_version.timeMod
#join other tables for filter, etc
INNER JOIN view ON view.id = view_version.viewId
INNER JOIN content_type ON content_type.id = view.contentTypeId
WHERE view_version.siteId=1
# LIMIT Best Version
AND best_version.timeMod IS NULL
AND view.contentTypeId IN (2)
ORDER BY view_version.title ASC;
Using Doctrine QueryBuilder
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// Join Best Version
->leftJoin('GutensiteCmsBundle:View\ViewVersion', 'bestVersion', 'WITH', 'bestVersion.viewId = e.viewId AND bestVersion.timeMod > e.timeMod')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
// LIMIT Joined Best Version
->andWhere('bestVersion.timeMod IS NULL')
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
In terms of performance, it really depends on the dataset. See this discussion for details.
TIP: The table should include indexes on both these values (viewId and timeMod) to speed up results. I don't know if it would also benefit from a single index on both fields.
A native SQL query using the original JOIN method may be better in some cases, but compiling the query over an extended range of code that dynamically creates it, and getting the mappings correct is a pain. So this is at least an alternative solution that I hope helps others.

JPA criteria subquery with common customerId field

How do I write the following MySQL query using JPA Criteria?
select * from deptors where customerId in
(select customerId from address where zipcode="12345-6789")
and gender="male";
I have read http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Criteria#Subquery and many stackoverflow threads including JPA 2.0, Criteria API, Subqueries, In Expressions. But I am just not getting it. Thanks for helping.
Something like:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Deptors> cq = cb.createQuery(Deptors.class);
Root<Deptors> c = cq.from(Deptors.class);
Subquery<Address> subquery = cq.subquery(Address.class);
Root<Address> a = subquery.from(Address.class);
However I would rather recommend building this query with JPQL and/or externalize it as a NamedQuery into your Entity.
Sebastian

Symfony2, JOINS with doctrine2 or sql

I have a question about symfony2.
I have a project and I am using databases with it. I use for the most part Doctrine2 and entity classes. I like the entity class object database stuff, very handy etc.
My question is, is there a way to perform normal SQL in symfony? I always get an exception when I try to use standard SQL. I am having trouble with joins in doctrine2, so i would rather use normal SQL for that.
My join would look like this in SQL:
SELECT DISTINCT Document . *
FROM Document
INNER JOIN DocumentGruppe ON Document.id = DocumentGruppe.dokId
INNER JOIN UserGruppe ON DocumentGruppe.gruppenId = UserGruppe.gruppenId
WHERE UserGruppe.userId =9
The where clause at the end is just for testing. If I use doctrine with it's DQL it always says that there is an exception: The Variable DocumentGruppe was not defined before.
Here is my DQL query:
$test = $em->createQuery(
'SELECT DISTINCT d
FROM AcmeDocumentBundle:Document d
INNER JOIN DocumentGruppe dg ON d.id = dg.dokId
INNER JOIN UserGruppe ug ON dg.gruppenId = ug.gruppenId
WHERE ug.userId =9
'
);
Does anyone know a workaround or a way to use this doctrine2 stuff to work with joins?
Every JOINED tables must be declared as associations in mapping... How is your entity defined ? Show us your mapping file (Document.php if annotation, or Resources/config/doctrine/document;xml or yml if XMl or YAML).
Your request will be something like that :
$test = $em->createQuery(
'SELECT DISTINCT d
FROM AcmeDocumentBundle:Document d
INNER JOIN d.documentGruppen dg
INNER JOIN d.userGruppen ug
WHERE ug.userId =9
'
);

Why is Linq2SQL generating a nested query instead of using a JOIN?

I'm trying to understand why Linq is generating the SQL that it is for the statement below:
var dlo = new DataLoadOptions();
dlo.LoadWith<TemplateNode>(x => x.TemplateElement);
db.LoadOptions = dlo;
var data = from node in db.TemplateNodes
where node.TemplateId == someValue
orderby node.Left
select node;
Which generates the following SQL:
SELECT [t2].[Id],
[t2].[ParentId],
[t2].[TemplateId],
[t2].[ElementId],
[t2].[Left] AS [Left],
[t2].[Right] AS [Right],
[t2].[Id2],
[t2].[Content]
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [t0].[Left]) AS [ROW_NUMBER],
[t0].[Id],
[t0].[ParentId],
[t0].[TemplateId],
[t0].[ElementId],
[t0].[Left],
[t0].[Right],
[t1].[Id] AS [Id2],
[t1].[Content]
FROM [dbo].[TemplateNode] AS [t0]
INNER JOIN [dbo].[TemplateElement] AS [t1]
ON [t1].[Id] = [t0].[ElementId]
WHERE [t0].[TemplateId] = 16 /* #p0 */) AS [t2]
WHERE [t2].[ROW_NUMBER] > 1 /* #p1 */
ORDER BY [t2].[ROW_NUMBER]
There is a Foreign Key from TemplateNode.ElementId to TemplateElement.Id.
I would have expected the query to produce a JOIN, like so:
SELECT * FROM TemplateNode
INNER JOIN TemplateElement ON TemplateNode.ElementId = TemplateElement.Id
WHERE TemplateNode.TemplateId = #TemplateId
As per the suggestions in the answers to this question I have profiled both queries and the JOIN is 3 times faster than the nested query.
I'm using a .NET 4.0 Windows Forms app to test with SQL Server 2008 SP2 64bit developer edition.
The only reason that LINQ-SQL would generate the ROW_NUMBER query is due to the Skip Method. As bizare as the above SQL seems, I think within T-SQL there is no construct for simple paging like MySQL's Limit 10,25, so you get the above SQL when using Skip and Take.
I would assume that there is a Skip being used for paging purposes and LINQ-SQL is modifying the query. If you use an application like LINQ-Pad you can run different LINQ queries to see their generated SQL.
Your example of a join is not equivalent. You cannot get the ROW_NUMBER and subsequently select only rows WHERE ROW_NUMBER > 1 with a simple join. You would have to do a sub-select or similar to get this result.