Define entities with @Entity and an identifier using
@Id (optionally @GeneratedValue). Create new rows
with EntityManager.persist(entity). Retrieve existing rows with
EntityManager.find(EntityClass.class, id) or queries
(JPQL/Criteria).
// Persist
em.persist(order);
// Find by primary key
Order order = em.find(Order.class, orderId);
// Query examples
List<Order> recent = em.createQuery(
"select o from Order o where o.createdAt > :cutoff", Order.class)
.setParameter("cutoff", cutoffInstant)
.getResultList();
JPA entities move through states: new → managed → detached/removed. The provider performs dirty‑checking on managed entities and writes changes on flush/commit.
Use lifecycle annotations to run code at key points:
@PrePersist, @PostPersist,
@PreUpdate, @PostUpdate,
@PreRemove, @PostRemove, and
@PostLoad. You can keep these in the entity or in an
entity listener class for cross‑cutting concerns.
@Entity
class Order {
@Id @GeneratedValue Long id;
@PrePersist void stampCreatedAt() { this.createdAt = Instant.now(); }
@PreUpdate void stampUpdatedAt() { this.updatedAt = Instant.now(); }
}
Wrap write operations in transactions (e.g., @Transactional).
On commit, the provider flushes changes from the
persistence context to the database; on rollback, changes are
discarded. Use em.flush() to force synchronization when you
need generated values early or to fail fast on constraint errors.
@Transactional
public void placeOrder(Order o) {
em.persist(o); // becomes managed
// ... business logic ...
// commit triggers flush + SQL writes
}
Test your understanding with the exercise below. You will add lifecycle callbacks, persist and retrieve entities, and observe flush behavior.
JPA Entity Lifecycle — Exercise