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.
Hibernate SessionFactory and Session
Hibernate SessionFactory and Session Explained
Hibernate is an Object-Relational Mapping (ORM) framework for Java. It simplifies the interaction between Java applications and relational databases by mapping Java objects to database tables. Two key interfaces in Hibernate are SessionFactory
and Session
. Understanding their roles and lifecycles is crucial for using Hibernate effectively.
SessionFactory
The SessionFactory
is a heavyweight, immutable object that serves as a factory for creating Session
objects. It is typically created once per application and shared across multiple threads. Think of it as the "blueprint" or "recipe" for creating database connections and configuring Hibernate.
Key responsibilities of the SessionFactory
:
- Configuration Loading: It reads the Hibernate configuration file (typically
hibernate.cfg.xml
or configured programmatically) and loads all the necessary mappings and settings. - Connection Pooling: It manages a pool of database connections. This is crucial for performance, as establishing a new database connection is an expensive operation.
- Caching: It manages the second-level cache (optional), which can significantly improve performance by caching frequently accessed data.
- Metadata Management: It holds metadata about the mapping between Java classes and database tables.
Session
The Session
is a lightweight, single-threaded object that represents a unit of work with the database. It is short-lived and should be created and destroyed within a specific transaction or operation. Think of it as the "connection" or "conversation" with the database.
Key responsibilities of the Session
:
- Managing Persistent Objects: It manages the lifecycle of persistent objects (objects mapped to database tables). This includes creating, reading, updating, and deleting objects.
- Transaction Management: It provides methods for starting, committing, and rolling back transactions.
- Querying the Database: It provides methods for querying the database using HQL (Hibernate Query Language) or Criteria API.
- Caching: It manages the first-level cache (mandatory), which caches objects within the current session.
Understanding the Roles of SessionFactory
and Session
in Hibernate
The SessionFactory
is the factory for creating Session
objects. You typically create a single SessionFactory
instance when your application starts up and reuse it throughout the application's lifetime. Each request to the database (each "unit of work") requires a new Session
. The Session
is then used to interact with the database to persist, retrieve, update or delete entities.
Imagine the SessionFactory
as a power plant providing electricity. The electricity is the database connection. The Session
is the appliance using that electricity to perform a specific task (like saving data or retrieving data). You wouldn't have multiple power plants for each appliance; you have one power plant providing the electricity for all appliances. Similarly, you have one SessionFactory
providing Session
s for all database operations.
Creating a SessionFactory
and Obtaining Session
Instances
Here's an example of how to create a SessionFactory
and obtain Session
instances using Hibernate's programmatic configuration:
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.Session;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration().configure("hibernate.cfg.xml"); // Or specify a different configuration file
// You can also configure Hibernate programmatically
// configuration.setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver");
// configuration.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/mydatabase");
// configuration.setProperty("hibernate.connection.username", "myuser");
// configuration.setProperty("hibernate.connection.password", "mypassword");
// configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
return configuration.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;
}
public static Session getSession() {
return sessionFactory.openSession();
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
public static void main(String[] args) {
// Example usage
Session session = null;
try {
session = HibernateUtil.getSession();
session.beginTransaction();
// Perform database operations here (e.g., save an object)
// Example:
// User user = new User("John Doe", "john.doe@example.com");
// session.save(user);
session.getTransaction().commit();
} catch (Exception e) {
if (session != null && session.getTransaction() != null) {
session.getTransaction().rollback();
}
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
HibernateUtil.shutdown();
}
}
Explanation:
HibernateUtil
Class: This class encapsulates theSessionFactory
creation and provides utility methods to access theSessionFactory
andSession
. This is a common practice for managing Hibernate resources.buildSessionFactory()
Method: This method creates theSessionFactory
.- It creates a
Configuration
object. - It calls
configure("hibernate.cfg.xml")
to load the configuration from thehibernate.cfg.xml
file. Alternatively, you can configure Hibernate properties programmatically usingconfiguration.setProperty()
. - It calls
buildSessionFactory()
on theConfiguration
object to create theSessionFactory
.
- It creates a
getSessionFactory()
Method: This method returns the singletonSessionFactory
instance.getSession()
Method: This method opens a newSession
from theSessionFactory
. Each call will create a new session.shutdown()
Method: This method closes theSessionFactory
and releases all resources (connection pool, caches). It's important to call this when your application shuts down.main()
Method (Example Usage): Demonstrates how to obtain aSession
, begin a transaction, perform database operations, commit the transaction, and close theSession
. Error handling with rollback is included.
Important Notes:
- You'll need to replace the placeholder comments in the
main
method and the programatic configuration section with your actual Hibernate entity class and database details. - Ensure you have a valid `hibernate.cfg.xml` file in your classpath, or configure programmatically.
- Remember to add Hibernate dependencies to your project.
Managing Session
Lifecycle
The Session
is a short-lived object, and its lifecycle must be carefully managed. Here are the key aspects of managing the Session
lifecycle:
- Opening a
Session
: UseSessionFactory.openSession()
to open a newSession
. This creates a new database connection (or obtains one from the connection pool). - Transactions: All database operations should be performed within a transaction. Use
Session.beginTransaction()
to start a transaction. - Database Operations: Perform your CRUD (Create, Read, Update, Delete) operations using the
Session
object (e.g.,session.save()
,session.get()
,session.update()
,session.delete()
). - Committing or Rolling Back:
- If the database operations are successful, call
Session.getTransaction().commit()
to commit the transaction. This persists the changes to the database. - If an error occurs, call
Session.getTransaction().rollback()
to roll back the transaction. This discards any changes made during the transaction and returns the database to its previous state.
- If the database operations are successful, call
- Closing the
Session
: Always close theSession
after you're finished using it, regardless of whether the transaction was committed or rolled back. UseSession.close()
to close theSession
and release the database connection back to the connection pool. This is *crucial* to prevent resource leaks.
Best Practices:
- Use a
try-catch-finally
block: Wrap your Hibernate code within atry-catch-finally
block to ensure that theSession
is always closed, even if an exception occurs. Thefinally
block should always contain thesession.close()
call. - Short-lived Sessions: Keep the
Session
open for the shortest possible time. Avoid keeping aSession
open for the entire duration of a user session or request. - Thread Safety:
Session
objects are *not* thread-safe. Each thread should have its ownSession
instance.
By following these guidelines, you can effectively manage the Session
lifecycle and avoid common Hibernate pitfalls.