Hibernate: Advanced_Example

The example we have just discussed is very simple; we have only tried inserting in a table, we still need to know how to edit, delete, how to map and deal with relations.

This section will try to cover these issues and complete working our example; but first we will do some cleaning to our project (This is optional).

  • We will use the class 'HibernateUtil' for getting the SessionFactory.
    • Create the class HibernateUtil and place this code

      import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static final SessionFactory sessionFactory = buildSessionFactory(); private static SessionFactory buildSessionFactory() { try { // Create the SessionFactory from hibernate.cfg.xml return new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }

    • In SimpleTest.java,

      Replace

      SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession();

      with

      Session session = HibernateUtil.getSessionFactory().getCurrentSession();

  • Use some packages
    • Create two Packages 'hibernate_Classes' and 'hibernate_Tests'. R-Click your project / New / Package
    • Put SimpleTest.java in the Package 'hibernate_Tests', by Dragging and Dropping or Copying and Pasting.
    • Put 'Lecturer.java', and HibernateUtil.java in the Package 'hibernate_Classes'.
    • In the mapping file, we want to tell Hibernate that this Lecturer Class is located in the Package 'hibernate_Classes'.

      Add 'package="hibernate_Classes"' to the hibernate-mapping element

      <hibernate-mapping package="hibernate_Classes">

    • Import the new Package in SimpleTest.java.

    Try running the SimpleTest again, to ensure that nothing went wrong.

Now we are done with the INSERT, so let's try to UPDATE, DELETE, and SELECT.

We are going to create these commands for the 'Lecturer' table, so let's create a class named 'LecturersHandler' in 'Hibernate_Tests' package and add to it a method for each command, then test them in a main method.

So the methods in our class will be

  • AddNewLecturer for the INSERT
  • DeleteLecturer for the DELETE
  • EditLecturer for the UPDATE
  • GetAllLecturers and SearchFirstNames for the SELECT
  • A main method, and some helping methods for printing

It is a very simple method that takes a Lecturer as a parameter and saves it..

  • The method:

    public void AddNewLecturer (Lecturer L1) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); session.save(L1); tx.commit(); }

    You are expected to be understanding every statement in the method; if not, please read the explanation of the SimpleTest.java

  • Test it in the main method:

    LecturersHandler handler = new LecturersHandler(); Lecturer L1 = new Lecturer(); // ----------- Add ------------- L1.setFirstName("Slim"); L1.setLastName("Abdennadher"); handler.AddNewLecturer(L1);

  • Run your code and Check your database.

Common sense...,

we UPDATE a record by setting the values of certain fields, Where the Primary key is equal to a certain value.

The same takes place here; we have an object to be updated,

  • The Primary key of the object specifies which record is to be updated, and
  • The values of the object's variables represent the new record values.

  • The method:

    It is the same like AddNewLecturer, except for session.save(L1), we use session.update(L1) instead. So copy and paste the same method, change its name and replace session.save.

  • Test it in the main method:

    After the statements you have added for testing the AddNewLecturer method, Add these:

    // ----------- Edit ------------- L1.setFirstName("SLIM"); handler.EditLecturer(L1);

  • Run your code and Check your database.

It works the same like EditLecturer, when deleting a certain record, we Delete where the ID is equal to a certain value.

Also, the same takes place here, we have an object to be Deleted,

  • The ID of the object specifies which record to be Deleted.

  • The method:

    Again the same like the previous ones, but instead of

    session.save(L1)

    we use

    session.delete(L1)

  • Test it in the main method:

    After the statements you have added for testing the AddNewLecturer method, Add these:

    // ---------- Delete ------------ handler.DeleteLecturer(L1);

  • Run your code and Check your database.

Now, we are done with the Add, Update, and Delete. Let's move to the Select.

The previous three methods where about updating the database, now we will move to retrieving from the Database.

If you need to retrieve a certain object and you already know its identifier, you will be able to 'load' it using a statement similar to this:

session.load(Lecturer.class, lecturerID)

Else, if you do not know the identifier, or you do not need a certain object, you will have to query the database.

