How can I annotate this complex join in JPA? - java

Let's say I have the following tables:
Locations
================
LocationKey, LocationName
Employees
================
EmployeeKey, FirstName, LastName
EmployeeLocationXRef
================
EmployeeLocationXRefKey, LocationKey, EmployeeKey
TimeSheets
=================
TimeSheetKey, EmployeeLocationXRefKey, Minutes
OK, so as you can see, in order for me to get all of the TimeSheets for a Location and Employee, I can do the following in SQL
select
*
from TimeSheets ts
join EmployeeLocationXRef ex on (ex.EmployeeLocationXRefKey = ts.EmployeeLocationXRefKey)
join Locations l on (l.LocationKey = ex.LocationKey)
join Employees e on (e.EmployeeKey = ex.EmployeeKey)
where
l.LocationKey = 'xxxx'
and e.EmployeeKey = 'yyyy'
But I have tried every way I can think of to map with (with annotations) in JPA using #JoinTable, etc.
Any ideas how this could be done? Unfortunately, this is a legacy system and the schema cannot be changed.
EDIT
Forgot to mention the EmployeeLocationXRef entity has not been directly mapped. Which could be the real problem. I will see about creating that entity mapping and see if it makes it easier.

I don't see anything particularly difficult or strange about this schema. There should simply be one entity per table and (unless there are unique constraints):
a ManyToOne between EmployeeLocationXRef and Location, using a JoinColumn
a ManyToOne between EmployeeLocationXRef and Employee, using a JoinColumn
a ManyToOne between Timesheet and EmployeeLocationXRef, using a JoinColumn
(these associations can of course be made bidirectional, or be in the other direction if you prefer so).
The JPQL query would simply be
select ts from Timesheet ts
join ts.employeeXRef ex
join ex.location l
join ex.employee e
where
l.locationKey = 'xxxx'
and e.employeeKey = 'yyyy'
which is a straightforward translation of the SQL query.

Related

Hibernate criteria - Inner Join OR Condition

I want to ignore default Join Restriction in createAlias. I have a OnetoOne relationship.
My Problem is Hibernate generates default restriction for the join relationship.
Pojo
Note : No column for diagnosticTemplate in charge table.
Charge.java
#OneToOne(mappedBy = "charge")
private DiagnosticTemplate diagnosticTemplate;
DiagnosticTemplate.java
#OneToOne
#JoinColumn(name = "charge")
#Exclude
private Charge charge;
Query
select
*
from
charges c
inner join diagnostic_template dt
on (dt.charge = c.id and dt.status=1) or (dt.status=0)
Criteria
Criteria criteria = getSession().createCriteria(Charge.class, "charge")
.createAlias("charge.diagnosticTemplate", "diagnosticTemplate",
JoinType.INNER_JOIN,
Restrictions.or(
Restriction.and(Restrictions.eqProperty("charge.id",
"diagnosticTemplate.charge"),
Restrictions.eq("diagnosticTemplate.status",true)),
Restrictions.eq("diagnosticTemplate.status",false) ))
Hibernate Query
select
*
from
charges c
inner join diagnostic_template dt
on dt.charge = c.id and (dt.charge = c.id and dt.status=1) or (dt.status=0)
How to avoid this condition? or anything wrong with my relationship?
Please help..!
When you join a charge with charge.diagnosticTemplate, it means that Hibernate will try to lookup a DiagnosticTemplate that is linked with this charge. Thus, the generated condition dt.charge = c.id does make sense.
Remember that you have define the relationship using foreign key. So, soft relation like dt.status=0 cannot be understood by Hibernate.
If you still wish to achieve you query, you can consider joining the two instance indirectly (not using association path). Refer to How to join Multiple table using hibernate criteria where entity relationship is not direct?.

Hibernate Native SQL Query retrieving multiple entities in join

Referencing with this answer to this correlated thread,
the trick posted by ehrhardt works fine.
But, what I have to do if I have to join with multiple entities? for example:
List<Person> peopleWithBooks = session.createSQLQuery(
"select {p.*}, {b.*}, {m.*} from person p, book b, magazine m where <complicated join>")
.addEntity("p", Person.class)
.addJoin("b", "p.books")
.addJoin("m", "p.magazines")
.addEntity("p", Person.class)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
Hibernate aggregates right the first join, but not the second (magazine entities are not grouped).
There are any tricks or there is a limit to join with only one correlated entity?
And if I have to join with entities that have sub entities? (my goal is to retrieve all selected data with only one custom query)

Inner join using HQL

