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:
- Chapter 8 – Why JPA and Hibernate matter
- Chapter 9 – Hibernate Connection Management
- Chapter 10 – Mapping Types and Identifiers
- Chapter 11 – Relationships
- Chapter 12 – Inheritance
- Chapter 13 – Flushing
- Chapter 14 – Batching
- Chapter 15 – Fetching
- Chapter 16 – Caching
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.