Hibernate supports three ways for querying the database; they are:

  1. Using Hibernate Query Language (HQL).
  2. Using Criteria Queries
  3. Using Native SQL.

To have a quick overview on the three approaches; This is how to retrieve Lecturers whose First Names are 'Fatma', using the three approaches: // Do not test them now

  1. HQL
    • Simple way:

      Query namesSearch = session.createQuery("from Lecturer where firstName = 'Fatma' ");

    • Or Using positional parameters:

      Query namesSearch = session.createQuery("from Lecturer where firstName = ? "); namesSearch.setString(0, "Fatma");

    • Or Using named Parameters:

      Query namesSearch = session.createQuery("from Lecturer where firstName = :fn "); namesSearch.setString("fn", "Fatma");

    Starting the query with 'from' means you are selecting the whole object.

    The second and the third approaches work with binding parameters. From their names, it is clear that positional parameters depend on the position of the parameter (the index of the question '?' mark with a count starting from zero), while the named parameters depend on the parameter name.

  2. Criteria Query

    Criteria namesSearch = session.createCriteria(Lecturer.class); namesSearch.add(Restrictions.eq("firstName", "Fatma"));

    Restriction.eq represents the 'equal' restriction.

  3. Native SQL

    SQLQuery namesSearch = session.createSQLQuery("Select * From Lecturer where FName = 'Fatma'"); namesSearch.addEntity(Lecturer.class);

    .addEntity (Lecturer.class) tells Hibernate that the result is to be in the form of 'Lecturer' Objects, else the values will be selected separately (i.e. firstName, lastName are two Strings saved as two separated objects).

    Note:

    To refer to the property 'First Name', we use the corresponding variable name 'firstName', except in the Native SQL approach, we are use the table column name FName.

    All these approaches are for constructing the query ( i.e. the query is not executed yet ). To execute the query we usually use .list() that returns a list of the results.

    List<Lecturer> lecturers = (List<Lecturer>)namesSearch .list();

    Now you can test the different approaches yourself, or by using the method in this text file.

    When you paste the method, you will have some errors. So move the mouse over the errors, eclipse will suggest some solutions, So choose to import 'org.hibernate' packages.

Through out the tutorial, we will be using simple HQL; you can read about Bind Parameters, Criteria Queries and Native SQL from JBoss Community Documentation.

Back to our planned methods

We still need to create the two methods:

  • GetAllLecturers and
  • SearchFirstNames

We will create a void method that returns a list of all Lecturers, then use another method to print the returned list.

  • The method:

    public List<Lecturer> GetAllLecturers () { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); List<Lecturer> lecturers = (List<Lecturer>)session.createQuery("from Lecturer").list(); tx.commit(); return lecturers; }

  • The printing method:

    public void PrintLecturersList (List<Lecturer> lecturers) { System.out.println (" ------ The Lecturers ------"); for (int i = 0; i < lecturers.size() ; i++) { System.out.println( lecturers.get(i).getFirstName() +" "+ lecturers.get(i).getLastName()); } System.out.println (" ---------------------------"); }

  • Test it in the main method:

    handler.PrintLecturersList(handler.GetAllLecturers());

  • Run your code and Check the Console.

Note that the entity objects returned by Hibernate are at Persistent states (Of course, unless you leave the context of the session i.e. after

tx.commit())

We not always select the whole object, you might need to select certain columns. In this method, we are going to select only the IDs and firstNames of Lecturers whose firstNames contain a certain string; and again we are going to use a printing method.

  • The method:

    public List<Object []> SearchFirstNames (String searchString) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); String query = "select ID , firstName from Lecturer where firstName Like '%" + searchString + "%'"; List<Object []> names = (List<Object []>)session.createQuery(query).list(); tx.commit(); return names; }

    As you can see in the method, when we specify certain columns to be selected, the returned list is a list of Tuples, each tuple is represented by an array of objects. In other words, The returned list is a 'List of Arrays of Objects'.

  • The printing method:

    public void PrintNamesAndIDs (List<Object []> NamesAndIDs) { System.out.println( "ID\tFirst Name"); for (int i = 0 ; i < NamesAndIDs.size() ; i++) { Integer ID = (Integer)NamesAndIDs.get(i)[0]; String Name = (String)NamesAndIDs.get(i)[1]; System.out.println( ID.toString() + "\t" + Name); } }

  • Test it in the main method:

    handler.PrintNamesAndIDs(handler.SearchFirstNames("m"));

  • Run your code and Check the Console.

