CRUD Operations with Hibernate (Create, Read, Update, Delete)

This is a hands-on lesson demonstrating how to perform basic CRUD operations using Hibernate. You'll learn how to save new objects to the database (Create), retrieve objects (Read), modify existing objects (Update), and delete objects (Delete) using Hibernate's API.


Reading (Retrieving) Objects with Hibernate

Hibernate provides several mechanisms for retrieving objects from the database. The most common methods are session.get(), session.load(), and HQL/Criteria queries. Understanding the differences between these methods and how to handle potential exceptions like ObjectNotFoundException is crucial for effective data retrieval.

Retrieving Objects with session.get()

The session.get() method retrieves an object from the database based on its primary key. If the object exists, it's returned immediately. If the object does not exist, session.get() returns null.

Example:

Session session = sessionFactory.openSession();
try {
    Student student = session.get(Student.class, 123); // 123 is the primary key
    if (student != null) {
    System.out.println("Student Name: " + student.getName());
} else {
    System.out.println("Student with ID 123 not found.");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}

Explanation:

  • The code opens a Hibernate session.
  • session.get(Student.class, 123) attempts to retrieve a Student object with the primary key 123.
  • If a Student object with that ID exists, it's returned and its name is printed.
  • If no such Student exists, student will be null, and a "not found" message is printed.
  • The session is closed in the finally block to ensure proper resource management.

Retrieving Objects with session.load()

The session.load() method also retrieves an object based on its primary key, but it behaves differently from session.get(). Instead of immediately hitting the database, session.load() returns a proxy object. This proxy object only retrieves the data from the database when you actually access one of its properties. If the object does not exist in the database, accessing a property of the proxy object will result in an ObjectNotFoundException being thrown.

Example:

 Session session = sessionFactory.openSession();
try {
    Student student = session.load(Student.class, 456); // 456 is the primary key

    // No database access yet.  'student' is a proxy object.
    System.out.println("Student class: " + student.getClass().getName());

    // Database access occurs here, when we try to get the name.
    // If student with ID 456 doesn't exist, ObjectNotFoundException will be thrown.
    System.out.println("Student Name: " + student.getName());

} catch (ObjectNotFoundException e) {
    System.out.println("Student with ID 456 not found (ObjectNotFoundException).");
} catch (Exception e) {
    e.printStackTrace();
} finally {
    session.close();
} 

Explanation:

  • session.load(Student.class, 456) returns a proxy object representing the Student with ID 456. No database query is executed at this point.
  • The System.out.println("Student class: " + student.getClass().getName()); will print the proxy class name.
  • The database is accessed only when student.getName() is called.
  • If the student with ID 456 doesn't exist, an ObjectNotFoundException is thrown during the student.getName() call.

Key Differences between get() and load():

  • get() returns null if the object isn't found. load() throws an ObjectNotFoundException when you try to access a property of the proxy object if the object doesn't exist.
  • get() always hits the database immediately. load() returns a proxy object and only hits the database when needed (lazy loading).
  • Use get() when you need to check if an object exists immediately. Use load() when you're certain the object exists, and you want to potentially defer database access for performance reasons.

Retrieving Objects with HQL/Criteria Queries

Hibernate Query Language (HQL) and Criteria queries provide more flexible ways to retrieve data. They allow you to specify complex criteria for selecting objects.

Example (HQL):

 Session session = sessionFactory.openSession();
try {
    String hql = "FROM Student WHERE name = :studentName";
    Query<Student> query = session.createQuery(hql, Student.class);
    query.setParameter("studentName", "Alice");

    List<Student> students = query.list();

    if (!students.isEmpty()) {
        for (Student student : students) {
            System.out.println("Student ID: " + student.getId() + ", Name: " + student.getName());
        }
    } else {
        System.out.println("No students found with the name 'Alice'.");
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    session.close();
} 

Explanation:

  • The code defines an HQL query to retrieve all Student objects where the name property is equal to "Alice".
  • query.setParameter("studentName", "Alice") sets the value of the named parameter :studentName in the HQL query. This is best practice to prevent SQL injection.
  • query.list() executes the query and returns a list of Student objects.
  • The code iterates through the list and prints the ID and name of each student.

Example (Criteria API):

 Session session = sessionFactory.openSession();
try {
    CriteriaBuilder builder = session.getCriteriaBuilder();
    CriteriaQuery<Student> criteria = builder.createQuery(Student.class);
    Root<Student> root = criteria.from(Student.class);
    criteria.select(root).where(builder.equal(root.get("city"), "New York"));

    List<Student> students = session.createQuery(criteria).getResultList();

    if (!students.isEmpty()) {
        for (Student student : students) {
            System.out.println("Student ID: " + student.getId() + ", Name: " + student.getName() + ", City: " + student.getCity());
        }
    } else {
        System.out.println("No students found from 'New York'.");
    }


} catch (Exception e) {
    e.printStackTrace();
} finally {
    session.close();
} 

Explanation:

  • This example shows how to use the Criteria API to build a query to retrieve Student objects whose city is "New York".
  • It uses the CriteriaBuilder to create a CriteriaQuery.
  • The Root<Student> represents the root entity in the query (i.e., the Student entity).
  • The where clause specifies the filtering criteria.
  • The session.createQuery(criteria).getResultList() executes the query and returns the result as a list of Student objects.

Handling ObjectNotFoundException

The ObjectNotFoundException is thrown by session.load() (or when accessing lazy-loaded properties) when an object with the specified ID does not exist in the database. It's important to handle this exception to prevent your application from crashing and to provide a user-friendly error message.

Example (Handling ObjectNotFoundException):

 Session session = sessionFactory.openSession();
try {
    Student student = session.load(Student.class, 999); // Assuming 999 is a non-existent ID

    //This will cause ObjectNotFoundException
    System.out.println("Student Name: " + student.getName());

} catch (ObjectNotFoundException e) {
    System.out.println("Error: Student with ID 999 not found.");
    // Optionally log the error or take other appropriate action.
} catch (Exception e) {
    e.printStackTrace(); // Handle other potential exceptions
} finally {
    session.close();
} 

Explanation:

  • The code attempts to load a Student object with the ID 999.
  • If a student with that ID doesn't exist, accessing student.getName() will throw an ObjectNotFoundException.
  • The catch (ObjectNotFoundException e) block catches the exception and prints an informative error message to the console. In a real application, you might log the error or display a user-friendly message on the UI.
  • The catch (Exception e) is a general exception handler to catch any other unexpected exceptions.

Best Practices:

  • Choose the appropriate retrieval method (get(), load(), HQL/Criteria) based on your application's requirements.
  • Handle ObjectNotFoundException gracefully, providing informative error messages or taking appropriate action.
  • Use parameterized queries (with HQL/Criteria) to prevent SQL injection.
  • Always close your Hibernate sessions in a finally block to ensure resources are released.