Model Lifecycle
The Model Lifecycle describes the states an entity passes through from creation to deletion.
Entity Lifecycle States
graph TB
New["New Entity
Created in Code"] Transient["Transient State
Not Yet Persisted"] Managed["Managed State
Tracked by Hibernate"] Persistent["Persistent
Saved to Database"] Detached["Detached State
Transaction Ended"] Removed["Removed
Marked for Delete"] Deleted["Deleted
from Database"] New --> Transient Transient -->|repository.save()| Managed Managed -->|Transaction Commits| Persistent Persistent -->|Transaction Ends| Detached Persistent -->|repository.delete()| Removed Removed -->|Commit| Deleted style New fill:#bbdefb style Transient fill:#90caf9 style Managed fill:#64b5f6 style Persistent fill:#42a5f5 style Detached fill:#2196f3 style Removed fill:#ef9a9a style Deleted fill:#ef5350
Created in Code"] Transient["Transient State
Not Yet Persisted"] Managed["Managed State
Tracked by Hibernate"] Persistent["Persistent
Saved to Database"] Detached["Detached State
Transaction Ended"] Removed["Removed
Marked for Delete"] Deleted["Deleted
from Database"] New --> Transient Transient -->|repository.save()| Managed Managed -->|Transaction Commits| Persistent Persistent -->|Transaction Ends| Detached Persistent -->|repository.delete()| Removed Removed -->|Commit| Deleted style New fill:#bbdefb style Transient fill:#90caf9 style Managed fill:#64b5f6 style Persistent fill:#42a5f5 style Detached fill:#2196f3 style Removed fill:#ef9a9a style Deleted fill:#ef5350
State Descriptions
1. New Entity (Transient)
Created in code but not yet persisted
Supplier supplier = Supplier.builder()
.id(UUID.randomUUID().toString())
.name("ACME Corp")
.build();
// supplier is NEW/TRANSIENT - not yet in database2. Managed State
Entity is tracked by Hibernate and associated with session
Supplier saved = repository.save(supplier);
// saved is now MANAGED - within active session
// Changes are tracked automatically3. Persistent
Entity saved to database with committed transaction
// After @Transactional method completes
// Entity is PERSISTENT - record exists in database
// Changes are persisted4. Detached State
Entity no longer tracked after session closes
// After @Transactional method ends
// Entity becomes DETACHED - session closed
// Changes are NO LONGER tracked automatically
supplier.setName("New Name"); // Won't be persisted
// Must reattach or save explicitly5. Removed
Entity marked for deletion
repository.delete(supplier);
// supplier is REMOVED - scheduled for deletion6. Deleted
Entity removed from database
// After @Transactional completes
// supplier is DELETED - removed from database
// Record no longer existsLifecycle Transitions
Save New Entity
New → Transient → Managed → Persistent
Supplier supplier = new Supplier(); // NEW
supplier.setName("ACME Corp");
repository.save(supplier); // MANAGED (within session)
// @Transactional ends → PERSISTENT (committed to database)Update Managed Entity
Managed → Managed (automatically persisted on commit)
@Transactional
public void updateSupplier(String id, String newName) {
Supplier supplier = repository.findById(id); // MANAGED
supplier.setName(newName);
// No need to call save() - automatically persisted on commit
}Update Detached Entity
Detached → Managed → Persistent (requires reattachment)
Supplier supplier = repository.findById(id); // PERSISTENT
// Detach from session somehow...
// Now DETACHED - changes won't be persisted
supplier.setName("New Name");
// Reattach via save or merge
repository.save(supplier); // MANAGED again, then PERSISTENTDelete Entity
Managed → Removed → Deleted
Supplier supplier = repository.findById(id); // MANAGED
repository.delete(supplier); // REMOVED
// @Transactional ends → DELETED (committed deletion)Practical Implications
Why State Matters
Managed State:
@Transactional
public void updateSupplier(String id, String newName) {
Supplier supplier = repository.findById(id); // MANAGED
supplier.setName(newName);
// No explicit save() needed - automatically persisted
}Detached State:
public void updateSupplier(String id, String newName) {
Supplier supplier = repository.findById(id); // DETACHED (no @Transactional)
supplier.setName(newName);
// Changes NOT persisted - session already closed
// Must explicitly save
repository.save(supplier); // Reattaches and persists
}Best Practice: Use @Transactional
// ✅ Good - Explicit transaction boundary
@Transactional
public SupplierDTO update(String id, UpdateSupplierDTO dto) {
Supplier supplier = repository.findById(id); // MANAGED
supplier.setName(dto.getName());
// Auto-persisted on commit - clean and simple
return mapper.toDTO(supplier);
}
// ❌ Bad - Detached entity issues
public SupplierDTO update(String id, UpdateSupplierDTO dto) {
Supplier supplier = repository.findById(id); // DETACHED
supplier.setName(dto.getName());
// Changes NOT persisted - forgotten save?
return mapper.toDTO(supplier);
}