Now, you are done with about 2/3 of the tutorial, you are just missing the Relations. So take a deep breath, we are moving to relations...!!! Do not worry, they are simple. It is the Art of ORM.

Well... we will start by the simplest relation which is many-to-one.

We will apply it to our example by having a 'Course' entity that is taught by a 'Lecturer'.

  • At the Database level: A Course has a foreign key 'LecturerID'.

    • Create the table Course; you can use this query:

      Create Table Course( ID int, Course_Title nvarchar(50), Lecturer_ID int, Primary Key (ID), Foreign Key (Lecturer_ID) References Lecturer)

    • Insert some records in your Tables to help us while testing.

  • At the application Level

    We said before that Hibernate allows you to work in the whole application with an Object Oriented paradigm, so of course Hibernate will not represent the Lecturer teaching a Course by a 'Foreign Key'; Instead, we will be able to represent it by the whole object of the lecturer (i.e. An instant of the class 'Course' will be having an instant variable of type 'Lecturer' that represents the lecturer teaching it) // this is one way for dealing with such a relation.

    • Create the Class 'Course' in hibernate_Classes package

      with the instant variables:

      • private int ID;

      • private String title;

      • private Lecturer lecturer;

      And their getters and setters.

      Eclipse can generate the getters and setters for you this way:

      • R-Click anywhere in your 'Course' Class
      • Select Source / Generate Getters and Setters
      • Select All / Ok

    • Create the Mapping

      You can either create a new mapping file 'Course.hbm.xml' for example, or complete working on the file 'Lecturer.hbm.xml'

      Add the mapping for the 'ID' and 'title' like we have done before for the 'lecturer'.

      As for the 'lecturer' variable, instead of mapping it using the element 'property', we use the element many-to-one since many courses can be taught by one Lecturer. This is expressed this way:

      <many-to-one name="lecturer" class="Lecturer" column="Lecturer_ID" />

      It can be read:

      A many-to-one relation where The variable of name 'lecturer' is an instant of the class 'Lecturer' (i.e. representing a record from the table 'Lecturer' that is represented by the class 'Lecturer'). And the corresponding foreign key column in this table, the 'Course' table, is Lecturer_ID.

    • Add it to Hibernate configurations

      If you have created a new file for the 'Course' mapping, you have to tell Hibernate about it. So add a Resource reference for it in Hibernate.cfg.xml.

      <mapping resource="Course.hbm.xml" />

    Run SimpleTest.java as it is again, to compile the new mapping and ensure it has no errors.

    It should run normally and insert again in the 'Lecturer' Table.

Now let's learn how use it...

We will be creating some methods that are accessing the Table 'Course', so we will put them in a new class named 'CoursesHandler.java' in the 'hibernate_Tests' package.

The Add, Edit, and Delete are almost the same as the previous ones, so we will pass them and move on to retrieving. We will create a method that prints all Courses and their Lecturers.

  • The method:

    public void ListCourseAndLecturer () { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); String query = "from Course crs"; List results = session.createQuery(query).list(); // ------------- Printing the content of the list ---------------- System.out.println (" -- Courses and Lecturers --"); for (int i = 0; i < results.size() ; i++) { Course crs = (Course)results.get(i); System.out.println( crs.getTitle()+ "\t" + crs.getLecturer().getFirstName()); } System.out.println (" ---------------------------"); tx.commit(); }

  • Test it in the main method

    CoursesHandler handler = new CoursesHandler(); handler.ListCourseAndLecturer();

