How to get related objects with Hibernate

I am developing an application using Spring rest and Hibernate, and I want to get nested records from the database, how do I get Profession for User , now I want to get Users associated with Profession I selected earlier.

Here is my Dao class

 @SuppressWarnings({ "unchecked", "rawtypes" }) public List<Profession> getProfessionById(long id) throws Exception { session = sessionFactory.openSession(); Criteria cr = session.createCriteria(Profession.class); cr.add(Restrictions.eq("uid", id)); List results = cr.list(); tx = session.getTransaction(); session.beginTransaction(); tx.commit(); return results; } 
+7
java spring orm hibernate jpa
source share
7 answers

Selection strategies

There are four sampling strategies.

  • fetch- "join" = disable lazy loading, always load all collections and objects.
  • fetch- "select" (default) = Lazy loads all collections and objects.
  • batch-size = "N" = Get up to 'N collections or objects, Not record.
  • fetch- "subselect" = group your collection into a sub select statement.

You can check the detailed explanation in the Hibernate documentation.

FetchType.LAZY available upon request.

FetchType.EAGER is direct

 @SuppressWarnings({ "unchecked", "rawtypes" }) public List<User> getProfessionById(long id) throws Exception { session = sessionFactory.openSession(); Criteria cr = session.createCriteria(Profession.class, "pro") .setFetchMode("user", FetchMode.JOIN); cr.add( Restrictions.eq("uid", id)); Profession pro = cr.uniqueResult(); tx = session.getTransaction(); session.beginTransaction(); tx.commit(); return pro.getUsers(); } 
+2
source share

First you need to add below the mapping in the class class of the Profession and User class

Classroom Profession

 //bi-directional many-to-one association to Users @OneToMany(mappedBy="profession", cascade=CascadeType.ALL) private List<User> users; public List<User> getUsers() { return this.users; } public void setUsers(List<User> users) { this.users = users; } 

In a custom object class

 @ManyToOne(fetch=FetchType.EAGER) @JoinColumn(name="profession_id") private Profession profession; 

