SQL Query builder utility - java

I have a Set of columns and Tables, in respective drop downs, I am working on a Code to generate a dynamic SQL based on the Table-Column selection
It's working in case of simple Select statements but in the case of Multiple Joins, I am trying to figure out a Syntax for handlin Right and Left Joins.
Please help..this is the Error for SQL Syntax
1)
(Select dbo.Employee.Dept_ID,dbo.Employee.Emp_ID,dbo.Employee.Emp_Name,dbo.Employee_DataVal.DeptNo,
dbo.Employee_DataVal.EmpName,dbo.Employee_DataVal.EmpNo,dbo.Employee_DataVal.Salary,dbo.Emp_Sal.Emp_ID,dbo.Emp_Sal.Salary
FROM Employee
INNER JOIN Employee_DataVal
ON Employee.Dept_ID = Employee_DataVal.DeptNo
OR Employee_DataVal.EmpName = Employee.Emp_Name)
LEFT JOIN Emp_Sal
ON Employee.Emp_ID = Emp_Sal.Emp_ID
Incorrect syntax near the keyword 'LEFT'.
2)Select dbo.Employee.Dept_ID,dbo.Employee.Emp_ID,
dbo.Employee.Emp_Name,dbo.Employee_DataVal.DeptNo,
dbo.Employee_DataVal.EmpName,dbo.Employee_DataVal.EmpNo
,dbo.Emp_Sal.Emp_ID,dbo.Emp_Sal.Salary
FROM Employee INNER JOIN Employee_DataVal
ON Employee.Emp_ID = Employee_DataVal.EmpNo
AND Employee.Dept_ID = Employee_DataVal.DeptNo
LEFT JOIN Employee
ON Employee_DataVal.EmpName = Employee.Emp_Name
The objects "Employee" and "Employee" in the FROM clause have the same exposed names. Use correlation names to distinguish them.
PS: Running this sql on SQL server

This is a common problem when working with complex dynamic SQL strings on a string basis - the correct handling of the SQL syntax in its string form is difficult, and it is easy to create SQL injection vulnerabilities as well.
SQL builder APIs like jOOQ and others are very well suited for this task. I'm not sure what exactly the problem was in your case, but let's just assume that the last LEFT JOIN is optional in your query. You could write a query like this:
List<Field<?>> c = new ArrayList<>(Arrays.asList(
EMPLOYEE.DEPT_ID,
EMPLOYEE.EMP_ID,
EMPLOYEE.EMP_NAME,
EMPLOYEE_DATAVAL.DEPTNO,
EMPLOYEE_DATAVAL.EMPNAME,
EMPLOYEE_DATAVAL.EMPNO,
EMPLOYEE_DATAVAL.SALARY
));
Table<?> t = EMPLOYEE
.join(EMPLOYEE_DATAVAL)
.on(EMPLOYEE.DEPT_ID.eq(EMPLOYEE_DATAVAL.DEPTNO)
.or(EMPLOYEE_DATAVAL.EMPNAME.eq(EMPLOYEE.EMP_NAME));
if (someCondition) {
t = t.leftJoin(EMP_SAL).on(EMPLOYEE.EMP_ID.eq(EMP_SAL.EMP_ID));
c.addAll(Arrays.asList(
EMP_SAL.EMP_ID,
EMP_SAL.SALARY
));
}
Result<?> result =
ctx.select(c)
.from(t)
.fetch();

Speaking directly to the syntax errors:
The parentheses in this statement are invalid. Removing them will solve the problem.
The table Employee is used twice in the FROM clause. You must alias the tables for this to work.
Select dbo.Employee.Dept_ID,dbo.Employee.Emp_ID,
dbo.Employee.Emp_Name,dbo.Employee_DataVal.DeptNo,
dbo.Employee_DataVal.EmpName,dbo.Employee_DataVal.EmpNo
,dbo.Emp_Sal.Emp_ID,dbo.Emp_Sal.Salary
FROM Employee e1 INNER JOIN Employee_DataVal
ON e1.Emp_ID = Employee_DataVal.EmpNo
AND e1.Dept_ID = Employee_DataVal.DeptNo
LEFT JOIN Employee e2
ON Employee_DataVal.EmpName = e2.Emp_Name
Speaking to your broader question, the concept of a generic SQL query generator is quite common and has had several implementation. You won't find full implementation guidance in a forum such as this.
Cheers!

You are using LEFT Join same as the Self join. which is actually creating the problem.

In first case error is coming because of the wrong ) in wrong place as pointed below; Which making the end of query and so LEFT JOIN throwing an error. the ) must be at end of the query.
FROM Employee
INNER JOIN Employee_DataVal
ON Employee.Dept_ID = Employee_DataVal.DeptNo
OR Employee_DataVal.EmpName = Employee.Emp_Name ) <--Here
LEFT JOIN Emp_Sal
In second case, you are trying to do a self join to the same table in that case as the error already suggested you, you need to use correlation names like
FROM Employee emp1 <-- Here used a table alias emp1
INNER JOIN Employee_DataVal ed
ON emp1.Emp_ID = ed.EmpNo
AND emp.Dept_ID = ed.DeptNo
LEFT JOIN Employee emp2 <-- Here used a different table alias emp2
ON ed.EmpName = emp2.Emp_Name
Moreover, the LEFT JOIN Employee won't make any sense here and which can simply be modified to below code
FROM Employee emp1
INNER JOIN Employee_DataVal ed
ON emp1.Emp_ID = ed.EmpNo
AND emp.Dept_ID = ed.DeptNo
AND emp.Emp_Name = ed.EmpName <-- here by adding another join condition

