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 = true for 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 only

Tip: 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 styleManaged?MemoryBest for
Entity in read-write txyes + snapshothighestyou will modify it
Entity in readOnly txyes, no snapshotmediumreading entity graphs
DTO / record projectionnolowestAPI responses, lists, reports
Interface projectionno (proxy)lowquick read models in Spring Data