Inefficient JPA query using compound Where clause - java

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

Related

Hibernate Criteria: hibernate left join without association

I am a Hibernate newbie and i have this below query. It is working as i expected. These two tables are not associated. Is there a way to get the same result by using Criteria API or how can i run this query via Hibernate ? Any help would be appreciated.
SELECT p.title, c.content
FROM posts p
LEFT JOIN comments c ON p.id = c.post_id
WHERE p.status = 'A' AND (p.title iLIKE '%r%' OR c.content iLIKE '%r%');
Criteria API needs a path between entities, so I'm not sure this join could be done using Criteria API. Better do it with HQL if you have Hibernate >= 5.1:
select p.title, c.content
from org.example.Posts p
left outer join org.example.Comments c
on p.id = c.id
where p.status = 'A' AND (lower(p.title) LIKE '%r%' OR lower(c.content) LIKE '%r%');
Still, you could stick to using SQL queries with Hibernate or better still, create association between Posts and Comments.

Java JPQL, join table, many to many relation

We are working on a project in school and we trying to use entity JPA.
We have several tables in our Mysql database and the one we are using is taxonomy, term, campaign and term_campaign_relationship.
We have made an entity for campaign, term, and taxonomy. We would like to get all the terms that are related to a campaign and filtered by taxonomy id.
We have used the #ManyToMany JPQL with #JoinTable and joinColumns
With that we have received all the terms that are related with the campaign.
But how do we get the result filtered by the taxonomy id only?
The sql statement below shows the result we are looking for...
SELECT
t.id, t.term_name, t.taxonomy_id
FROM
term t
INNER JOIN
taxonomy ty ON ty.id = t.taxonomy_id
INNER JOIN
campaign c ON c.id IN (SELECT
tc.campaign_id
FROM term_campaign_relationship tc
WHERE tc.term_id = t.id
)
WHERE c.id = 1 AND ty.id = 1;
This is from our campaign entity in java
#ManyToMany()
#JoinTable(
name="term_campaign_relationship",
joinColumns={#JoinColumn(name="campaign_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="term_id", referencedColumnName="id")})
private Collection<Term> programTypes;
Please advise if we are missing anything...
Is this the solution you looked for?
Query query = entityManager.createQuery("Select tax from taxonomy tax where tax.id = :arg1");
query.setParameter("arg1", 1);

SQL Query builder utility

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

JPA join query not working

I am trying to use JOIN with JPA. This is my JPA query:
SELECT o FROM Assessment AS o
INNER JOIN AssessmentText at
WHERE o = at.assessment
AND at.localeCode = :localeCode
The relation from the Assessment to AssessmentText is OneToMany.
When I am executing this query I am getting:
org.apache.openjpa.persistence.ArgumentException:
Encountered "INNER JOIN AssessmentText at" at character 31,
but expected: [".", "FETCH", "INNER", "JOIN", "LEFT", <IDENTIFIER>].
I am using JPA implementation: OpenJPA 2.2.1 with MySQL database.
Why do I get this error and how to solve this?
Assuming you want to eager fetch AssessmentText, you can do this (assument Assessment has a member called assessmentText):
SELECT o FROM Assessment AS o LEFT JOIN FETCH o.assessmentText WHERE o.assessmentText.localeCode = :localeCode
You are missing ON in your query It should be something like
SELECT o FROM Assessment AS o INNER JOIN AssessmentText at ON o = at.assessment WHERE at.localeCode = :localeCode
You didn't mention the relationship from Assessment to AssessmentText was - it needs to be used in the join declaration.
The query you do have shows a relationship from AssessmentText to Assessment which can be used if you refactor the query slightly:
SELECT at.assessment FROM AssessmentText at WHERE at.localeCode = :localeCode
Have in mind you should create your query in the following way:
Query query = entityManager.createQuery(queryString);
Not em.createNativeQuery (if you were doing it so).

HQL ERROR: Path expected for join

I keep trying variations of this query and can't seem to make this happen. I've also referenced this post: Path Expected for Join! Nhibernate Error and can't seem to apply the same logic to my query. My User object has a UserGroup collection.
I understand that the query needs to reference entities within the object, but from what I'm seeing I am...
#NamedQuery(
name = "User.findByGroupId",
query =
"SELECT u FROM UserGroup ug " +
"INNER JOIN User u WHERE ug.group_id = :groupId ORDER BY u.lastname"
)
select u from UserGroup ug inner join ug.user u
where ug.group_id = :groupId
order by u.lastname
As a named query:
#NamedQuery(
name = "User.findByGroupId",
query =
"SELECT u FROM UserGroup ug " +
"INNER JOIN ug.user u WHERE ug.group_id = :groupId ORDER BY u.lastname"
)
Use paths in the HQL statement, from one entity to the other. See the Hibernate documentation on HQL and joins for details.
You need to name the entity that holds the association to User. For example,
... INNER JOIN ug.user u ...
That's the "path" the error message is complaining about -- path from UserGroup to User entity.
Hibernate relies on declarative JOINs, for which the join condition is declared in the mapping metadata. This is why it is impossible to construct the native SQL query without having the path.
You'll be better off using where clauses. Hibernate does not accept inner joins for tables where the PK/FK relationship is not there between Entities
do
SELECT s.first_name, s.surname, sd.telephone_number FROM Student s, StudentDetails sd WHERE s.id = sd.student_id
instead of
SELECT s.first_name, s.surname, sd.telephone_number FROM Student s INNER JOIN StudentDetails sd on s.id = sd.student_id
The latter will only work if Student's id (s.id) is referenced as FK on StudentDetails (sd.student_id)) table design / erd

Categories

Resources