High-Performance Java Persistence – Part Two

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Another milestone has been reached

The second part of High-Performance Java Persistence book is out. It’s been almost a year since I started writing this book, and seven months since I published the first part.

Table of content

Before explaining what the second part is all about, it’s better to take a look at the current table of content:

I Introduction
1. Preface
1.1 The database server and the connectivity layer
1.1.1 The ORM framework
1.1.2 The native query builder framework
2. Performance and Scaling
2.1 Response time and throughput
2.2 Database connections boundaries
2.3 Scaling up and scaling out
2.3.1 Master-Slave replication
2.3.2 Multi-Master replication
2.3.3 Sharding
II JDBC and Database Essentials
3. JDBC Connection Management
3.1 DriverManager
3.2 DataSource
3.2.1 Why is pooling so much faster?
3.3 Queuing theory capacity planning
3.4 Practical database connection provisioning
3.4.1 A real-life connection pool monitoring example
3.4.1.1 Concurrent connection request count metric
3.4.1.2 Concurrent connection count metric
3.4.1.3 Maximum pool size metric
3.4.1.4 Connection acquisition time metric
3.4.1.5 Retry attempts metric
3.4.1.6 Overall connection acquisition time metric
3.4.1.7 Connection lease time metric
4. Batch Updates
4.1 Batching Statements
4.2 Batching PreparedStatements
4.2.1 Choosing the right batch size
4.2.2 Bulk operations
4.3 Retrieving auto-generated keys
4.3.1 Sequences to the rescue
5. Statement Caching
5.1 Statement lifecycle
5.1.1 Parser
5.1.2 Optimizer
5.1.2.1 Execution plan visualization
5.1.3 Executor
5.2 Caching performance gain
5.3 Server-side statement caching
5.3.1 Bind-sensitive execution plans
5.4 Client-side statement caching
6. ResultSet Fetching
6.1 ResultSet scrollability
6.2 ResultSet changeability
6.3 ResultSet holdability
6.4 Fetching size
6.5 ResultSet size
6.5.1 Too many rows
6.5.1.1 SQL limit clause
6.5.1.2 JDBC max rows
6.5.1.3 Less is more
6.5.2 Too many columns
7. Transactions
7.1 Atomicity
7.2 Consistency
7.3 Isolation
7.3.1 Concurrency control
7.3.1.1 Two-phase locking
7.3.1.2 Multi-Version Concurrency Control
7.3.2 Phenomena
7.3.2.1 Dirty write
7.3.2.2 Dirty read
7.3.2.3 Non-repeatable read
7.3.2.4 Phantom read
7.3.2.5 Read skew
7.3.2.6 Write skew
7.3.2.7 Lost update
7.3.3 Isolation levels
7.3.3.1 Read Uncommitted
7.3.3.2 Read Committed
7.3.3.3 Repeatable Read
7.3.3.4 Serializable
7.4 Durability
7.5 Read-only transactions
7.5.1 Read-only transaction routing
7.6 Transaction boundaries
7.6.1 Distributed transactions
7.6.1.1 Two-phase commit
7.6.2 Declarative transactions
7.7 Application-level transactions
7.7.1 Pessimistic and optimistic locking
7.7.1.1 Pessimistic locking
7.7.1.2 Optimistic locking
III JPA and Hibernate
8. Why JPA and Hibernate matter
8.1 The impedance mismatch
8.2 JPA vs. Hibernate
8.3 Schema ownership
8.4 Write-based optimizations
8.5 Read-based optimizations
8.6 Wrap-up
9. Connection Management and Monitoring
9.1 JPA connection management
9.2 Hibernate connection providers
9.2.1 DriverManagerConnectionProvider
9.2.2 C3P0ConnectionProvider
9.2.3 HikariCPConnectionProvider
9.2.4 DatasourceConnectionProvider
9.2.5 Connection release modes
9.3 Monitoring connections
9.3.1 Hibernate statistics
9.3.1.1 Customizing statistics
9.4 Statement logging
9.4.1 Statement formatting
9.4.2 Statement-level comments
9.4.3 Logging parameters
9.4.3.1 DataSource-proxy
9.4.3.2 P6Spy
10. Mapping Types and Identifiers
10.1 Types
10.1.1 Primitive types
10.1.2 String types
10.1.3 Date and Time types
10.1.4 Numeric types
10.1.5 Binary types
10.1.6 UUID types
10.1.7 Other types
10.1.8 Custom types
10.2 Identifiers
10.2.1 UUID identifiers
10.2.1.1 The assigned generator
10.2.2 The legacy UUID generator
10.2.2.1 The newer UUID generator
10.2.3 Numerical identifiers
10.2.3.1 Identity generator
10.2.3.2 Sequence generator
10.2.3.3 Table generator
10.2.3.4 Optimizers
10.2.3.4.1 The hi/lo algorithm
10.2.3.4.2 The default sequence identifier generator
10.2.3.4.3 The default table identifier generator
10.2.3.4.4 The pooled optimizer
10.2.3.4.5 The pooled-lo optimizer
10.2.3.5 Optimizer gain
10.2.3.5.1 Sequence generator performance gain
10.2.3.5.2 Table generator performance gain
10.2.3.6 Identifier generator performance
11. Relationships
11.1 Relationship types
11.2 @ManyToOne
11.3 @OneToMany
11.3.1 Bidirectional @OneToMany
11.3.2 Unidirectional @OneToMany
11.3.3 Ordered unidirectional @OneToMany
11.3.3.1 @ElementCollection
11.3.4 @OneToMany with @JoinColumn
11.3.5 Unidirectional @OneToMany Set
11.4 @OneToOne
11.4.1 Unidirectional @OneToOne
11.4.2 Bidirectional @OneToOne
11.5 @ManyToMany
11.5.1 Unidirectional @ManyToMany
11.5.2 Bidirectional @ManyToMany
11.5.3 The @OneToMany alternative
12. Inheritance
12.1 Single table
12.2 Join table
12.3 Table-per-class
12.4 Mapped superclass
13. Flushing
13.1 Flush modes
13.2 Events and the action queue
13.2.1 Flush operation order
13.3 Dirty Checking
13.3.1 The default dirty checking mechanism
13.3.1.1 Controlling the Persistence Context size
13.3.2 Bytecode enhancement
14. Batching
14.1 Batching insert statements
14.2 Batching update statements
14.3 Batching delete statements
15. Fetching
15.1 DTO projection
15.1.1 DTO projection pagination
15.1.2 Native query DTO projection
15.2 Query fetch size
15.3 Fetching entities
15.3.1 Direct fetching
15.3.1.1 Fetching a Proxy reference
15.3.1.2 Natural identifier fetching
15.3.2 Query fetching
15.3.3 Fetching associations
15.3.3.1 FetchType.EAGER
15.3.3.2 FetchType.LAZY
15.3.3.2.1 The N+1 query problem
15.3.3.2.2 How to catch N+1 query problems during testing
15.3.3.2.3 LazyInitializationException
15.3.3.2.4 The Open Session in View Anti-Pattern
15.3.3.2.5 Temporary Session Lazy Loading Anti-Pattern
15.3.3.3 Associations and pagination
15.4 Query plan cache
16. Caching
16.1 Caching flavors
16.2 Cache synchronization strategies
16.2.1 Cache-aside
16.2.2 Read-through
16.2.3 Write-invalidate
16.2.4 Write-through
16.2.5 Write-behind
16.3 Database caching
16.4 Application-level caching
16.4.1 Entity aggregates
16.4.2 Distributed key-value stores
16.4.3 Cache synchronization patterns
16.4.4 Synchronous updates
16.4.5 Asynchronous updates
16.4.5.1 Change data capture
16.5 Second-level caching
16.5.1 Enabling the second-level cache
16.5.2 Entity cache loading flow
16.5.3 Entity cache entry
16.5.3.1 Entity reference cache store
16.5.4 Collection cache entry
16.5.5 Query cache entry
16.5.6 Cache concurrency strategies
16.5.6.1 READ_ONLY
16.5.6.1.1 Inserting READ_ONLY cache entries
16.5.6.1.2 Updating READ_ONLY cache entries
16.5.6.1.3 Deleting READ_ONLY cache entries
16.5.6.2 NONSTRICT_READ_WRITE
16.5.6.2.1 Inserting NONSTRICT_READ_WRITE cache entries
16.5.6.2.2 Updating NONSTRICT_READ_WRITE cache entries
16.5.6.2.3 Risk of inconsistencies
16.5.6.2.4 Deleting NONSTRICT_READ_WRITE cache entries
16.5.6.3 READ_WRITE
16.5.6.3.1 Inserting READ_WRITE cache entries
16.5.6.3.2 Updating READ_WRITE cache entries
16.5.6.3.3 Deleting READ_WRITE cache entries
16.5.6.3.4 Soft locking concurrency control
16.5.6.4 TRANSACTIONAL
16.5.6.4.1 XA_Strict mode
16.5.6.4.2 XA mode
16.5.6.4.3 Inserting TRANSACTIONAL cache entries
16.5.6.4.4 Updating TRANSACTIONAL cache entries
16.5.6.4.5 Deleting TRANSACTIONAL cache entries
16.5.7 Query cache strategy
16.5.7.1 Table space query invalidation
16.5.7.2 Native SQL statement query invalidation
17. Concurrency Control
17.1 Hibernate optimistic locking
17.1.1 The implicit optimistic locking mechanism
17.1.1.1 Resolving optimistic locking conflicts
17.1.1.2 Splitting entities
17.1.1.3 Versionless optimistic locking
17.1.1.3.1 OptimisticLockType.DIRTY update caveat
17.2 The explicit locking mechanism
17.2.1 PESSIMISTIC_READ and PESSIMISTIC_WRITE
17.2.1.1 Lock scope
17.2.1.2 Lock timeout
17.2.2 LockModeType.OPTIMISTIC
17.2.2.1 Inconsistency risk
17.2.3 LockModeType.OPTIMISTIC_FORCE_INCREMENT
17.2.4 LockModeType.PESSIMISTIC_FORCE_INCREMENT

I initially estimated that the whole book would be around 250-300 pages. I was wrong. Even without the third part, the book is over 400 pages, and it covers database essentials, JDBC drivers peculiarities, as well as many topics about JPA and Hibernate.

The first part of the book is explained in this post.

The second part has been covered by the following articles:

The 17th chapter has just been released, and it goes through all sorts of concurrency control idioms. One of the greatest advantages of using JPA and Hibernate is that it provides advanced concurrency control mechanisms. I don’t talk only about the typical numerical version optimistic locking, but also about the versionless application-level concurrency control strategy and the subentity splitting pattern.

This chapter covers pessimistic locking as well, going beyond the classic PESSIMISTIC_READ and PESSIMISTIC_WRITE which you might already be familiar with. I describe how the follow-on locking mechanism works when the database engine cannot apply the FOR UPDATE clause properly. You can also find a detailed explanation of the SKIP LOCKED clause, which, like many other features, it’s a Hibernate-specific feature.

Transactions and Concurrency Control eBook

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.