Real World Example

Catalogue Caching at an E-commerce Site

Sites like Flipkart and Myntra render a category menu and filters on every page view. The category tree changes a few times a day; page views happen millions of times. ShopEase does the same with an L2-cached Category, a cached "menu" query, and no caching for stock and orders.

@Entity
@Table(name = "categories")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "shopease.category")
public class Category {
    @Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(nullable = false, unique = true)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Category parent;                 // tree: Electronics > Mobiles > Smartphones
    // getters/setters
}

public interface CategoryRepository extends JpaRepository<Category, Long> {
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
    @Query("select c from Category c order by c.name")
    List<Category> findMenu();
}

@Service
@RequiredArgsConstructor
public class CatalogService {
    private final CategoryRepository categories;
    private final ProductRepository products;

    @Transactional(readOnly = true)
    public List<Category> menu() {
        return categories.findMenu();            // served from cache after the first call
    }

    @Transactional
    public void renameCategory(Long id, String newName) {
        categories.findById(id).orElseThrow().setName(newName);
        // READ_WRITE: Hibernate updates the L2 entry after commit and invalidates
        // cached queries on "categories" automatically. No manual eviction needed.
    }
}

Region Sizing with Ehcache

<config xmlns="http://www.ehcache.org/v3">
  <cache alias="shopease.category">
    <expiry><ttl unit="minutes">30</ttl></expiry>
    <heap unit="entries">2000</heap>
  </cache>
  <cache alias="default-query-results-region">
    <expiry><ttl unit="minutes">5</ttl></expiry>
    <heap unit="entries">500</heap>
  </cache>
  <cache alias="default-update-timestamps-region">
    <heap unit="entries">1000</heap>   <!-- never expire this one -->
  </cache>
</config>

Note: Never set an expiry on the update-timestamps region: if its entries vanish, Hibernate cannot tell whether a cached query is stale.

Tip: Measure first. Enable statistics, look at the hit ratio per region, and remove caching from regions with a low hit ratio.