I am trying to inner join two tables on one column.
From DB side, there's no mapping as it's something I don't want to discuss.
I want to execute HQL query using INNER JOIN and retrieve ROLE objects/results.
Here's my hql so far
session.createQuery("from ROLE as role INNER JOIN INVOLVEMENT as involvement ON role.id = involvement.roleid WHERE involvement.id = X").list();
I see ON is not available on HQL. how do i explicitly tell Hibernate to JOIN on this column only.
I tried below one too
select roleSpec from ROLE as role, INVOLVEMENT as involvement WHERE role.ID = involvement.role_id and involvement.id =27251352
But I am getting ROLE not mapped in exception.
Please check that your ROLE is indeed a mapped entity.
In addition, you don't need to perform "ON" - hibernate knows what is the join column (I know how to define this at JPA ) - so no need to provide it at the statement.
It should be -
session.createQuery("from Role as role INNER JOIN Involvement as involvement WHERE involvement.id = X").list();
I assume you have Role class mapped to ROLE table, and Involvement class mapped to Involement table.
Maybe you used table names by mistake, and this is why you get the "not mapped" error.
Last time I wrote HQL (and not JPA-QL) I used the following link as reference, it provides all the info needed.

Can you specify date-based partition keys when joining to related entities?

I have two related entities being mapped by JPA annotations backed by hibernate. The entities both have sequence-backed identity columns in oracle. Our also have monthly partitions, represented by a column called ENTRY_DATE.
T_MASTER T_JOINED
--------- -----------
MASTER_ID JOINED_ID
ENTRY_DATE ENTRY_DATE
MASTER_ID(FK)
... ....
To gain the benefit of the partition key, I'd like Hibernate to join on both the identity IDs and the partition key, but when I use the following annotation in the Joined class:
#ManyToOne
#JoinColumns(value={
#JoinColumn(name="MASTER_ID"),
#JoinColumn(name="ENTRY_DATE")})
private Master master;
I get errors about having too many join columns. I am forced to use
#JoinColumn(name="MASTER_ID")
private Master master;
I'm a bit of a JPA/Hibernate noob. Is it possible to use a partition key in addition to the primary key when joining to related entities?
Thanks!
You probably need to declare both columns as parts of composite primary key in Master. Hibernate would not care that much about what is real PK in the database. Mapping wil be slightly more complex with #Embeddable but it should solve the problem.
yes.
Using native sql it looks as follows:
Query query = session.createSQLQuery( "SELECT {T_master.*}, {T_joined.*} FROM schema.t_master AS T_master OUTER LEFT JOIN schema.T_joined AS T_joined ON T_master.ENTRY_DATE = T_joined.ENTRY_DATE AND T_master.MASTER_ID = T_joined.JOINED ID " /*WHERE ... */ );
query.setEntity( "T_master", T_master.class);
query.setJoin( "T_joined", "T_master.joinedSet"); //joinedSet is the one-to-many mapping
query.setJoin( "T_master", "T_joined.master" );
Thus, you get to be very verbose on your query, and hibernate only acts as an object mapper.
Using Criteria or HQL, it is impossible to have OUTER LEFT JOIN ON some field other than primary keys. This is why if you have such an option, use native SQL code.

JPA Query optimization by avoiding JOIN to lookup table?

Imagine a table emp:
CREATE TABLE emp
( id NUMBER
, name VARCHAR
, dept_code VARCHAR
)
and a table dept:
CREATE TABLE dept
( code VARCHAR
, name VARCHAR
)
emp.dept_code references dept.code as a ForeignKey.
These tables are mapped to JPA Entities, and the ForeignKey is modeled as an association:
#ManyToOne
#JoinColumn(name = "dept_code")
private Department department;
Given following data:
emp dept
---------------- ------------------
1 John SALS SALS Sales
2 Louis SALS SUPT Support
3 Jack SUPT
4 Lucy SUPT
I would like to write a JPA query that returns all Emloyees in the Support Department. Assume I know the PrimaryKey of the Support Department (SUPT)
I guess that would be:
SELECT emp
FROM Employee emp JOIN emp.department dept
WHERE dept.code = 'SUPT'
Question:
As the Department key SUPT code is available in the emp table, is there a way to rewrite the JPA query by avoiding the JOIN to the Department Entity?
Would this result in a performance improvement? Or is a JPA implementation (like Hibernate) smart enough to avoid the database join to the dept table?
You would usually write the query as
select emp
from employee emp
where emp.department.code = 'SUPT'
and let your provider figure out the best way to come up with the result. In the case of hibernate, yes, it is smart enough to realize it can just look at the join column.
edit : It is worth noting, that you haven't set up lazy loading in your annotations, so it's going to join the table in to create the department entity anyway :)

Categories