| Method | Description | Returns |
|---|---|---|
| session.persist(obj) | Insert new entity (JPA standard) | void |
| session.save(obj) | Insert new entity (Hibernate legacy) | Serializable (ID) |
| session.get(Class, id) | Load entity by ID (returns null if not found) | Entity or null |
| session.load(Class, id) | Load proxy by ID (throws exception if not found) | Proxy |
| session.update(obj) | Update detached entity | void |
| session.merge(obj) | Merge detached entity state (JPA standard) | Managed entity |
| session.delete(obj) | Delete entity (Hibernate legacy) | void |
| session.remove(obj) | Delete entity (JPA standard) | void |
| session.saveOrUpdate(obj) | Insert or update based on ID | void |
public class UserDAO {
private SessionFactory sessionFactory;
public UserDAO(SessionFactory sf) { this.sessionFactory = sf; }
// CREATE
public Long createUser(User user) {
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(user); // INSERT INTO users ...
tx.commit();
return user.getId();
} catch (Exception e) {
if (tx != null) tx.rollback();
throw e;
} finally {
session.close();
}
}
// READ by ID - get() returns null if not found
public User getUserById(Long id) {
try (Session session = sessionFactory.openSession()) {
return session.get(User.class, id);
}
}
// READ by ID - load() returns proxy, throws ObjectNotFoundException if not found
public User getUserProxy(Long id) {
try (Session session = sessionFactory.openSession()) {
return session.load(User.class, id); // Lazy proxy
}
}
// READ all
public List<User> getAllUsers() {
try (Session session = sessionFactory.openSession()) {
return session.createQuery("FROM User", User.class).list();
}
}
}
// UPDATE - modify managed entity (auto-dirty-checking)
public void updateUserEmail(Long id, String newEmail) {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
User user = session.get(User.class, id);
if (user != null) {
user.setEmail(newEmail); // Hibernate detects change automatically
// No explicit update() needed - dirty checking handles it
}
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
// UPDATE - merge detached entity
public User mergeUser(User detachedUser) {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
User managed = session.merge(detachedUser); // Returns managed copy
tx.commit();
return managed;
} catch (Exception e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
// DELETE
public void deleteUser(Long id) {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
User user = session.get(User.class, id);
if (user != null) {
session.remove(user); // DELETE FROM users WHERE id = ?
}
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
// SAVE OR UPDATE
public void saveOrUpdateUser(User user) {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
session.saveOrUpdate(user); // INSERT if id is null, UPDATE otherwise
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
Hibernate entities can be in one of four states:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// 1. TRANSIENT: not associated with session
User user = new User("alice", "alice@example.com");
// user is transient here
// 2. PERSISTENT: associated with session after persist()
session.persist(user);
// user is now persistent - changes tracked automatically
user.setEmail("newalice@example.com"); // Hibernate will UPDATE this
// 3. REMOVED: marked for deletion
session.remove(user);
// user is now removed - will be deleted on commit
tx.commit();
session.close();
// 4. DETACHED: session closed, user is detached
// user.setEmail("another@example.com"); // NOT tracked!
// Re-attach with merge
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
User managed = session2.merge(user); // user becomes managed again
tx2.commit();
session2.close();
CRUD behavior depends on whether an entity is transient, managed, detached, or removed. Persist schedules a new managed entity, find loads into the persistence context, merge copies detached state into a managed instance, and remove marks a managed instance for deletion. SQL may be delayed until flush or commit.
Place a complete business change inside one transaction, not one transaction per repository call. Validate invariants before commit and remember that a failed flush makes the current transaction unusable until rollback.
Large write sets need batching, periodic flush and clear, and bounded transaction size so the persistence context does not retain every entity. Preserve deterministic ordering when database locks may be acquired by concurrent workers.
Use an @Version field for optimistic locking when concurrent edits must not silently overwrite each other. Handle the resulting conflict at the application boundary by rejecting, merging, or retrying only an operation known to be safe. Pessimistic locks are justified when conflict probability and correction cost outweigh blocking risk.
Hibernate performs dirty checking. It snapshots managed entity state and compares changes during flush, then generates an UPDATE when needed.
A detached object may be stale, may conflict with an already managed instance, or may contain fields the client was never allowed to change. merge copies state into a managed instance and returns that managed object. In service code, loading the current entity and applying validated changes is often safer than accepting an entire detached graph.
CascadeType.REMOVE or orphanRemoval can propagate deletion through mapped relationships. That may be correct for privately owned children, but dangerous for shared entities.
Explore 500+ free tutorials across 20+ languages and frameworks.