Related

Querydsl ignores common table expression alias for JPA Entities

I need to build SQL query with common table expression using QueryDSL:
WITH cte AS (
SELECT DISTINCT BUSINESS_ID FROM BUSINESS WHERE MERCHANT_CODE like ?
)
SELECT t0.*
FROM PAYMENT t0
LEFT JOIN cte t1 ON t0.PAYER = t1.BUSINESS_ID
LEFT JOIN cte t2 ON t0.PAYEE = t2.BUSINESS_ID
WHERE (t1.BUSINESS_ID IS NOT NULL OR t2.BUSINESS_ID IS NOT NULL)
I have two JPA entities (Payment, Business).
This is how I implemented that:
String merchantCode = "abcd%";
QPayment payment = QPayment.payment;
QBusiness business = QBusiness.business;
QBusiness cte = new QBusiness("cte");
QBusiness merchant1 = new QBusiness("t1");
QBusiness merchant2 = new QBusiness("t2");
Configuration configuration = new Configuration(new OracleTemplates());
new JPASQLQuery<>(entityManager, configuration)
.with(cte,
JPAExpressions.selectDistinct(business.businessId).from(business)
.where(business.merchantCode.like(merchantCode)))
.select(payment)
.from(payment)
.leftJoin(cte, merchant1).on(payment.payer.eq(merchant1.businessId))
.leftJoin(cte, merchant2).on(payment.payee.eq(merchant2.businessId))
.where(merchant1.businessId.isNotNull()
.or(merchant2.businessId.isNotNull()));
And the problem is that during leftJoin it doesn't treat cte as a link, instead it inserts table name and two aliases: LEFT JOIN BUSINESS cte t1 ON .... I tried different templates – didn't help.
Am I doing something wrong or it's a QueryDSL bug?
JPQL doen't support CTEs, as we can see in grammar. And querydsl works over JPQL. CTEs are pretty vendor-specific, so you'll have to do one of following:
Rewrite query to be JPA-compatible
Use JPA native query
Query sql with querydsl (actually I don't remember if it supports CTEs)
From all above I would chose the 2nd option. Making native queries doen't harm your code. It makes your code more performant.
Take a good look at the tutorial
QCat cat = QCat.cat;
QCat mate = new QCat("mate");
QCat kitten = new QCat("kitten");
query.from(cat)
.innerJoin(cat.mate, mate)
.leftJoin(cat.kittens, kitten)
.list(cat);
You will want to .leftjoin(cte.merchant1, merchant1).on(...) or whatever the corresponding field is called in the parent "cte".
Basically you need to name the field which you want to join. Just stating the meta model does not suffice as there is no way of telling what you actually want. You can see it in your code (as well in the tutorial's kitten example): you have two Merchant you want to join to the cte, so which one is which.
The .on()-clause just states the conditions under which a join is valid, like you could place filters there.
.

Inefficient JPA query using compound Where clause

I have a tables with One-Many Relationships as follows
City->School->Teacher->Children
and my JPQL for retrieving children from a city is as below
#Query("Select c from Children c where c.teacher.school.city=:city")
Set<Children> findChildrenFromCity(#Param("city") City city);
This reference here about Where clause says that
"Compound path expressions make the where clause extremely powerful."
However, upon observing the logs I realise that the above query is doing strange things like
Generate multiple Selects instead of one Select
Some cross joins can be seen in the logs
I am trying to understand if I am defining my query correctly and if the compound Where is indeed so powerful, why is my query so inefficient.
You can use the following method:
Set<Children> findAllByTeacherSchoolCity(String city);
assuming, that your class Children has field Teacher teacher, Teacher has School school and School has String city.
In case there are differences, please ask in comments for clarification.
Try this
#Query("Select c from City city join city.schools s join s.teachers t join t.childrens c where city = :city")
Set<Children> findChildrenFromCity(#Param("city") City city);
This query is running exactly one Select query to fetch the Children entities. Check the below mentioned logs.
HIBERNATE: SELECT childrens3_.id AS id1_0_,
childrens3_.date_created AS date_cre2_0_,
childrens3_.date_updated AS date_upd3_0_,
childrens3_.NAME AS name4_0_,
childrens3_.teacher_id AS teacher_5_0_ FROM city city0_
INNER JOIN school schools1_
ON city0_.id = schools1_.city_id
INNER JOIN teacher teachers2_
ON schools1_.id = teachers2_.school_id
INNER JOIN children childrens3_
ON teachers2_.id = childrens3_.teacher_id WHERE city0_.id = ?
Now what you have is an n+1 issue. To fix such issue you can use join fetch instead of simple joins.
If you want use Query annotation try this approach
#Query("Select c from Children c join fetch c.teacher t join fetch t.school s join fetch s.city ct where ct.id = :id")

join and where clause in hibernate

I am using join and where clause in hibernate 3.but i cant reach the solution.I got the error.
Query qry= session.createQuery("SELECT addemployee.eid,addemployee.fname,addemployee.location,"
+ "empdet.jtitle,empdet.leadname FROM addemployee LEFT JOIN empdet ON addemployee.eid = empdet.eid WHERE (addemployee.eid ='"+id+"')");
List l = qry.list();
Iterator it=l.iterator();
while(it.hasNext())
{
Object rows[] = (Object[])it.next();
System.out.println(rows[0]+separator+rows[1]+separator+rows[2]+separator+rows[3]+separator+rows[4]);
}
Issue: org.hibernate.hql.ast.QuerySyntaxException: unexpected token: ON near line 1, column 127 [SELECT addemployee.eid,addemployee.fname,addemployee.location,empdet.jtitle,empdet.leadname FROM addemployee LEFT JOIN empdet ON addemployee.eid = empdet.eid WHERE (addemployee.eid ='206')]
Try to use session.createSQLQuery() instead.
and don't put enclosed apostrophe (''), you are inputting numbers, not varchar.
like this.
Query qry= session.createSQLQuery("SELECT addemployee.eid,addemployee.fname,addemployee.location,"
+ "empdet.jtitle,empdet.leadname FROM addemployee LEFT JOIN empdet ON addemployee.eid = empdet.eid WHERE (addemployee.eid ="+id+")");
Hibernate Session's createQuery() method requires valid HQL syntax. You can check how to write joins here.
Basically, in HQL you work with your entities, not SQL tables. So you don't need to write ON, because you already map association between entities.
If you still want to write native SQL query, you need to use
session.createSQLQuery(); instead

LEFT JOIN freezes query execution

I have a pretty simple sql query:
SELECT o.description, oi.description, cu.name
FROM dbo.order o
LEFT JOIN dbo.orderitem oi o.orderItemId = oi.orderItemId
INNER JOIN dbo.customer cu on cu.customerId = o.customerId
WHERE cu.id = 12345
That query works, every time.
If I change the INNER JOIN to a LEFT JOIN...
LEFT JOIN dbo.customer cu on cu.customerId = o.customerId
then it always freezes when I call it with Java (via REST) and sometimes freezes if I am using my SQL Client. Sometimes it just works with the exact same search parameter and returns a few rows (<10) as expected.
Freezes means that it won't even start counting the execution time in my SQL client (SQuirreL) and that it will run into a timeout when I call it in Java.
But it will work again if I add a second WHERE clause like
SELECT o.description, oi.description, cu.name
FROM dbo.order o
LEFT JOIN dbo.orderitem oi o.orderItemId = oi.orderItemId
LEFT JOIN dbo.customer cu on cu.customerId = o.customerId
WHERE cu.id = 12345
AND o.someOtherId = 3456
or remove the cu.id completely.
Does anyone have any clue why that could happen?
I am not getting this line..
LEFT JOIN dbo.orderitem am oi o.orderItemId = oi.orderItemId
Dont you think it should be like:
LEFT JOIN dbo.orderitem oi on o.orderItemId = oi.orderItemId
Can you try?
This is because the left join on customer makes the where clause ineffective; the query would return all orders, with all orderitems, but only for orders of customer 12345, it would also return the customer name.
Maybe you want to do a right join instead, so you get the customer name even if he has no orders?

How use hibernate criteria for a left outer join without direct link into the two tables

I'm trying to translate a SQL quest into Hibernate criteria.
My quest is working in SQL :
select * from objective
left outer join conditionstate
on objective.conditionid = conditionstate.conditionid
and conditionstate.personid = XXXX
where objective.toto_id = YYYYY
The objective is not directly mapped to the condition_state, but into a condition.
objective --> condition <-- condition_state
I've tested something like :
final DetachedCriteria criteriaObjective =
DetachedCriteria.forClass(Objective.class);
criteriaObjective.createAlias("conditionState", "conditionState", Criteria.LEFT_JOIN);
without success..
It's hard to suggest something without seeing your actual mappings. Going by your explanation and assuming that both condition and conditionState are mapped as many-to-one, you'd write something like:
final DetachedCriteria criteriaObjective =
DetachedCriteria.forClass(Objective.class);
criteriaObjective
.createCriteria("condition", Criteria.LEFT_JOIN)
.createCriteria("conditionState", Criteria.LEFT_JOIN)
.add(Restrictions.eq("personid", "XXXX") );
criteriaObjective.add(Restrictions.eqProperty("toto_id", "YYYY") );
Note that the above is NOT equivalent to the SQL query you've provided because "personid" condition will be generated as part of "WHERE" clause. As far as I know it's impossible to do a left join with condition using Criteria API - you may need to use HQL instead which provides with keyword for that exact purpose:
select o from objective as o
left join o.condition as c
left join c.conditionState as cs
with cs.person_id = 'XXXX'
and o.toto_id = 'YYYY'

Categories

Resources