JPQL table join query - java

How do I get JPA & JPQL to pass a complete join query to the RDBMS? For example,
SELECT e
FROM Employee e
WHERE a.runkey = e.runkey
AND e.middle = 'M'
AND a.state = 'MA'
With the following Employee class
#Entity
public class Employee implements Serializable {
blah ... blah
#OneToOne
#JoinColumn(
name = "runkey",
referencedColumnName = "runkey",
insertable=false, updatable=false)
private Address address;
}
and the JPQL,
SELECT e
FROM Employee e
INNER JOIN FETCH e.address AS a
WHERE a.state = :state
AND e.middle = :middle
I am able to get Hibernate JPA to pull the data as expected.
However, eclipselink croaks that it cannot traverse associated field "address".
If so, how then should I design the Employee entity and how should I phrase the JPQL in order to get eclipselink to execute a table join with WHERE filters on both tables?
(Rant: Otherwise Eclipselink JPA is no better than JDO!!!)
~
Further edit: Does this post mean anything to my case ....
https://forums.oracle.com/forums/thread.jspa?threadID=1568659

The problem is that you are trying to alias a join fetch which is not allowed according to the JPQL specs. Hibernate allows this anyway.
You can still obtain the desired behavior with EclipseLink using query hints.
Take a look at the following posts:
jpa fetch join query
EclipseLink JPQL (Glassfish v3): join fetch syntax problem?
The following link can also be useful:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/QueryOptimization

Related

Hibernate left join fetch by non-primary keys

Following entities:
#Table
class AA1 {
#Id
Long id;
String a_number;
Category category;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name= 'a_number', referencedColumnName='a_number')
#JoinColumn(name= 'category', referencedColumnName='category')
BB1 bb1
...other fields...
}
#Table
class BB1 {
#Id
String a_number;
Category category;
String value;
}
JPQL query:
SELECT a FROM AA1 a LEFT JOIN a.bb1 b;
Hibernate produces correct sql query, but when it tries to collect data it makes additional call like:
SELECT b.a_number, b.category, b.value FROM BB1 b WHERE b.a_number = ? AND b.category = ?
I checked that query returns null.
How can I avoid such database queries?
My investigation: As far as I see Hibernate creates key by(AA1.a_number and AA1.category) and tries to retrieve entity from context. And for specific row 'left join' query returns null values and Hibernate asks context by key and context returns null, it leads to call to database for it.
You must add FETCH to your JPQL query :
SELECT a FROM AA1 a LEFT JOIN FETCH a.bb1 b;
but keep the LAZY loading, because Hibernante will always try to get ManyToOne or OneToOne relationship, which are EAGER by default, with an additional query.
Look at this article https://thorben-janssen.com/5-common-hibernate-mistakes-that-cause-dozens-of-unexpected-queries/ from Th
By default, to make lazy loading work for #OneToOne and #ManyToOne, you need to enable "no proxy lazy associations". Otherwize, despite the FetchType.LAZY annotation, the associated object will be "fetched" and the "fetch" will be done with an extra sql query.
Therefore, one half-way solution to leverage performances without enabling "no proxy lazy associations" is to avoid extra queries by forcing a join fetch on the associated objet. Various technics allow to reach this goal : "LEFT JOIN FETCH" in JPQL queries or EntityGraph.
Just looking at the entity definition #ManyToOne(fetch = FetchType.LAZY) is the cause.
You are explicitly telling JPA to fetch BB1 only when it is needed/accessed.Hence when the first call to get the parent entity is made BB1 is not loaded.It is only when you are accessing the child that triggers JPA to fetch it.
If you change it to FetchType.EAGER , both the entities will be queried in a single call.
But be careful there are advantages/pit-falls with either approach.
Read more abt it here : https://thorben-janssen.com/entity-mappings-introduction-jpa-fetchtypes/

Using JPA to retrieve particular many-valued association columns

There is a column that's so huge, it slows the entire query speed.
So i need to ommit the one, only give when actualy needed.
I've tried this solution:
#Query("SELECT new Account (a.name) From Account a Where a.id = :id)
The above code will retrive the id column data.When I try to use other getter,obviously the rest property are all null.
but when it comes with other entity relation for example:
//The account entity code
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
#NotFound(action = NotFoundAction.IGNORE)
private User user;
/**
The user side code is ommitted
**/
#Query("SELECT new Account (a.name,a.user) From Account a)
it will generarte these sql query:
inner join
user user3_
on account0_.user_id=user3_.id
However, when we using the normal jpa method like
#Query("SELECT a FROM Account a WHERE a.id = :id")
Account getById(UUID id)
we can easily get the user entity with the getter method:
Account acc = accountRepository.getById(id);
User user = acc.getUser();
Not the inner join sql query;
How can I retrieve the paritcular association entity columns with getter?
Can it be possible to achieve with jpa?
This post should help : How to lazy load an entity attribute using JPA
Prior I strongly recommend you to understand what lazy loading is in JPA and how it works. These one could help : What is lazy loading in Hibernate? and Difference between FetchType LAZY and EAGER in Java Persistence API?

