The query cache stores the identifiers of entities resulting from specific queries. It must always be paired with the L2 cache. If used in isolation, it can degrade performance by forcing individual database lookups for every cached entity ID.
In enterprise Java applications, database communication is almost always the primary performance bottleneck. While object-relational mapping (ORM) frameworks like Hibernate and the Jakarta Persistence API (JPA) drastically accelerate development speed, they abstract away the underlying database tier. When developers treat the database as a black box, applications suffer from inefficient queries, connection starvation, and severe concurrency issues.
And there it was. A single, highlighted paragraph: "The difference between a toy application and a production system is not the database—it is the developer's understanding of the persistence context. Use JOIN FETCH for single aggregations, a @EntityGraph for complex trees, and never, ever loop over lazy associations inside a transaction."
She flipped to the chapter on batching. The PDF showed her how to rewrite the history loader. Not a loop of 200 queries, but two: one for the orders, one for the items, joined in memory with a WHERE id IN (:ids) . She copied the pattern, her fingers flying over the keyboard.
Best suited for data (like country codes or application settings). 6. Advanced Database Patterns Optimistic vs. Pessimistic Locking High-performance Java Persistence.pdf
hibernate.jdbc.batch_size=30 hibernate.order_inserts=true hibernate.order_updates=true Use code with caution.
Disables Hibernate's ability to use JDBC batching for inserts because the framework must execute the SQL statement immediately to retrieve the database-generated ID.
+-----------------------------------------------------------+ | Java Application | | [ Entity Cache ] -> [ JPA / Hibernate ] -> [ JDBC Pool ] | +-----------------------------------------------------------+ | v (TCP/IP Network) +-----------------------------------------------------------+ | Relational Database | | [ Transaction Log ] -> [ Buffer Pool ] -> [ Disk Storage] | +-----------------------------------------------------------+ JDBC Layer Fundamentals
Creating a physical database connection is incredibly expensive. Always use a high-performance connection pool like . The query cache stores the identifiers of entities
What are you using (PostgreSQL, Oracle, MySQL)?
Never use FetchType.EAGER in mappings. It is an unchangeable global setting that forces Hibernate to load associations even when they are not needed for a specific business use case.
The N+1 query problem occurs when an application executes one query to fetch a parent record and then executes
Automatically ensures entity uniqueness within a transaction. Second-Level Cache (L2) And there it was
If a specific business use case requires an association, fetch it dynamically using a JPQL JOIN FETCH or a JPA Entity Graph. This prevents the infamous N+1 query problem , where loading child entities executes distinct SQL statements. 3. Transaction and Concurrency Control
Avoid the temptation to set excessively large pool sizes. A massive pool creates CPU context switching and disk contention on the database server. Use the standard formula:
Master High-Performance Java Persistence: A Complete Guide to Optimizing Database Access
High-Performance Java Persistence: Optimizing Database Access for Enterprise Applications