# DTO Projections and Read Models — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/f-dto

> Return exactly the data a use case needs with constructor, record and interface projections.

## Not every read needs entities

Entities are ideal when you will **change** data: they are tracked, dirty-checked and cascade. For **read-only** use cases such as lists, reports and API responses, loading full entities wastes memory and invites lazy-loading problems. **DTO projections** select only the needed columns directly into data objects. In JPQL, a **constructor expression** creates DTOs: `select new com.shop.OrderSummary(o.id, c.name, o.total) from Order o join o.customer c`. Java **records** are perfect DTOs. Spring Data JPA adds **interface projections** (declare getters for the needed properties and Spring creates proxies) and **class-based projections** (records or classes). Projections are not managed, so there is no dirty checking and no snapshot overhead, and they produce focused SQL. For complex reporting, native SQL or a dedicated query tool is often clearer than forcing the ORM. A useful rule of thumb: **entities for writes, projections for reads**.

## Record projections with JPQL and Spring Data

Only three columns are selected; nothing is tracked by the persistence context.

```java
public record OrderSummary(Long id, String customerName, BigDecimal total, OrderStatus status) { }

List<OrderSummary> summaries = em.createQuery("""
        select new com.shop.orders.OrderSummary(o.id, c.name, o.total, o.status)
        from PurchaseOrder o join o.customer c
        where o.status = :status
        order by o.placedAt desc
        """, OrderSummary.class)
    .setParameter("status", OrderStatus.PAID)
    .setMaxResults(50)
    .getResultList();

public interface OrderRepository extends JpaRepository<PurchaseOrder, Long> {
    // class-based projection with a JPQL constructor expression
    @Query("""
        select new com.shop.orders.OrderSummary(o.id, c.name, o.total, o.status)
        from PurchaseOrder o join o.customer c where c.id = :customerId
        """)
    List<OrderSummary> summariesForCustomer(Long customerId);

    // interface projection: Spring generates the query and the implementation
    interface OrderIdAndTotal { Long getId(); BigDecimal getTotal(); }
    List<OrderIdAndTotal> findByStatus(OrderStatus status);
}
```

## Never return entities from REST controllers

Serialising entities exposes internal fields, triggers lazy loading during JSON rendering and couples your API to your schema. Map to DTOs inside the transaction.

**Quiz:** Why are DTO projections often better than entities for read-only list screens?

- [ ] They are required by JPA
- [ ] They automatically cache results forever
- [x] They select only needed columns and avoid persistence-context tracking and lazy-loading issues
- [ ] They can update the database directly

*Answer:* They select only needed columns and avoid persistence-context tracking and lazy-loading issues. Projections fetch exactly what the screen needs without entity management overhead.
