Read-Only Transactions

readOnly = true

Mark query-only service methods with @Transactional(readOnly = true). With Spring and Hibernate 6 this:

  • sets the Hibernate session to read-only, so loaded entities keep no snapshot for dirty checking (less memory, faster);
  • sets flush mode to MANUAL, so accidental changes to entities are not written;
  • calls Connection.setReadOnly(true), which lets drivers and routing data sources send the query to a read replica.
@Service
@Transactional(readOnly = true)           // default for the whole class
@RequiredArgsConstructor
public class ProductQueryService {

    private final ProductRepository products;

    public Page<ProductCardDto> list(Pageable pageable) { ... }   // read-only

    @Transactional                        // overrides: read-write
    public void updatePrice(Long id, BigDecimal price) { ... }
}

A Change That Is Silently Ignored

Example

@Transactional(readOnly = true)
public ProductDto show(Long id) {
    Product p = products.findById(id).orElseThrow();
    p.setName(p.getName().trim().toUpperCase());     // bug: modifying inside a read-only tx
    return ProductDto.from(p);
}

Example output

DEBUG org.hibernate.SQL : select p1_0.id,p1_0.category_id,p1_0.description,p1_0.name,p1_0.price,p1_0.stock from products p1_0 where p1_0.id=?
-- no UPDATE: the change is not flushed in a read-only transaction

Tip: Spring Data's SimpleJpaRepository is already annotated @Transactional(readOnly = true) for its find methods, and read-write for save/delete.