Note that this time we printed inside the method before tx.commit(), this is because the related objects are not loaded by Hibernate automatically (i.e. the Lecturer objects are not loaded to the Courses objects automatically), so we had to access them while being attached to the session. To be able to access the lecturer of the Course when the object is detached, we have to explicitly tell Hibernate to 'fetch' the 'lecturer'.

One way to do this is through the HQL statement used in the query, like this one for example

Select crs From Course crs left join fetch crs.lecturer

This HQL returns a list if Courses with their lecturers fetched (i.e. the lecturer variable in each course is loaded).

Let's see it working. We will use a method to get the list and a printing method to display the returned list.

  • The method:

    public List<Course> GetAllCourses () { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); String query = "Select crs From Course crs left join fetch crs.lecturer"; List<Course> courses = (List<Course>)session.createQuery(query).list(); tx.commit(); return courses; }

  • The printing method:

    public void printCoursesList (List <Course> courses) { System.out.println (" -- Courses and their Lecturers --"); for (int i = 0; i < courses.size() ; i++) { Course crs = (Course)courses.get(i); System.out.println( crs.getTitle()+ "\t by Dr. " + crs.getLecturer().getFirstName()); } System.out.println (" ---------------------------------"); }

  • Test it in the main method

    handler.printCoursesList(handler.GetAllCourses());

And this was the Many-To-One relation, you can read more about Many-To-One relation mapping from JBoss Community Documentation.

Well..., we have said that one way to represent the relation between the Course and the Lecturer is adding an instant variable of type Lecturer to the Class 'Course'. This relation could have been represented the other way around (i.e. The Lecturer could have a list of Courses he is teaching ); this is the One-To-Many relation.

We are going to proceed with our example to see this working, but instead of working on the relation between the 'Course' and the 'Lecturer', we will use the new entity 'Teaching Assistant', where a Course can have many TAs, while the TA is assisting in one course.

  • At the Database level: a TA has a foreign key 'CourseID'.

    • Create the table TA; you can use this query:

      Create Table TA( ID int, FirstName nvarchar(50), LastName nvarchar(50), CourseID int, Primary Key (ID), Foreign Key (CourseID) References Course)

    • Insert some records in the Table

  • At the application level

    We are going to repeat the steps we did for the course, with some little changes. Since this time the relation is reflected on the Course, so the List of TAs will be added to Course.java and its Mapping will be added to the Course mapping.

    • Create the Class 'TeachingAssistant' in hibernate_Classes package

      with the instant variables:

      • private int ID;

      • private String firstName;

      • private String lastName;

      And their getters and setters.

    • Add the TAs list to Course.java,

      • Add the instant variable

        private List TAs;

      • Create its getter and setter methods

    • Create the Mapping

      Again, you can either create a new mapping file 'TeachingAssistant.hbm.xml' for example, or complete working on a previously created mapping file.

      Add the mappings for the 'ID' , 'firstName' and 'lastName' normally like we did before.

    • Add the TAs list mapping

      This time the relation is One-to-Many (a Course can have many TAs), so we will be using the element 'one-to-many' , and since we are going to represent the TAs by a List, we will also be using the element bag , this way:

      <bag name="TAs" > <key column="CourseID" /> <one-to-many class = "TeachingAssistant"/> </bag>

      It can be read:

      The variable of name "TAs" , is representing a one-to-many relation with the table represented by the class "TeachingAssistant" , where the foreign key column is CourseID .

      Colloectiona can also be represented using a 'set' or 'map' or 'array' ... etc, this depends on the type of collection you are using (in our case, we used a List). Read about these different Types of Collections from a tutorial by Gary Mak, in this pdf (pages 4-6) he explains each of them separatly. You can also read about Collection Mapping from JBoss community documentation.

    • Add it to Hibernate configurations

      Do like what you have done when mapping the Course.

    Run SimpleTest.java, to check for errors.

Now, let's use it...

Getting all Courses and their TAs will look the same like Getting the Courses and their Lecturers, you can test it yourself using this HQL:

Select crs From Course crs left join fetch crs.TAs

What may look different is Adding a TA to a Course or Removing a TA from a course, so we will try these.

