🗃️ JPA Q60 / 63

What is the difference between first-level cache and second-level cache in JPA?

AI-Powered Answer ✓ Answered

In JPA (Java Persistence API), caching plays a crucial role in improving application performance by reducing the number of database access operations. JPA defines two primary levels of caching: the first-level cache and the second-level cache, each with distinct scopes and purposes.

First-Level Cache (Persistence Context Cache)

The first-level cache, also known as the Persistence Context cache or transactional cache, is an essential part of every JPA application. It is a mandatory cache and is always active. Each EntityManager instance has its own first-level cache, meaning it's tied to a specific persistence context and is not shared across different EntityManager instances.

  • Scope: Transactional and session-specific. It lives as long as the EntityManager or the transaction associated with it.
  • Sharing: Not shared. Each EntityManager has its own isolated cache.
  • Mechanism: When an entity is loaded or persisted, it is placed in this cache. Subsequent requests for the same entity within the same persistence context will return the cached instance, avoiding a database round trip.
  • Automatic: It's always enabled and managed by JPA. Developers don't explicitly enable or disable it.
  • Purpose: Ensures identity equality (only one instance of a persistent entity exists within a persistence context) and improves performance within a single transaction.
java
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

// First read: entity is loaded from DB and placed in first-level cache
Product product1 = em.find(Product.class, 1L); 
System.out.println("Product 1: " + product1.getName());

// Second read: entity is retrieved from first-level cache, no DB access
Product product2 = em.find(Product.class, 1L); 
System.out.println("Product 2: " + product2.getName());

// product1 == product2 will be true
System.out.println("Are product1 and product2 the same instance? " + (product1 == product2)); 

em.getTransaction().commit();
em.close();

Second-Level Cache (Shared Cache)

The second-level cache, also known as the shared cache or application-level cache, is an optional cache that can be configured by the developer. Unlike the first-level cache, it is shared across all EntityManager instances within the same EntityManagerFactory and even across different applications in a clustered environment (depending on implementation). It stores entity data (not entity objects themselves) after an entity manager is closed, making it available for subsequent EntityManager instances.

  • Scope: Application-level and shared. It lives as long as the EntityManagerFactory or until explicitly cleared.
  • Sharing: Shared across all EntityManager instances created by the same EntityManagerFactory.
  • Mechanism: Stores entity data (e.g., field values) in a non-transactional region. When an entity is requested, JPA first checks the first-level cache. If not found, it checks the second-level cache before querying the database.
  • Optional: Must be explicitly enabled and configured, typically via persistence.xml or annotations.
  • Purpose: Reduces database load and improves performance significantly across multiple transactions and users.
  • Concurrency: Requires careful handling of concurrency strategies (e.g., READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL) to ensure data consistency.
xml
<persistence-unit name="myPU">
    <properties>
        <!-- Enable second-level cache -->
        <property name="jakarta.persistence.sharedCache.mode" value="ENABLE_SELECTIVE"/>
        
        <!-- Hibernate-specific configuration for second-level cache provider -->
        <property name="hibernate.cache.use_second_level_cache" value="true"/>
        <property name="hibernate.cache.region.factory_class" 
                  value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
    </properties>
</persistence-unit>

<!-- Example for an entity to be cached -->
@Entity
@Cacheable
public class Product {
    @Id
    private Long id;
    private String name;
    // ...
}

Key Differences Summarized

FeatureFirst-Level CacheSecond-Level Cache
ScopePersistence Context (Transactional)EntityManagerFactory (Application-wide)
SharingNot shared (per EntityManager)Shared (across all EntityManager instances)
Mandatory/OptionalMandatory and always activeOptional and configurable
LifetimeAs long as EntityManager/transactionAs long as EntityManagerFactory/application
ContentActual entity objectsEntity data (field values)
PurposeIdentity equality, transactional performanceReduce DB load, application-wide performance

When to Use Each

The first-level cache is always in use and requires no configuration. The second-level cache is highly beneficial for frequently accessed, relatively static data that is read more often than it is updated. It significantly reduces database load and network traffic for read-heavy applications, but it introduces complexity regarding data consistency, especially in distributed environments. Careful consideration of cache concurrency strategies is crucial when enabling the second-level cache.