OneToMany association with JoinTable returning null - java

In short: A user has many "not clients" (these are the clients the user cannot serve). User is associated to multiple clients through a join table.
User class: https://gist.github.com/dd99690fcaaba2c834d6
Client class: https://gist.github.com/10de71bcd1914ded5fb9
DAO: https://gist.github.com/dd4a369d60a05460d0c0
the "notClients" attribute in User is always null, can anyone help me understand why?

In short, because you're not including it in your select query. Not sure why you're writing out SQL queries and using the bean transformer? That's a very strange way to use hibernate. In the end it means what you're getting back is not a hibernate managed entity. It's just an object with the specific stuff that you selected mapped onto it.
The "normal"/"correct" way to use hibernate would be something like this:
private User getUser(int id, String userType)
{
User result;
session = HibernateUtil.getWilsonsSessionFactory().getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("select u from User u
where u.id = :id and u.role = :role");
query.setParameter("id", id);
query.setParameter("role", userType);
result = (User)query.uniqueResult();
session.getTransaction().commit();
return result;
}
Then what you get back is an Hibernate entity that will have all its mapped properties populated.
(would not personally use uniqueResult in that way either, but I am willing to admit that is largely a style preference.)

Related

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?

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();

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

Java-Hibernate-Newbie: How do I acces the values from this list?

I have this class mapped
#Entity
#Table(name = "USERS")
public class User {
private long id;
private String userName;
}
and I make a query:
Query query = session.createQuery("select id, userName, count(userName) from User order by count(userName) desc");
return query.list();
How can I access the values returned by the query?
I mean, how should I treat the query.list()? As a User or what?
To strictly answer your question, queries that specify a property of a class in the select clause (and optionally call aggregate functions) return "scalar" results i.e. a Object[] (or a List<Object[]>). See 10.4.1.3. Scalar results.
But your current query doesn't work. You'll need something like this:
select u.userName, count(u.userName)
from User2633514 u
group by u.userName
order by count(u.userName) desc
I'm not sure how Hibernate handles aggregates and counts, but I'm not sure if your query is going to work at all. You're trying to select a aggregate (i.e. the "count(userName)"), but you don't have a "group by" clause for userName.
If the query does in fact work, and Hibernate can figure out what to do with it, the results you get back will most likely be a raw Object[], because Hibernate will not be able to map your "count(userName)" data into any field on your mapped objects.
Overall, when you get into using aggregates in queries, Hibernate can get a little more tricky, since you're no longer mapping tables/columns directly into classes/fields. It might be a good idea to read up more on how to do aggregates in Hibernate, from their documentation.

JPA eager fetch does not join

What exactly does JPA's fetch strategy control? I can't detect any difference between eager and lazy. In both cases JPA/Hibernate does not automatically join many-to-one relationships.
Example: Person has a single address. An address can belong to many people. The JPA annotated entity classes look like:
#Entity
public class Person {
#Id
public Integer id;
public String name;
#ManyToOne(fetch=FetchType.LAZY or EAGER)
public Address address;
}
#Entity
public class Address {
#Id
public Integer id;
public String name;
}
If I use the JPA query:
select p from Person p where ...
JPA/Hibernate generates one SQL query to select from Person table, and then a distinct address query for each person:
select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3
This is very bad for large result sets. If there are 1000 people it generates 1001 queries (1 from Person and 1000 distinct from Address). I know this because I'm looking at MySQL's query log. It was my understanding that setting address's fetch type to eager will cause JPA/Hibernate to automatically query with a join. However, regardless of the fetch type, it still generates distinct queries for relationships.
Only when I explicitly tell it to join does it actually join:
select p, a from Person p left join p.address a where ...
Am I missing something here? I now have to hand code every query so that it left joins the many-to-one relationships. I'm using Hibernate's JPA implementation with MySQL.
Edit: It appears (see Hibernate FAQ here and here) that FetchType does not impact JPA queries. So in my case I have explicitly tell it to join.
JPA doesn't provide any specification on mapping annotations to select fetch strategy. In general, related entities can be fetched in any one of the ways given below
SELECT => one query for root entities + one query for related mapped entity/collection of each root entity = (n+1) queries
SUBSELECT => one query for root entities + second query for related mapped entity/collection of all root entities retrieved in first query = 2 queries
JOIN => one query to fetch both root entities and all of their mapped entity/collection = 1 query
So SELECT and JOIN are two extremes and SUBSELECT falls in between. One can choose suitable strategy based on her/his domain model.
By default SELECT is used by both JPA/EclipseLink and Hibernate. This can be overridden by using:
#Fetch(FetchMode.JOIN)
#Fetch(FetchMode.SUBSELECT)
in Hibernate. It also allows to set SELECT mode explicitly using #Fetch(FetchMode.SELECT) which can be tuned by using batch size e.g. #BatchSize(size=10).
Corresponding annotations in EclipseLink are:
#JoinFetch
#BatchFetch
"mxc" is right. fetchType just specifies when the relation should be resolved.
To optimize eager loading by using an outer join you have to add
#Fetch(FetchMode.JOIN)
to your field. This is a hibernate specific annotation.
The fetchType attribute controls whether the annotated field is fetched immediately when the primary entity is fetched. It does not necessarily dictate how the fetch statement is constructed, the actual sql implementation depends on the provider you are using toplink/hibernate etc.
If you set fetchType=EAGER This means that the annotated field is populated with its values at the same time as the other fields in the entity. So if you open an entitymanager retrieve your person objects and then close the entitymanager, subsequently doing a person.address will not result in a lazy load exception being thrown.
If you set fetchType=LAZY the field is only populated when it is accessed. If you have closed the entitymanager by then a lazy load exception will be thrown if you do a person.address. To load the field you need to put the entity back into an entitymangers context with em.merge(), then do the field access and then close the entitymanager.
You might want lazy loading when constructing a customer class with a collection for customer orders. If you retrieved every order for a customer when you wanted to get a customer list this may be a expensive database operation when you only looking for customer name and contact details. Best to leave the db access till later.
For the second part of the question - how to get hibernate to generate optimised SQL?
Hibernate should allow you to provide hints as to how to construct the most efficient query but I suspect there is something wrong with your table construction. Is the relationship established in the tables? Hibernate may have decided that a simple query will be quicker than a join especially if indexes etc are missing.
Try with:
select p from Person p left join FETCH p.address a where...
It works for me in a similar with JPA2/EclipseLink, but it seems this feature is present in JPA1 too:
If you use EclipseLink instead of Hibernate you can optimize your queries by "query hints". See this article from the Eclipse Wiki: EclipseLink/Examples/JPA/QueryOptimization.
There is a chapter about "Joined Reading".
to join you can do multiple things (using eclipselink)
in jpql you can do left join fetch
in named query you can specify query hint
in TypedQuery you can say something like
query.setHint("eclipselink.join-fetch", "e.projects.milestones");
there is also batch fetch hint
query.setHint("eclipselink.batch", "e.address");
see
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
I had exactly this problem with the exception that the Person class had a embedded key class.
My own solution was to join them in the query AND remove
#Fetch(FetchMode.JOIN)
My embedded id class:
#Embeddable
public class MessageRecipientId implements Serializable {
#ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY)
#JoinColumn(name="messageId")
private Message message;
private String governmentId;
public MessageRecipientId() {
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
public String getGovernmentId() {
return governmentId;
}
public void setGovernmentId(String governmentId) {
this.governmentId = governmentId;
}
public MessageRecipientId(Message message, GovernmentId governmentId) {
this.message = message;
this.governmentId = governmentId.getValue();
}
}
Two things occur to me.
First, are you sure you mean ManyToOne for address? That means multiple people will have the same address. If it's edited for one of them, it'll be edited for all of them. Is that your intent? 99% of the time addresses are "private" (in the sense that they belong to only one person).
Secondly, do you have any other eager relationships on the Person entity? If I recall correctly, Hibernate can only handle one eager relationship on an entity but that is possibly outdated information.
I say that because your understanding of how this should work is essentially correct from where I'm sitting.

Categories

Resources