Since the TA-Course relation is represented in the TAs list of the Course Class, So working on this relation means working on that List (i.e. Adding and Removing a TA from a Course is Adding and Removing from the List of that Course's TAs).

We will create a method that given a certain TA's ID and a Course's ID, adds the CourseID to the TA by adding this TA to the List of the Course's TAs. So in the method, we will first get the TA with that ID, then add him to the list. // We will complete working on CoursesHandler, since we are still dealing with Courses.:

  • The method:

    public void AddTAToCourse (int CourseID, int TAID) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); String query = "Select crs From Course crs left join fetch crs.TAs where crs.ID = '"+ CourseID + "' "; Course c1 = (Course)(session.createQuery(query).list().get(0)); TeachingAssistant t1 = (TeachingAssistant)session.load(TeachingAssistant.class, TAID); c1.getTAs().add(t1); tx.commit(); }

  • Test it in the main method:

    handler.AddTAToCourse(1, 1);

    // make sure you have TAs and Courses in your database.

Removing a TA from a course will look the same except for

c1.getTAs().add(t1)

, it will be instead

c1.getTAs().remove(t1)

Before moving to the Many-To-Many relation, there is a point to be mentioned here. In our example we used 'join fetch', this means that our 'fetch' is with type 'join'. In the 'join' fetch, Hibernate gets the related objects (the TAs objects to be loaded to the TAs list) in one Outer Join Select statement.

Select ... From Course crs left outer join TA tas on crs.ID = tas.CourseID

There is another type for fetch which is 'Select', Where Hibernate retrieves those related objects one by one, each in a separate Select statement.

Select … From Course Select … From TA Where TA.CourseID = ... Select … From TA Where TA.CourseID = ... Select … From TA Where TA.CourseID = ...

Also fetching the collection (the TAs list in our example) can take place either when the collection is accessed or without the collection being accessed. This has to do with the so called 'Lazy' property. We will not go into these details to keep it simple, and you can read about the 'Lazy' property and 'fetching' strategies in details from this presentation pdf by Sang Shin, or have an overview from hibernate.org.

Now we are moving to the last part in our tutorial, which is the Many-To-Many relation.

In the previous relations, we where choosing between either to map it many-to-one or one-to-many. i.e., for the Lecturer and Course, we had the choice to create a Lecturer instant variable for the class Course, or create a List of Courses for the class Lecturer.

Actually, we could have combined both together; this is called 'Bidirectional mapping'. In a Bidirectional mapping, the relation is represented in both sides. (i.e., The Lecturer has a List of Courses, and the Course has a Lecturer object).

Mapping the relation from one side, like we have done, is called 'Unidirectional mapping'.

We will try the Bidirectional mapping along with the Many-to-Many relation, by adding to our example the entity Student, where a Student can have many Courses and a Course can have many Students.

  • At the Database level: an Enroll table represents the relation

    • Create the table Student; you can use this query:

      Create Table Student( ID int, FirstName nvarchar(50), LastName nvarchar(50), Primary Key (ID))

    • Create the table Enroll; you can use this query:

      Create Table Enroll ( CourseID int, StudentID int, Foreign Key (CourseID) References Course, Foreign Key (StudentID) References Student)

    • Insert some records in the Tables

  • At the application level

    This time we will be creating the Class Student, adding to it a List of Courses; and adding to the class Course, a List of Students.

    • Create the Class 'Student' in hibernate_Classes package

      with the instant variables:

      • private int ID;

      • private String firstName;

      • private String lastName;

      • private List courses;

      And their getters and setters.

    • Add the Students list to Course.java,

      Add the instant variable

      private List students;

      Create its getter and setter methods

    • Create the Mapping

      Add the mappings for the 'ID' , 'firstName' and 'lastName' normally like we did before.

      Add the mapping for the 'courses'

      This is a many-to-many relation, so we will be using the element many-tomany, and again we are using a List, so we will be using the bag, this way

      <bag name="courses" table="Enroll"> <key column="StudentID" /> <many-to-many column="CourseID" class = "Course"/> </bag>

      It can be read:

      The list with the name "courses" is representing a many-to-many relation with the table represented by the class "Course", where the relation is represented by the table "Enroll". The key of this Class (Student) in the relation is StudentID , and the key of the class "Course" is CourseID.

    • Add the 'students' list mapping

      The same way we did for the courses List. // this time the mapping is in the Course Class mapping

      <bag name="students" table="Enroll" > <key column="CourseID" /> <many-to-many column="StudentID" class = "Student"/> </bag>

      It can be read:

      The list with the name "students" is representing a many-to-many relation with the table represented by the class "Student", where the relation is represented by the table "Enroll". The key of this Class (Course) in the relation is CourseID , and the key of the class "Student" is StudentID.

    • Add it to Hibernate configurations

      Do like we have done for the previous mappings

    Run SimpleTest.java, to check for errors.

