Careeroza
Read-Only Queries and Projections
Read-Only Transactions
Every managed entity costs memory for a snapshot (a copy of its loaded state used by dirty checking). For pure reads you do not need it.
@Transactional(readOnly = true): Spring sets the Hibernate session to read-only and the flush mode to MANUAL, so entities get no snapshots and nothing is flushed. Some drivers/pools also route the connection to a read replica.- Per-query hint
org.hibernate.readOnly=truefor one query inside a read-write transaction.
Load Only the Columns You Need
A product listing needs id, name, price, thumbnail. Loading the full entity also loads the long description and pulls in eager associations. Use a DTO projection:
public record ProductCard(Long id, String name, BigDecimal price, String categoryName) {}
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query("""
select new com.shopease.product.ProductCard(p.id, p.name, p.price, c.name)
from Product p join p.category c
where c.id = :categoryId and p.stock > 0
""")
List<ProductCard> findCards(@Param("categoryId") Long categoryId, Pageable page);
}select p1_0.id,p1_0.name,p1_0.price,c1_0.name
from products p1_0 join categories c1_0 on c1_0.id=p1_0.category_id
where c1_0.id=? and p1_0.stock>0
offset ? rows fetch first ? rows onlyTip: DTO projections are never managed: no snapshot, no dirty checking, no lazy-loading surprises. They are the fastest way to read data for an API response.
Comparing the Options
| Read style | Managed? | Memory | Best for |
|---|---|---|---|
| Entity in read-write tx | yes + snapshot | highest | you will modify it |
| Entity in readOnly tx | yes, no snapshot | medium | reading entity graphs |
| DTO / record projection | no | lowest | API responses, lists, reports |
| Interface projection | no (proxy) | low | quick read models in Spring Data |