Basic CRUD with Hibernate
Learn how to Create, Read, Update, and Delete entities using a Hibernate Session — save(), get()/find(), merge(), and delete().
Introduction
Every persistence framework exists to make four operations easy: Create, Read, Update, and Delete — commonly shortened to CRUD. Now that you know how entities are mapped with annotations and how primary keys are generated, it is time to put that mapping to work. In this lesson you will perform all four CRUD operations against a real entity using a Hibernate Session, and see exactly what SQL Hibernate generates behind the scenes for each one.
- How to save a new entity with session.save() / persist()
- How to fetch an entity by its primary key with get() and find()
- How Hibernate tracks changes and updates rows automatically
- How to remove a row using session.delete() / remove()
Setting Up the Entity
We will use a simple Student entity throughout this lesson. It has an auto-generated primary key, a name, and an email address.
@Entity@Table(name = "students")public class Student {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Column(name = "name", nullable = false) private String name;
@Column(name = "email", unique = true) private String email;
public Student() { // Hibernate requires a no-arg constructor }
public Student(String name, String email) { this.name = name; this.email = email; }
// Getters and setters public Long getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; }}Every operation below follows the same shape: open a Session, begin a Transaction, do the work, commit, and close the Session. This pattern will become second nature as you write more Hibernate code.
Create: Saving an Entity
To insert a new row, create a plain Java object, populate it, and hand it to the Session using save() (Hibernate-native) or persist() (JPA standard). Both make the object managed and schedule an INSERT.
Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();
Student student = new Student("Aditi Sharma", "aditi@example.com");session.save(student); // or: session.persist(student);
tx.commit();session.close();
System.out.println("Generated ID: " + student.getId());Click Run to see what this code prints.
save() returns the generated identifier immediately and can be called outside a transaction (not recommended). persist() is the JPA standard, returns void, and always requires an active transaction. Prefer persist() in new code.
Read: Fetching an Entity
To load an entity by its primary key, use get() or load() (Hibernate-native) or find() (JPA standard). get() and find() both return null if no row matches; load() throws an exception instead, because it returns a lazy proxy.
Session session = sessionFactory.openSession();
Student student = session.get(Student.class, 1L);// JPA equivalent: Student student = session.find(Student.class, 1L);
if (student != null) { System.out.println("Name: " + student.getName()); System.out.println("Email: " + student.getEmail());} else { System.out.println("No student found with that id.");}
session.close();Click Run to see what this code prints.
Update: Modifying an Entity
Hibernate does not require an explicit "update" call for an entity that is already managed within an open Session — any change to its fields is detected automatically at flush/commit time through a mechanism called dirty checking. If you have a detached entity (loaded in a previous, now-closed Session), you reattach it with merge().
Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();
Student student = session.get(Student.class, 1L);student.setEmail("aditi.sharma@example.com");// No explicit save/update call needed — Hibernate detects the change.
tx.commit();session.close();Click Run to see what this code prints.
Student detachedStudent = new Student();detachedStudent.setId(1L); // must set the id manually — it is not managed// (setId would need a setter added to the entity above)
Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();
Student managed = session.merge(detachedStudent);
tx.commit();session.close();merge() copies the state of the object you pass in onto a managed entity and returns that managed entity. The object you originally passed in stays detached — always continue working with the object merge() returns.
Delete: Removing an Entity
To delete a row, load the managed entity first, then pass it to session.delete() (Hibernate-native) or session.remove() (JPA standard). Hibernate issues a DELETE statement matching the primary key.
Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();
Student student = session.get(Student.class, 1L);if (student != null) { session.delete(student); // or: session.remove(student);}
tx.commit();session.close();Click Run to see what this code prints.
Common Mistakes
- Forgetting to open a Transaction before save/update/delete — writes silently fail to commit or throw exceptions.
- Calling delete() with an entity that was never loaded through the current Session (it must be managed, not just any object with an id set).
- Assuming save() and persist() are interchangeable in every situation — they differ in return value and transaction requirements.
- Mutating a detached entity and expecting Hibernate to pick up the change without calling merge().
Best Practices
- Always wrap write operations (save, update, delete) inside a Transaction.
- Prefer the JPA-standard persist(), find(), merge(), and remove() methods for portability across ORM providers.
- Close the Session in a finally block, or use try-with-resources, so connections are never leaked.
- Check for null after get()/find() before operating on the returned entity.
Frequently Asked Questions
No. save() schedules the INSERT; it is only executed (and made permanent) when the Session flushes, which typically happens right before transaction commit.
get() (and find()) simply return null. load() instead returns a proxy and throws ObjectNotFoundException only when you try to access its fields.
It depends on your cascade and foreign key configuration. Without proper cascading, deleting a parent that still has children referencing it will throw a constraint violation exception.
Key Takeaways
- save()/persist() insert a new row and make the object managed.
- get()/find() fetch an entity by its primary key, returning null if absent.
- Managed entities are updated automatically via dirty checking — no explicit update call needed.
- merge() reattaches a detached entity and returns the managed copy.
- delete()/remove() issue a DELETE for a managed entity.
Summary
CRUD is the foundation everything else in Hibernate builds on. Once you are comfortable saving, reading, updating, and deleting a single entity with a Session, you are ready to move beyond primary-key lookups into flexible, expressive queries — starting with Hibernate Query Language.