Now let's use it...

Dealing with a Many-to-Many relation will look the same like that of the One-to-Many relation, the difference we have here is that of the Bidirectional vs the Unidirectional mapping. This time, we can simply get the Students of a Course, and get the Courses of a Student. To add a student to a Course, we are going to add the Student to the Students list in the Course, and add the Course to the Courses list in the Student; and the same for removing.

We will be implementing a method to get the Students of a Course, and you can try getting the Courses of a Student yourself.

  • The method:

    public List<Student> GetCourseStudents(int courseID) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); String query = "Select crs from Course as crs left join fetch crs.students" + " where crs.ID='"+ courseID + "' "; Course s1 = (Course)(session.createQuery(query).uniqueResult()); tx.commit(); return s1.getStudents(); }

    The .uniqueResult() is used when you are sure that the query is returning one object and not a list of objects.

  • The printing method:

    public void printStudentsList (List<Student> students) { System.out.println (" ---------- Students ----------"); for (int i = 0; i < students.size() ; i++) { Student student = (Student)students.get(i); System.out.println ( student.getFirstName() + " " + student.getLastName()); } System.out.println (" ------------------------------"); }

  • Test it in the main method

    handler.printStudentsList(handler.GetCourseStudents(1));

Now we will try Adding a Student to a Course, and the Removing works the same so you can do it yourself.

  • The method:

    public void AddStudentToCourse(int studentID, int courseID) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction tx = session.beginTransaction(); String query = "select student from Student student " + "left join fetch student.Courses where student.ID='"+ studentID + "' "; Student s1 = (Student)(session.createQuery(query).uniqueResult()); String query2 = "select crs from Course crs left join fetch crs.Students " + "where crs.ID='"+ courseID + "' "; Course c1 = (Course)(session.createQuery(query2).uniqueResult()); s1.getCourses().add(c1); c1.getStudents().add(s1); tx.commit(); }

    We could have used the commented statement

    c1.getStudents().add(s1);

    instead of

    s1.getCourses().add(c1);

    Both will be doing the same job.

  • Test it in the main method:

    handler.AddStudentToCourse(1, 2);

Run your code and then check the Table enroll.

In a Bidirectional mapped relation, the two sides of the relation are allowed to access the relation, we can restrict the update of the relation to be done by only one of them, sometimes referred to as an 'owner'. This is done by setting the property 'inverse' of the non-owner to true; so for example if we do not want the relation to be updated except from the 'Course' side (i.e. the Students List of the Course), we will have this in our Student mapping:

<bag name="courses" table="Enroll" inverse="true">

In this section, we have discussed the

  • Many-to-One Unidirectional mapping
  • One -To-Many Unidirectional mapping
  • Many-to-Many Bidirectional mapping

Each of the Unidirectional could have been mapped Bidirectional, and vice versa. Also for the first and the second, we could have used join tables.

Read more about Association Mapping from JBoss Community Documentation.

Finally, You are Done now...

Do not forget that this tutorial aimed at getting you started with Hibernate, so if you want complete with Hibernate, you will need to do some further readings.

If you find JBoss Community Documentation a helpfull reference, so here is its Outline, where you can find almost everything you may need.

RenewSession