Then you can get the profession object by id using the existing DAO class code.

 @SuppressWarnings({ "unchecked", "rawtypes" }) public List<Profession> getProfessionById(long id) throws Exception { session = sessionFactory.openSession(); Profession profession = (Profession) session.get(Profession.class, id); // here you can get list of users for this profession // List<User> users = profession.getUsers(); return profession; } 
+2
source share

I think that you are doing something wrong in your DAO class, because you are opening a session and transaction inside your method and probably inside each of the DAO methods.

It has a cost in productivity and poor conceptually, a transaction should group a set of operations that will be completely successful or fail. In most cases, the database transaction will be associated with the entire business operation, in this case with a REST request. Something like

POST / user

@ RestController.createUser

open transaction

UserDAO.saveUser

make a transaction

answer

Also, if you look at your code, you open a transaction and then close.

 tx = session.getTransaction(); session.beginTransaction(); tx.commit(); 

In this case, you query the database so that the transaction is not needed at all.

Transactions are a cross-cutting issue in your application, and given that you are already using Spring, you should look at the @Transactional annotation (or its xml equivalent) to achieve the transaction with AOP (Spring creates an aroud aspect). This will make your code more readable and maintainable.

@ManojP's answer is good and bad. I think you should avoid bidirectional relationships whenever possible, because it complicates the design. My advice is this: always start with a one-way relationship, and if you find a case where you cannot avoid it, use it. The good thing is that using laziness shows you when he does it:

 List<User> users = profession.getUsers(); 

This line of code must be outside the DAO. It happens that you have a list of users marked as lazy (by default), and then, when you select professions with a request for criteria, the selection by professions of the table is launched, and each of the Profession objects is created using instead of the simplest collection. When you call the profession .getUsers (), the new element is selected by a trigger on the user table, where professional_id = profession.getId (). So:

  • List of results = criteria .list ();
  • Select * from Profession
  • professionA.getUsers ();
  • Select * from user, where profession_id =: professionA.getId ()

But be careful! if you have a collection of professions (for example, I think you have one because you are returning the list) and repeat each profession and specify a list of users that you will do:

  • List of results = criteria .list ();
  • Select * from Profession
  • for each profession β†’ profession.getUsers
  • Select * from user, where profession_id =: professionA.getId ()
  • Select * from user, where profession_id =: professionB.getId ()

This will have poor performance.

@Farvilain's answer is good. But in this case, you will always receive a Profession with a collection of Users, because inside your DAO you always use FetchMode.JOIN, and then lose the advantage of laziness. So, if you always want a list of users when requesting Professions, use lazy = true for the collection of users, but keep in mind what costs may be incurred (if the User does not have lazy collection A, when you request Professions you will also get a list of users plus compilation A). If you do not want this, you might have this signature:

 public List<Profession> getProfessionById(Long id, FetchMode fetchMode) throws Exception 

This is not very nice, but shows that the DAO client can select fecth mode.

+2
source share

According to your query, the Profession table has a uid column, which is probably an FK in the Users table, and I think the Users table should have an FK for the profession.

So, the Users table will have a many-to-one association with Profession :

 @ManyToOne private Profession profession; 

and Profession can associate all users with a certain profession, so in the Profession object you have the flip side of this association:

 @OneToMany(mappedBy = "profession") private List<Users> users = new ArrayList<>(); 

Now, to get all users with a profession, you can run a simple query:

 List<Users> users = (ist<Users>) session.createQuery( "select u " + "from Profession p " + "join fetch p.users u " + "where p.id = :id") .setParameter("id", proffesionId) .list(); 
+1
source share

Starting with your code

 @SuppressWarnings({ "unchecked", "rawtypes" }) public List<Profession> getProfessionById(long id) throws Exception { session = sessionFactory.openSession(); Criteria cr = session.createCriteria(Profession.class); cr.add(Restrictions.eq("uid", id)); List results = cr.list(); tx = session.getTransaction(); session.beginTransaction(); tx.commit(); return results; } 

First I suggest you use the Transactionnal annotation if you use Spring. This makes the code clearer. Then stop using SuppressWarnings, it is useless and useless, you can configure your IDE to hide them.

 @Transactionnal(readonly = true) public List<Profession> getProfessionById(long id) throws Exception { Criteria cr = session.createCriteria(Profession.class); cr.add(Restrictions.eq("uid", id)); List results = cr.list(); return results; } 

Do not use the fact that the criteria are free APIs and do not create a local variable, now it is not included.

 @Transactionnal(readonly = true) public List<Profession> getProfessionById(long id) throws Exception { return session .createCriteria(Profession.class) .add(Restrictions.eq("uid", id)) .list(); } 

Now solve your problem

 @Transactionnal(readonly = true) public List<Profession> getProfessionById(long id) throws Exception { return session .createCriteria(Profession.class) .add(Restrictions.eq("uid", id)) .setFetchMode("users", FetchMode.JOIN) .list(); } 

This requires, of course, this line in the profession;

 public class Profession { [...] @OneToMany(mappedBy = "profession") private Set<Users> users = new HashSet<>(); [...] } 

Attention

You do not need a getter / setter, do not call backward mapping in the absence.

Be sure not to use List, Hibernate doesn't really like List without order declaration.

Do not declare users in the same way as Eagerly in essence, you will experience a terrible problem every time you download a profession belonging to a large number of users, even if you are not good at getting them yourself.

Do not use FetchMode.EAGER, it is deprecated!

+1
source share

You may be able to use the following:

 Criteria cr = session.createCriteria(Profession.class); cr.setFetchMode("user", FetchMode.EAGER); cr.add(Restrictions.eq("uid", id)); List results = cr.list(); 

I did not use the Hibernate criteria, but the quick google came up with something similar.

0
source share

Use ManyToMany mapping for professions so that assosoated Users are also added. Check out this link.

0
source share

All Articles