Using JPA to retrieve particular many-valued association columns - java

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?

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/

JPA & Hibernate: Eager loading performing subsequent queries to fetch all data, instead of doing it in just one query

I have the following doubt. I would like to know why when using JPA and Hibernate, when performing an Eager loading in a ManyToOne or OneToMany relationship, it calls the DB in order to obtain the Entity information but additionally, produces subsequent queries to fetch every child.
On the other side, when using a query with JOIN FETCH it performs the query as I would expect it to be, taking the information all at once, since the fetchType is denoted as "EAGER".
Here it is a simple example:
I have the Class Student which has ManyToOne relationship with the Class Classroom.
#Entity
#Table(name = "STUDENT")
public class Student {
#ManyToOne(optional = true, fetch = FetchType.EAGER)
#JoinColumn(name = "ClassroomID")
private Classroom mainClass;
In the other side there is the class named Classroom as follows:
#Entity
public class Classroom {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "mainClass", fetch = FetchType.EAGER)
private List<Student> studentsList;
When obtaining the Classroom object, it performs one query to obtain the information from itself and subsequent queries to obtain the information of each student contained in the studentsList for every classRoom object.
First query:
Hibernate:
/* SELECT
r
FROM
Classroom r
LEFT JOIN
r.classStudents */
select
classroom0_.id as id1_0_,
classroom0_.number as number2_0_
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
And then it performs the next query as many times as Students assigned to each classroom.
Hibernate:
/* load one-to-many com.hw.access.Classroom.classStudents */
select
classstude0_.ClassroomID as Classroo4_0_1_,
classstude0_.id as id1_1_1_,
classstude0_.id as id1_1_0_,
classstude0_.FIRST_NAME as FIRST_NA2_1_0_,
classstude0_.LAST_NAME as LAST_NAM3_1_0_,
classstude0_.ClassroomID as Classroo4_1_0_
from
STUDENT classstude0_
where
classstude0_.ClassroomID=?
The question is: Why doesn't it takes the Information all at once? Why doesn't it takes the information in just one query? As it is already performing the Join clause there.
Why just when adding Fetch explicitly in the query, it does what it is requested?
For example:
SELECT
r
FROM
Classroom r
LEFT JOIN FETCH
r.classStudents */
And then, the output query does takes all the information in just one query:
Hibernate:
select
classroom0_.id as id1_0_0_,
classstude1_.id as id1_1_1_,
classroom0_.number as number2_0_0_,
classstude1_.FIRST_NAME as FIRST_NA2_1_1_,
classstude1_.LAST_NAME as LAST_NAM3_1_1_,
classstude1_.ClassroomID as Classroo4_1_1_,
classstude1_.ClassroomID as Classroo4_0_0__,
classstude1_.id as id1_1_0__
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
As you have a OneToMany relationship from Classroom to Student, using a single query would cause the Classroom fields to be repeated for each line.
Now imagine you have a second OneToMany relationship from Classroom to, say Course; if, for a given Classroom you have N Students and M Courses, you would have a query returning N+M rows, each containing the same fields of class Classroom.
I found it described in https://vladmihalcea.com/eager-fetching-is-a-code-smell/
under EAGER fetching inconsistencies:
Both JPQL and Criteria queries default to select fetching, therefore issuing a secondary select for each individual EAGER association. The larger the associations’ number, the more additional individual SELECTS, the more it will affect our application performance.
Also, note that Hibernate similarly ignoreg fetching annotations for HQL queries:
https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects
Hibernate ignores my outer-join="true" or fetch="join" setting and fetches an association lazily, using n+1 selects!
HQL queries always ignore the setting for outer-join or fetch="join" defined in mapping metadata. This setting applies only to associations fetched using get() or load(), Criteria queries, and graph navigation. If you need to enable eager fetching for a HQL query, use an explicit LEFT JOIN FETCH.
By default fetchtype is lazy, that mean if you dont ask for the List in your request Hibernate will not collect it.
In first request you ask for all attribute of Classroom r including the Student list so Hibernate will load them lazily (after finding out that you need them).
But when fetchtype is set to eager hibernate collect it even if you dont ask it.

JPA Query ElementCollection key

I have an entity class called User with the variable username with an ElementCollection map to represent "friendship" between two users, mapped by the userId, the start date and the id of the friend. The code for that relation is as follows:
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "friendship", joinColumns = #JoinColumn(name="userId"))
#MapKeyColumn(name = "friendId")
#Column(name = "startDate")
private Map<Integer, Date> friendShipMap;
I want to display the friends on a profile page for each user. I know I can just access the map via Java and get the ids (and find the usernames for the friends like that), but I'd like to use a custom #Query to get all the friends usernames for a given id. Somehow, the map is confusing me and I fail to find a proper statement for this.
Any help on this is greatly appreciated.
Edit: To clarify, I would like to use a custom #Query statement for the following SQL statement:
select u.username from users u join friendship m on u.userId = m.friendId where m.userId = 1;
So I thought it would be basically like this:
#Query("SELECT u.username FROM users u join u.friendShipMap m on u.userId = key(m) where m.userId = :userId")
List<User> getFriends(#Param("userId") int userId);
But I can't use m.userId, it says "cannot dereference scalar collection element: userId".
The corresponding table looks like this:
userId | startDate |friendId
You can use this #Query
#Query("select u.username from Users u inner join u.friendship f where f.friendId = :userId")
Make sure that you have an association between your entities Users and Friendship. Should be a #OneToMany relation I think.
And it should be in a List<String> instead of Map.
EDIT:1
If I understand it correctly, you will need another class friendship.
Maybe you need an embeddable class (friendship) described here.
http://www.thejavageek.com/2014/01/31/jpa-elementcollection-annotation/
Or you can create a new entity.
http://www.concretepage.com/hibernate/elementcollection_hibernate_annotation
And this is also a good example:
https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection

Hibernate many to many - fetch method eager vs lazy

New to Hibernate.
I have User Group many to many relation.
Three tables : User , Group and UserGroup mapping table.
Entities:
#Entity
#Table(name = "user")
public class User {
#Id
#Column (name = "username")
private String userName;
#Column (name = "password", nullable = false)
private String password;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="usergroup",
joinColumns={#JoinColumn(name="username")},
inverseJoinColumns={#JoinColumn(name="groupname")})
private Set<Group> userGroups = new HashSet<Group>();
... setter and getters
#Entity
#Table(name = "group")
public class Group {
#Id
#Column(name = "groupname")
private String groupName;
#Column(name = "admin", nullable = false)
private String admin;
#ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
... setter and getters
Notice that in Group Entity I'm using fetch method EAGER.
Now, when I'm calling my DAO to retrive all the groups in the system
using the following criteria:
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
I'm getting all the rows from the mappgin table (usergroup) instead of getting the actual number of groups...
for example if i have
in user table
username password
-----------------
user1 user1
user2 user2
in group table
groupname admin
---------------
grp1 user1
grp2 user2
in usergroup table
username groupname
------------------
user1 grp1
user2 grp2
user1 grp2
user2 grp1
The result will be the following list - {grp1,grp2,grp2,grp1}
Instead of {grp1,grp2}
If I change in Group Entity the fetch method to LAZY I'm getting the correct results
but hibernate throws me LazyException in another place...
Please assist what fetch method should I use and why ?
Thanks!
Lazy people will tell you to always use FetchType.EAGER counter-intuitively. These are the people who generally don't worry about database performance and only care about making their development lives easier. I'm going to say you should be using FetchType.LAZY for the increased performance benefit. Because database access is usually the performance bottleneck of most applications, every little bit helps.
If you do actually need to get a list of users for a group, as long as your call getUsers() from within a transactional session, you won't get that LazyLoadingException that is the bane of all new Hibernate users.
The following code will get you all groups without populating the list of users for those groups
//Service Class
#Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
The following code will get you all groups with the users for those groups at the DAO level:
//DAO class
#SuppressWarnings("unchecked")
public List<Group> findAllWithUsers(){
Criteria criteria = getCurrentSession().createCriteria(Group.class);
criteria.setFetchMode("users", FetchMode.SUBSELECT);
//Other restrictions here as required.
return criteria.list();
}
Edit 1: Thanks to Adrian Shum for this code
For more info on the different types of FetchMode's see here
If you don't want to have to write a different DAO method just to access your collection object, as long as you are in the same Session that was used to fetch the parent object you can use the Hibernate.initialize() method to force the initialisation of your child collection object. I would seriously not recommend that you do this for a List<T> of parent objects. That would put quite a heavy load on the database.
//Service Class
#Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);
//Forces the initialization of the collection object returned by getUsers()
Hibernate.initialize(group.getUsers());
return group;
}
I've not come across a situation where I've had to use the above code, but it should be relatively efficient. For more information about Hibernate.initialize() see here
I have done this in the service layer rather than fetching them in the DAO, because then you only have to create one new method in the service rather than making a separate DAO method as well. The important thing is that you have wrapped the getUsers() call within the transaction, so a session will have been created that Hibernate can use to run the additional queries. This could also be done in the DAO by writing join criteria to your collection, but I've never had to do that myself.
That said, if you find that you are calling the second method far more than you are calling the first, consider changing your fetch type to EAGER and letting the database do the work for you.
Although answer from JamesENL is almost correct, it is lacking of some very key aspect.
What he is doing is to force the lazy-loading proxy to load when the transaction is still active. Although it solved the LazyInitialization error, the lazy loadings are still going to be done one by one, which is going to give extremely poor performance. Essentially, it is simply achieving the same result of FetchType.EAGER manually (and with a even worse way, because we missed the possibilities of using JOIN and SUBSELECT strategy), which even contradict with the concern of performance.
To avoid confusion: Using LAZY fetch type is correct.
However, in order to avoid Lazy Loading Exception, in most case, you should have your repository (or DAO?) fetch the required properties.
The most inefficient way is to do it by accessing the corresponding property and trigger the lazy loading. There are some really big drawbacks:
Imagine what happen if you need to retrieve multiple level of data.
If the result set is going to be big, then you are issuing n+1 SQLs to DB.
The more proper way is to try to fetch all related data in one query (or a few).
Just give an example using Spring-data like syntax (should be intuitive enough to port to handcraft Hibernate Repository/DAO):
interface GroupRepository {
#Query("from Group")
List<Group> findAll();
#Query("from Group g left join fetch g.users")
List<Group> findAllWithUsers();
}
Join fetching is equally simple in Criteria API (though seems only left join is available), quoted from Hibernate doc:
List cats = session.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();

JPQL table join query

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

Categories

Resources