An extended persistence context keeps managed entities associated with a bean for its entire lifecycle rather than limiting them to one transaction. It can span several request/response cycles or system transactions, supporting conversations that pause while a user supplies input. section 14.2 section 10.2.6
The persistence context may become stale while the conversation is open. Calling refresh(entity) reloads the database state, overwrites in-memory changes, and can undo edits when a user cancels; it can also throw EntityNotFoundException if the row was deleted. Extended scope does not eliminate transaction boundaries: database interactions still require transactions. section 10.2.6 section 14.2
A Spring-based implementation combines extended scope with container-managed access:
- Inject EntityManager with @PersistenceContext(type = PersistenceContextType.EXTENDED). section 14.2
- Place DAO operations under @Transactional and enable annotation-driven transaction management with a transaction manager. section 14.2
- Use the injected EntityManager proxy rather than sharing an EntityManager directly, because an EntityManager is not thread-safe by itself. section 14.2
- Reuse the same configuration in generic DAOs by placing the EXTENDED persistence context on AbstractGenericDao. section 14.3
Refreshing an entity during a conversation
An application loads an Item, changes its name in memory, and then discovers that another process updated the database. Calling em.refresh(item) replaces the in-memory name with the concurrent database value. The same operation can discard edits after a user cancels a multi-step dialogue. section 10.2.6
Listing 14.3 The ItemDaoImpl class
Path: Ch14/spring-jpa-dao/src/main/java/com/manning/javapersistence/ch14 /dao/ItemDaoImpl.java @Repository #1 @Transactional #1 public class ItemDaoImpl implements ItemDao { @PersistenceContext(type = PersistenceContextType.EXTENDED) #2 private EntityManager em; #2 @Override #3 public Item getById(long id) { #3 return em.find(Item.class, id); #3 } @Override #4 public List<Item> getAll() { #4 return (List<Item>) em.createQuery("from Item", Item.class) #4 .getResultList(); #4 } @Override #5 public void insert(Item item) { #5 em.persist(item); #5 for (Bid bid : item.getBids()) { #5 em.persist(bid); #5 } #5 } @Override #6 public void update(long id, String name) { #6 Item item = em.find(Item.class, id); #6 item.setName(name); #6 em.persist(item); #6 } @Override #7 public void delete(Item item) { #7 for (Bid bid : item.getBids()) { #7 em.remove(bid); #7 } #7 em.remove(item); #7 } @Override #8 public Item findByName(String name) { #8 return em.createQuery("from Item where name=:name", Item.class) #8 .setParameter("name", name).getSingleResult(); #8 } } #1 The <kbd>ItemDaoImpl</kbd> class is annotated with <kbd>@Repository</kbd> and <kbd>@Transactional</kbd>. #2 The <kbd>EntityManager em</kbd> field is injected in the application, as annotated with <kbd>@PersistenceContext</kbd>. Persistence type <kbd>EXTENDED</kbd> means the persistence context is kept for the whole lifecycle of a bean. #3 Retrieve an <kbd>Item</kbd> by its <kbd>id</kbd>. #4 Retrieve all <kbd>Item</kbd> entities. #5 Persist an <kbd>Item</kbd> and all its <kbd>Bid</kbd>s. #6 Update the <kbd>name</kbd> field of an <kbd>Item</kbd>. #7 Remove all the bids belonging to an <kbd>Item</kbd> and the <kbd>Item</kbd> itself. #8 Search for an <kbd>Item</kbd> by its <kbd>name</kbd>.
Inspect a concrete ItemDaoImpl showing @PersistenceContext with PersistenceContextType.EXTENDED alongside repository and transaction annotations. section 14.2
