Lesson 4 / 25
EntityManager Operations
Use persist, find, getReference, merge and remove correctly.
The core API
The EntityManager is the main JPA interface for working with entities (Hibernate's Session implements it and adds more). persist(entity) makes a new entity managed; it is inserted at the next flush. find(Class, id) loads an entity by primary key, returning null if it does not exist, and returns the same instance if it is already in the persistence context, without another query. getReference(Class, id) returns a lazy proxy without querying, useful for setting a foreign key (order.setCustomer(em.getReference(Customer.class, 42L))); accessing its fields later triggers the load, or EntityNotFoundException if it does not exist. merge(entity) copies the state of a detached object onto a managed instance (loading it if necessary) and returns the managed copy; the argument itself stays detached, a classic source of bugs. remove(entity) schedules deletion of a managed entity. refresh reloads state from the database, and detach or clear remove entities from management. In Spring applications you usually inject the EntityManager with @PersistenceContext or use Spring Data repositories, which call these methods for you.
Operations and their effects
persist and find bring entities into the persistence context; flush turns changes into SQL.
Using the EntityManager directly
Note that merge returns the managed instance you should keep using.
@Service
public class ProductService {
@PersistenceContext
private EntityManager em;
@Transactional
public Long create(String sku, String title, BigDecimal price) {
Product p = new Product(sku, title, price); // transient
em.persist(p); // managed; INSERT at flush/commit
return p.getId(); // id assigned (sequence) during persist
}
@Transactional
public Product updateFromDto(ProductDto dto) {
Product detached = dto.toEntity();
Product managed = em.merge(detached); // use the RETURNED instance
managed.changePrice(dto.price()); // tracked; 'detached' is not
return managed;
}
@Transactional
public void delete(Long id) {
Product p = em.find(Product.class, id);
if (p != null) em.remove(p); // remove requires a managed entity
}
}Do not call save on managed entities
Inside a transaction, a loaded entity is already managed: changing its fields is enough, and Hibernate writes the UPDATE at flush. Calling merge or repository.save again is unnecessary noise.
Quick check: What does `em.merge(detachedProduct)` return?
- Nothing; it updates the argument in place
- A new transient object
- A managed instance with the detached object's state copied onto it
- The database row count
Answer
A managed instance with the detached object's state copied onto it — merge returns the managed copy; the argument itself remains detached.