JPA 2 query giving strange behavior on OR clause

I have a JPA 2 query that is driving me nuts. A SurveyQuestion may have an Organization. Here's the mapping within SurveyQuestion
#ManyToOne( optional=true )
#JoinColumn( name="organization_key" )
private Organization organization;
Organization has a field named key. I create a TypedNamedQuery with the following jpql
SELECT q from SurveyQuestion q
where q.organization IS NULL
or q.organization.key = :organizationKey
with that query I only get the questions that have the given key. If I remove the q.organization.key = :organizationKey from the query, then I get all those that have no organization, but I can't for the life of me get it to return the combination of those that have no organization or have the given organization.
JPA2 with EclipseLink provider.
This is JPA standard.
q.organization.key
resolves to an inner join on q.organization, not to a left join!
You have to do this:
SELECT q
from SurveyQuestion q
left join q.organization o
where o is null OR o.key = :organizationKey

How to write a HQL query for Many To Many Associations?

I have 3 tables, Role[roleId, roleName], Token[tokenID, tokenName] & ROLETOKENASSOCIATION[roleId, tokenID]. The 3rd one was created automatically by hibernate. Now if i simply write a Query to get all the objects from Role class means, it gives the all role objects along with the associated tokenID & tokenName.
I just wanted the association as unidirectional. i.e: Roles--->Tokens
So the annotation in the Role class looks like,
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int roleId;
private String roleName;
#ManyToMany
#JoinTable(name="ROLE_TOKEN_ASSOCIATION",
joinColumns={#JoinColumn(name="roleId")},
inverseJoinColumns={#JoinColumn(name="tokenID")})
private List<Token> tkns;
//Getters & Setters
Now i want the tokenNames for the specific roleId.
First i made a query like this SELECT tkns.tokenName FROM Role WHERE Role.roleId=:roleId
But, i ended up with some dereference error.
Then i changed the query to SELECT tkns FROM Role r WHERE r.roleId=:roleId
Now i have got what i wanted. But it comes with roleId too.
How shall i get tokenName itself?
Actually my problem is solved, but i would like to know how to do it.
It ll be helpful to me, if anyone explains the Query Construction.
Any suggestions!!
Have you tried
SELECT t.tokenName FROM Role r JOIN r.tkns t WHERE r.roleId = :roleId
EDIT: This query almost directly maps to the corresponding SQL query where Role r JOIN r.tkns t is a shorthand syntax for the SQL join via the link table Role r JOIN ROLETOKENASSOCIATION rt ON r.roleId = rt.roleId JOIN Token ON rt.tokenId = t.tokenId. Affe's answer is another syntax for the same query.
See also:
Chapter 14. HQL: The Hibernate Query Language
You want a scalar list of just the name field? You should be able to get that like this
select t.name from Roles r, IN(r.tkns) t where r.roleId = :id

How do I left join tables in unidirectional many-to-one in Hibernate?

I'm piggy-backing off of How to join tables in unidirectional many-to-one condition?.
If you have two classes:
class A {
#Id
public Long id;
}
class B {
#Id
public Long id;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id")
public A parent;
}
B -> A is a many to one relationship. I understand that I could add a Collection of Bs to A however I do not want that association.
So my actual question is, Is there an HQL or Criteria way of creating the SQL query:
select * from A left join B on (b.parent_id = a.id)
This will retrieve all A records with a Cartesian product of each B record that references A and will include A records that have no B referencing them.
If you use:
from A a, B b where b.a = a
then it is an inner join and you do not receive the A records that do not have a B referencing them.
I have not found a good way of doing this without two queries so anything less than that would be great.
Thanks.
I've made an example with what you posted and I think this may work:
select a,b from B as b left outer join b.parent as a in HQL.
I have to find a "criteria" way of doing that though.
You may do so by specifying the fetch attribute.
(10) fetch (optional) Choose between outer-join fetching and fetching by sequential select.
You find it at: Chapter 6. Collection Mapping, scroll down to: 6.2. Mapping a Collection
EDIT
I read in your question's comment that you wanted a way to perform a raw SQL query? Here a reference that might possibly be of interest:
Chapter 13 - Native SQL Queries
and if you want a way to make it possible through HQL:
Chapter 11. HQL: The Hibernate Query Language
In chapter 11, you want to scroll down to 11.3. Associations and joins.
IQuery q = session.CreateQuery(#"from A as ClassA left join B as ClassB");
I guess however that ClassB needs to be a member of ClassA. Further reasdings shall help.
Another thing that might proove to be useful to you are named queries:
<query name="PeopleByName">
from Person p
where p.Name like :name
</query>
And calling this query from within code like so:
using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction()) {
session.GetNamedQuery("PeopleByName")
.SetParameter("name", "ayende")
.List();
tx.Commit();
}
Please take a look at the referenced link by Ayende who explains it more in depth.

Categories

Resources