JPA and Hibernate Performance Tips

Vlad Mihalcea
Vlad MihalceaJava Champion at https://vladmihalcea.com/
@vlad_mihalcea vladmihalcea.com
JPA and Hibernate
Performance Tips
@vlad_mihalcea vladmihalcea.com
About me
vladmihalcea.com
@vlad_mihalcea vladmihalcea.com
Mappings
CC BY-SA 2.0 - https://www.flickr.com/photos/47515486@N05/44129053595/
@vlad_mihalcea vladmihalcea.com
TABLE generator
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String title;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
TABLE generator
CREATE TABLE hibernate_sequences (
sequence_name (255) NOT NULL,
next_val,
PRIMARY KEY (sequence_name)
)
CREATE TABLE post (
id NOT NULL,
title (255),
PRIMARY KEY (id)
)
@vlad_mihalcea vladmihalcea.com
TABLE generator
public IntegralDataTypeHolder getNextValue() {
return session.getTransactionCoordinator().createIsolationDelegate()
.delegateWork(new AbstractReturningWork<IntegralDataTypeHolder>() {
public IntegralDataTypeHolder execute(
Connection connection) throws SQLException {
…
}, true
);
}
for (int i = 0; i < 3; i++) {
Post post = new Post();
post.setTitle(
String.format("High-Performance Java Persistence, Part %d", i + 1)
);
entityManager.persist(post);
}
@vlad_mihalcea vladmihalcea.com
SELECT tbl.next_val FROM hibernate_sequences tbl
WHERE tbl.sequence_name = 'default' FOR UPDATE
INSERT INTO hibernate_sequences (sequence_name, next_val) VALUES ('default', 1)
UPDATE hibernate_sequences SET next_val = 2
WHERE next_val = 1 AND sequence_name = 'default'
SELECT tbl.next_val FROM hibernate_sequences tbl
WHERE tbl.sequence_name = 'default' FOR UPDATE
UPDATE hibernate_sequences SET next_val = 3
WHERE next_val = 2 AND sequence_name = 'default'
SELECT tbl.next_val FROM hibernate_sequences tbl
WHERE tbl.sequence_name = 'default' FOR UPDATE
UPDATE hibernate_sequences SET next_val = 4
WHERE next_val = 3 AND sequence_name = 'default'
TABLE generator
@vlad_mihalcea vladmihalcea.com
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 1', 1)
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 2', 2)
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 3', 3)
TABLE generator
@vlad_mihalcea vladmihalcea.com
IDENTITY vs TABLE generator
@vlad_mihalcea vladmihalcea.com
SEQUENCE vs TABLE generator
@vlad_mihalcea vladmihalcea.com
AUTO Generator
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
AUTO Generator
• Prior to Hibernate 5 – native strategy.
• Hibernate 5 – SequenceStyleGenerator strategy (falls back to
TABLE)
@vlad_mihalcea vladmihalcea.com
AUTO Generator – Hibernate 5 and MySQL
@Id
@GeneratedValue(generator="native")
@GenericGenerator(name = "native", strategy = "native")
private Long id;
INSERT INTO post (title)
VALUES ('High-Performance Java Persistence, Part 1')
INSERT INTO post (title)
VALUES ('High-Performance Java Persistence, Part 2')
INSERT INTO post (title)
VALUES ('High-Performance Java Persistence, Part 3')
@vlad_mihalcea vladmihalcea.com
Identifier portability – annotation mapping
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue(generator = "sequence",
strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "sequence", allocationSize = 10)
private Long id;
private String title;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
Identifier portability – XML mapping
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings
xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm_2_2.xsd"
version="2.2">
<package>com.vladmihalcea.book.hpjp.hibernate.identifier.global</package>
<entity class="Post" access="FIELD">
<attributes>
<id name="id">
<generated-value strategy="IDENTITY"/>
</id>
</attributes>
</entity>
</entity-mappings
@vlad_mihalcea vladmihalcea.com
Custom types – IP address column
• IP address stored in the Classless Inter-Domain Routing format
• VARCHAR(18)
• BIGINT column encoding – 4 bytes (e.g. 192.168.123.231) + 1 byte (e.g. 24)
• PostgreSQL cidr or inet type (requires 7 bytes)
@vlad_mihalcea vladmihalcea.com
Custom types – PostgreSQL inet column
Event matchingEvent = (Event) entityManager
.createNativeQuery(
"SELECT e.* " +
"FROM event e " +
"WHERE " +
" e.ip && CAST(:network AS inet) = TRUE")
.setParameter("network", "192.168.0.1/24")
.getSingleResult();
assertEquals("192.168.0.123", matchingEvent.getIp().getAddress());
@vlad_mihalcea vladmihalcea.com
Custom types – PostgreSQL inet column
@Entity(name = "Event")
@Table(name = "event")
@TypeDef(typeClass = IPv4Type.class, defaultForType = IPv4.class)
public class Event {
@Id
@GeneratedValue
private Long id;
@Column(name = "ip", columnDefinition = "inet")
private IPv4 ip;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
The hibernate-types project
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-5</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
@vlad_mihalcea vladmihalcea.com
JSON for unstructured data
@Entity(name = "Book")
@Table(name = "book")
@TypeDef(typeClass = JsonNodeBinaryType.class, defaultForType = JsonNode.class)
public class Book {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String isbn;
@Column(columnDefinition = "jsonb")
private JsonNode properties;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
JSON for unstructured data
Book book = new Book();
book.setIsbn("978-9730228236");
book.setProperties(
JacksonUtil.toJsonNode(
"{" +
" "title": "High-Performance Java Persistence"," +
" "author": "Vlad Mihalcea"," +
" "publisher": "Amazon"," +
" "price": 44.99" +
"}"
)
);
@vlad_mihalcea vladmihalcea.com
JSON for unstructured data
Book book = entityManager.unwrap(Session.class).bySimpleNaturalId(Book.class)
.load("978-9730228236");
book.setProperties(
JacksonUtil.toJsonNode(
"{" +
" "title": "High-Performance Java Persistence"," +
" "author": "Vlad Mihalcea"," +
" "publisher": "Amazon"," +
" "price": 44.99," +
" "url": "https://www.amzn.com/973022823X/"" +
"}"
)
);
@vlad_mihalcea vladmihalcea.com
Statement caching
CC BY 2.0 - https://www.flickr.com/photos/southbeachcars/16065861514/
@vlad_mihalcea vladmihalcea.com
Execution plan cache
@vlad_mihalcea vladmihalcea.com
Oracle server-side statement caching
• Soft parse
• Hard parse
• Bind peeking
• Adaptive cursor sharing (since 11g)
@vlad_mihalcea vladmihalcea.com
SQL Server server-side statement caching
• Execution plan cache
• Parameter sniffing
• Prepared statements should use the qualified object name
SELECT *
FROM etl.dbo.task
WHERE status = ?
@vlad_mihalcea vladmihalcea.com
PostgreSQL server-side statement caching
• Prior to 9.2 – execution plan cache
• 9.2 – deferred optimization
• The prepareThreshold connection property
@vlad_mihalcea vladmihalcea.com
MySQL server-side statement caching
• No execution plan cache
• Since Connector/J 5.0.5 PreparedStatements are emulated
• To activate server-side prepared statements:
• useServerPrepStmts
• cachePrepStmts
@vlad_mihalcea vladmihalcea.com
Client-side statement caching
• Recycling Statement, PreparedStatement or
CallableStatement objects
• Reusing database cursors
@vlad_mihalcea vladmihalcea.com
Oracle implicit client-side statement caching
• Connection-level cache
• PreparedStatement and CallabledStatement only
• Caches metadata only
connectionProperties.put(
"oracle.jdbc.implicitStatementCacheSize",
Integer.toString(cacheSize));
dataSource.setConnectionProperties(connectionProperties);
@vlad_mihalcea vladmihalcea.com
Oracle implicit client-side statement caching
• Once enabled, all statements are cached.
• Can be disabled on a per statement basis
if (statement.isPoolable()) {
statement.setPoolable(false);
}
@vlad_mihalcea vladmihalcea.com
Oracle explicit client-side statement caching
• Caches both metadata, execution state and data
OracleConnection oracleConnection =
(OracleConnection) connection;
oracleConnection.setExplicitCachingEnabled(true);
oracleConnection.setStatementCacheSize(cacheSize);
@vlad_mihalcea vladmihalcea.com
Oracle explicit client-side statement caching
PreparedStatement statement = oracleConnection.
getStatementWithKey(SELECT_POST_KEY);
if (statement == null) {
statement = connection.prepareStatement(SELECT_POST);
}
try {
statement.setInt(1, 10);
statement.execute();
} finally {
((OraclePreparedStatement) statement).closeWithKey(SELECT_POST_KEY);
}
@vlad_mihalcea vladmihalcea.com
SQL Server client-side statement caching
• The SQL Server JDBC driver version 6.3 added support for statement
caching.
• Prepared Statement caching is disabled by default.
connection.setStatementPoolingCacheSize(10);
connection.setDisableStatementPooling(false);
@vlad_mihalcea vladmihalcea.com
PostgreSQL Server client-side statement caching
• PostgreSQL JDBC Driver 9.4-1202 makes client-side statement
connection-bound instead of statement-bound
• Configurable:
• preparedStatementCacheQueries (default is 256)
• preparedStatementCacheSizeMiB (default is 5MB)
• Statement.setPoolable(false) is not supported
@vlad_mihalcea vladmihalcea.com
MySQL Server client-side statement caching
• Configurable:
• cachePrepStmts (default is false)
Required for server-side statement caching as well
• prepStmtCacheSize (default is 25)
• prepStmtCacheSqlLimit (default is 256)
• Statement.setPoolable(false) works for server-side
statements only
@vlad_mihalcea vladmihalcea.com
Statement caching gain (one minute interval)
Database System No Caching Throughput
(SPM)
Caching Throughput
(SPM)
Percentage Gain
DB_A 419 833 507 286 20.83%
DB_B 194 837 303 100 55.56%
DB_C 116 708 166 443 42.61%
DB_D 15 522 15 550 0.18%
@vlad_mihalcea vladmihalcea.com
Queries
CC BY 2.0 - https://www.flickr.com/photos/justinbaeder/5317820857/
@vlad_mihalcea vladmihalcea.com
Criteria API literal handling
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);
Root<Book> root = cq.from(Book.class);
cq.select(root);
cq.where(cb.equal(root.get("name"), "High-Performance Java Persistence"));
Book book = entityManager.createQuery(cq).getSingleResult();
SELECT
b.id AS id1_0_, b.isbn AS isbn2_0_, b.name AS name3_0_
FROM
book b
WHERE
b.name = ?
@vlad_mihalcea vladmihalcea.com
Criteria API literal handling
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);
Root<Book> root = cq.from(Book.class);
cq.select(root);
cq.where(cb.equal(root.get("isbn"), 978_9730228236L));
Book book = entityManager.createQuery(cq).getSingleResult();
SELECT
b.id AS id1_0_, b.isbn AS isbn2_0_, b.name AS name3_0_
FROM
book b
WHERE
b.isbn = 9789730228236
@vlad_mihalcea vladmihalcea.com
Criteria API literal handling
@vlad_mihalcea vladmihalcea.com
Criteria API literal handling
<property
name="hibernate.criteria.literal_handling_mode"
value="bind"
/>
<property
name="hibernate.criteria.literal_handling_mode"
value="inline"
/>
<property
name="hibernate.criteria.literal_handling_mode"
value="auto"
/>
@vlad_mihalcea vladmihalcea.com
IN query padding optimization
List<Post> getPostByIds(EntityManager entityManager, Integer... ids) {
return entityManager.createQuery(
"select p " +
"from Post p " +
"where p.id in :ids", Post.class)
.setParameter("ids", Arrays.asList(ids))
.getResultList();
}
@vlad_mihalcea vladmihalcea.com
IN query padding optimization
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?)
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?)
assertEquals(3, getPostByIds(entityManager, 1, 2, 3).size());
assertEquals(4, getPostByIds(entityManager, 1, 2, 3, 4).size());
@vlad_mihalcea vladmihalcea.com
IN query padding optimization
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?, ?)
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?, ?, ?)
assertEquals(5, getPostByIds(entityManager, 1, 2, 3, 4, 5).size());
assertEquals(6, getPostByIds(entityManager, 1, 2, 3, 4, 5, 6).size());
@vlad_mihalcea vladmihalcea.com
IN query padding optimization
@vlad_mihalcea vladmihalcea.com
IN query padding optimization
<property name="hibernate.query.in_clause_parameter_padding" value="true" />
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?) -- Params: (1, 2, 3, 3)
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?) -- Params: (1, 2, 3, 4)
assertEquals(3, getPostByIds(entityManager, 1, 2, 3).size());
assertEquals(4, getPostByIds(entityManager, 1, 2, 3, 4).size());
@vlad_mihalcea vladmihalcea.com
IN query padding optimization
assertEquals(5, getPostByIds(entityManager, 1, 2, 3, 4, 5).size());
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?, ?, ?, ?, ?) -- Params: (1, 2, 3, 4, 5, 5, 5, 5)
assertEquals(6, getPostByIds(entityManager, 1, 2, 3, 4, 5, 6).size());
SELECT p.id AS id1_0_, p.title AS title2_0_
FROM post p
WHERE p.id IN (?, ?, ?, ?, ?, ?, ?, ?) -- Params: (1, 2, 3, 4, 5, 6, 6, 6)
@vlad_mihalcea vladmihalcea.com
Query plan cache
• Entity queries (JPQL, Criteria API) need to be compiled to SQL
• Configurable:
• hibernate.query.plan_cache_max_size (2048)
• hibernate.query.plan_parameter_metadata_max_size (128)
@vlad_mihalcea vladmihalcea.com
Entity query plan cache improvement
@vlad_mihalcea vladmihalcea.com
Native SQL query plan cache improvement
@vlad_mihalcea vladmihalcea.com
JPQL DISTINCT scalar query
List<Integer> publicationYears = entityManager.createQuery(
"select distinct year(p.createdOn) " +
"from Post p " +
"order by year(p.createdOn)", Integer.class)
.getResultList();
SELECT DISTINCT
extract(YEAR FROM p.created_on) AS col_0_0_
FROM post p
ORDER BY (YEAR FROM p.created_on)
@vlad_mihalcea vladmihalcea.com
JPQL DISTINCT entity query
List<Post> posts = entityManager.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.title = :title", Post.class)
.setParameter("title", "High-Performance Java Persistence")
.getResultList();
SELECT DISTINCT
p.id AS id1_0_0_, pc.id AS id1_1_1_, p.title AS title2_0_0_,
pc.review AS review2_1_1_, pc.post_id AS post_id3_1_0__,
pc.id AS id1_1_0__
FROM post p
LEFT OUTER JOIN post_comment pc ON p.id=pc.post_id
WHERE p.title = ?
@vlad_mihalcea vladmihalcea.com
JPQL DISTINCT entity query
@vlad_mihalcea vladmihalcea.com
JPQL DISTINCT entity query
List<Post> posts = entityManager.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.title = :title", Post.class)
.setParameter("title", "High-Performance Java Persistence")
.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
.getResultList();
SELECT
p.id AS id1_0_0_, pc.id AS id1_1_1_, p.title AS title2_0_0_,
pc.review AS review2_1_1_, pc.post_id AS post_id3_1_0__,
pc.id AS id1_1_0__
FROM post p
LEFT OUTER JOIN post_comment pc ON p.id=pc.post_id
WHERE p.title = ?
@vlad_mihalcea vladmihalcea.com
JPQL DISTINCT entity query
@vlad_mihalcea vladmihalcea.com
Fetching
CC BY 2.0 - https://www.flickr.com/photos/bala_/3544603505/
@vlad_mihalcea vladmihalcea.com
Persistence Context – loaded state
@vlad_mihalcea vladmihalcea.com
Persistence Context – dirty checking
@vlad_mihalcea vladmihalcea.com
Persistence Context size
//session-level configuration
Session session = entityManager.unwrap(Session.class);
session.setDefaultReadOnly(true);
//query-level configuration
List<Post> posts = entityManager.createQuery(
"select p from Post p", Post.class)
.setHint(QueryHints.HINT_READONLY, true)
.getResultList();
@vlad_mihalcea vladmihalcea.com
@Transactional(readOnly = true)
public List<Post> findAllByTitle(String title) {
List<Post> posts = postDAO.findByTitle(title);
org.hibernate.engine.spi.PersistenceContext persistenceContext =
getHibernatePersistenceContext();
for(Post post : posts) {
assertTrue(entityManager.contains(post));
EntityEntry entityEntry = persistenceContext.getEntry(post);
assertNull(entityEntry.getLoadedState());
}
return posts;
}
Spring 5.1 read-only optimization
@vlad_mihalcea vladmihalcea.com
Fetching – Pagination
• JPA / Hibernate API works for both entity and native SQL queries
List<PostCommentSummary> summaries =
entityManager.createQuery(
"select new PostCommentSummary( " +
" p.id, p.title, c.review ) " +
"from PostComment c " +
"join c.post p")
.setFirstResult(pageStart)
.setMaxResults(pageSize)
.getResultList();
@vlad_mihalcea vladmihalcea.com
Fetching – 100k vs 100 rows
Fetch all Fetch limit
0
500
1000
1500
2000
2500
3000
3500
4000
4500
5000
Time(ms)
DB_A DB_B DB_C DB_D
@vlad_mihalcea vladmihalcea.com
JPA 2.2 Streaming
@vlad_mihalcea vladmihalcea.com
JPA 2.2 Streaming
default Stream getResultStream() {
return getResultList().stream();
}
final ScrollableResultsImplementor scrollableResults = scroll(
ScrollMode.FORWARD_ONLY
);
@vlad_mihalcea vladmihalcea.com
JDBC-level streaming support
• You still need to consider the Statement.setFetchSize.
• On MySQL, you need to set it to Integer.MIN_VALUE.
• On PostgreSQL, you need to set it to a positive integer value.
• What about the Execution Plan?
@vlad_mihalcea vladmihalcea.com
JPA 2.2 Streaming – Execution Plans
List<String> executionPlanLines = doInJPA(entityManager -> {
try(Stream<String> postStream = entityManager
.createNativeQuery(
"EXPLAIN ANALYZE " +
"SELECT p " +
"FROM post p " +
"ORDER BY p.created_on DESC")
.setHint(QueryHints.HINT_FETCH_SIZE, 50)
.getResultStream()
) {
return postStream.limit(50).collect(Collectors.toList());
}
});
CREATE INDEX idx_post_created_on ON post (created_on DESC)
@vlad_mihalcea vladmihalcea.com
JPA 2.2 Streaming – Execution Plans
LOGGER.info( "Execution plan: {}",
executionPlanLines
.stream()
.collect(Collectors.joining( "n" ))
);
Execution plan:
Sort (cost=65.53..66.83 rows=518 width=564)
(actual time=2.876..3.399 rows=5000 loops=1)
Sort Key: created_on DESC
Sort Method: quicksort Memory: 896kB
-> Seq Scan on post p
(cost=0.00..42.18 rows=518 width=564)
(actual time=0.050..1.371 rows=5000 loops=1)
Planning time: 1.586 ms
Execution time: 4.061 ms
@vlad_mihalcea vladmihalcea.com
JPA 2.2 Streaming – Execution Plans
List<String> executionPlanLines = doInJPA(entityManager -> {
return entityManager
.createNativeQuery(
"EXPLAIN ANALYZE " +
"SELECT p " +
"FROM post p " +
"ORDER BY p.created_on DESC")
.setMaxResults(50)
.getResultList();
});
LOGGER.info("Execution plan: {}",
executionPlanLines
.stream()
.collect(Collectors.joining("n"))
);
@vlad_mihalcea vladmihalcea.com
JPA 2.2 Streaming – Execution Plans
Execution plan:
Limit (cost=0.28..25.35 rows=50 width=564)
(actual time=0.038..0.051 rows=50 loops=1)
-> Index Scan using idx_post_created_on on post p
(cost=0.28..260.04 rows=518 width=564)
(actual time=0.037..0.049 rows=50 loops=1)
Planning time: 1.511 ms
Execution time: 0.148 ms
@vlad_mihalcea vladmihalcea.com
Fetching – Open Session in View Anti-Pattern
@vlad_mihalcea vladmihalcea.com
Fetching – Open Session in View Anti-Pattern
@vlad_mihalcea vladmihalcea.com
Fetching – Temporary Session Anti-Pattern
• “Band aid” for LazyInitializationException
• One temporary Session/Connection for every lazily fetched
association
<property
name="hibernate.enable_lazy_load_no_trans"
value="true"/>
@vlad_mihalcea vladmihalcea.com
Batching
CC BY-SA 2.0 - https://www.flickr.com/photos/dozodomo/6975654335/
@vlad_mihalcea vladmihalcea.com
JDBC Batch Updates
@vlad_mihalcea vladmihalcea.com
Transactional write-behind cache
@vlad_mihalcea vladmihalcea.com
Action queue
@vlad_mihalcea vladmihalcea.com
Batch processing – flush-clear-commit
try {
entityManager.getTransaction().begin();
for ( int i = 0; i < entityCount; ++i ) {
if ( i > 0 && i % batchSize == 0 ) {
flush( entityManager );
}
entityManager.persist( new Post( String.format( "Post %d", i + 1 ) ) );
}
entityManager.getTransaction().commit();
} catch (RuntimeException e) {
if ( entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
throw e;
} finally {
entityManager.close();
}
@vlad_mihalcea vladmihalcea.com
private void flush(EntityManager entityManager) {
entityManager.flush();
entityManager.clear();
entityManager.getTransaction().commit();
entityManager.getTransaction().begin();
}
Batch processing – flush and commit
@vlad_mihalcea vladmihalcea.com
Hibernate batching – default behavior
for (int i = 0; i < 3; i++) {
entityManager.persist(
new Post(String.format("Post no. %d", i + 1))
);
}
INSERT INTO post (title, id) VALUES ('Post no. 1', 1)
INSERT INTO post (title, id) VALUES ('Post no. 2', 2)
INSERT INTO post (title, id) VALUES ('Post no. 3', 3)
@vlad_mihalcea vladmihalcea.com
Enable JDBC batching
<property name="hibernate.jdbc.batch_size" value="5"/>
Query: ["INSERT INTO post (title, id) VALUES (?, ?)"],
Params: [('Post no. 1', 1),
('Post no. 2', 2),
('Post no. 3', 3)]
entityManager.unwrap(Session.class).setJdbcBatchSize(10);
for (long i = 0; i < 10; ++i) {
Post post = new Post();
post.setTitle(String.format("Post nr %d", i));
entityManager.persist(post);
}
@vlad_mihalcea vladmihalcea.com
PostgreSQL batch statements
log_statement = 'all'
LOG: execute S_2: insert into post (title, id) values ($1, $2)
DETAIL: parameters: $1 = 'Post no. 1', $2 = '1'
LOG: execute S_2: insert into post (title, id) values ($1, $2)
DETAIL: parameters: $1 = 'Post no. 2', $2 = '2'
LOG: execute S_2: insert into post (title, id) values ($1, $2)
DETAIL: parameters: $1 = 'Post no. 3', $2 = '3'
@vlad_mihalcea vladmihalcea.com
PostgreSQL rewrite batch statements
PGSimpleDataSource dataSource = (PGSimpleDataSource) dataSource();
dataSource.setReWriteBatchedInserts(true);
LOG: execute <unnamed>: insert into post (title, id)
values ($1, $2),($3, $4),($5, $6)
DETAIL: parameters: $1 = 'Post no. 1', $2 = '1’,
$3 = 'Post no. 2', $4 = '2’,
$5 = 'Post no. 3', $6 = '3'
@vlad_mihalcea vladmihalcea.com
Default UPDATE - Post entity mapping
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
private Long id;
private String title;
private long likes;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
Default UPDATE - Post entity data
Post post1 = new Post();
post1.setId(1L);
post1.setTitle("High-Performance Java Persistence");
entityManager.persist(post1);
Post post2 = new Post();
post2.setId(2L);
post2.setTitle("Java Persistence with Hibernate");
entityManager.persist(post2);
@vlad_mihalcea vladmihalcea.com
Default UPDATE - statement batching
Post post1 = entityManager.find(Post.class, 1L);
post1.setTitle("High-Performance Java Persistence 2nd Edition");
Post post2 = entityManager.find(Post.class, 2L);
post2.setLikes(12);
entityManager.flush();
Query :[
"update post set likes=?, title=? where id=?"
],
Params:[
(0, High-Performance Java Persistence 2nd Edition, 1),
(12, Java Persistence with Hibernate, 2)
]
@vlad_mihalcea vladmihalcea.com
Default UPDATE disadvantages
• Column size
• Indexes
• Replication
• Undo and redo log
• Triggers
@vlad_mihalcea vladmihalcea.com
Hibernate dynamic update
@Entity(name = "Post")
@Table(name = "post")
@DynamicUpdate
public class Post {
@Id
private Long id;
private String title;
private long likes;
//Getters and setters omitted for brevity
}
@vlad_mihalcea vladmihalcea.com
Hibernate dynamic update
Query:["update post set title=? where id=?"],
Params:[(High-Performance Java Persistence 2nd Edition, 1)]
Query:["update post set likes=? where id=?"],
Params:[(12, 2)]
Post post1 = entityManager.find(Post.class, 1L);
post1.setTitle("High-Performance Java Persistence 2nd Edition");
Post post2 = entityManager.find(Post.class, 2L);
post2.setlikes(12);
entityManager.flush();
@vlad_mihalcea vladmihalcea.com
Updating detached entities - Post and PostComment
List<Post> posts = doInJPA(entityManager -> {
return entityManager.createQuery(
"select p " +
"from Post p " +
"join fetch p.comments ", Post.class)
.getResultList();
});
for (Post post: posts) {
post.setTitle("Vlad Mihalcea's " + post.getTitle());
for (PostComment comment: post.getComments()) {
comment.setReview(comment.getReview() + " read!");
}
}
@vlad_mihalcea vladmihalcea.com
• JPA merge
• Hibernate update
JPA merge and Hibernate update
for (Post post: posts) {
entityManager.merge(post);
}
Session session = entityManager.unwrap(Session.class);
for (Post post: posts) {
session.update(post);
}
@vlad_mihalcea vladmihalcea.com
JPA merge – SELECT statements
SELECT p.id AS id1_0_1_, p.title AS title2_0_1_, c.post_id AS post_id3_1_3_,
c.id AS id1_1_3_, c.review AS review2_1_0_
FROM post p
LEFT OUTER JOIN post_comment c ON p.id = c.post_id
WHERE p.id = 1
SELECT p.id AS id1_0_1_, p.title AS title2_0_1_, c.post_id AS post_id3_1_3_,
c.id AS id1_1_3_, c.review AS review2_1_0_
FROM post p
LEFT OUTER JOIN post_comment c ON p.id = c.post_id
WHERE p.id = 3
SELECT p.id AS id1_0_1_, p.title AS title2_0_1_, c.post_id AS post_id3_1_3_,
c.id AS id1_1_3_, c.review AS review2_1_0_
FROM post p
LEFT OUTER JOIN post_comment c ON p.id = c.post_id
WHERE p.id = 5
…
@vlad_mihalcea vladmihalcea.com
JPA merge – UPDATE statements
Query:[
"update post set title=? where id=?"
],
Params:[
(Vlad Mihalcea's High-Performance Java Persistence, Part no. 0, 1),
(Vlad Mihalcea's High-Performance Java Persistence, Part no. 1, 3),
(Vlad Mihalcea's High-Performance Java Persistence, Part no. 2, 5)
]
Query:[
"update post_comment set post_id=?, review=? where id=?"
],
Params:[
(1, Excellent read!, 2),
(3, Excellent read!, 4),
(5, Excellent read!, 6)
]
@vlad_mihalcea vladmihalcea.com
Hibernate-specific update operation
Query:[
"update post set title=? where id=?"
],
Params:[
(Vlad Mihalcea's High-Performance Java Persistence, Part no. 0, 1),
(Vlad Mihalcea's High-Performance Java Persistence, Part no. 1, 3),
(Vlad Mihalcea's High-Performance Java Persistence, Part no. 2, 5)
]
Query:[
"update post_comment set post_id=?, review=? where id=?"
],
Params:[
(1, Excellent read!, 2),
(3, Excellent read!, 4),
(5, Excellent read!, 6)
]
@vlad_mihalcea vladmihalcea.com
Connections
CC BY 2.0 - https://www.flickr.com/photos/justinbaeder/5317820857/
@vlad_mihalcea vladmihalcea.com
Resource-local connection acquisition
@vlad_mihalcea vladmihalcea.com
Immediate connection acquisition
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void importForecasts(String dataFilePath) {
Document forecastXmlDocument = readXmlDocument( dataFilePath );
List<Forecast> forecasts = parseForecasts(forecastXmlDocument);
for(Forecast forecast : forecasts) {
entityManager.persist( forecast );
}
}
@vlad_mihalcea vladmihalcea.com
Resource-local delay connection acquisition
<property
name="hibernate.connection.provider_disables_autocommit"
value="true"
/>
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDataSourceClassName(dataSourceClassName);
hikariConfig.setDataSourceProperties(dataSourceProperties);
hikariConfig.setMinimumPoolSize(minPoolSize);
hikariConfig.setMaximumPoolSize(maxPoolSize);
hikariConfig.setAutoCommit(false);
DataSource datasource = new HikariDataSource(hikariConfig);
@vlad_mihalcea vladmihalcea.com
Resource-local connection acquisition optimization
@vlad_mihalcea vladmihalcea.com
Thank you
• Twitter: @vlad_mihalcea
• Blog: https://vladmihalcea.com
• Courses: https://vladmihalcea.com/courses
• Book: https://vladmihalcea.com/books/high-performance-java-persistence/
1 of 100

Recommended

片手間MySQLチューニング戦略 by
片手間MySQLチューニング戦略片手間MySQLチューニング戦略
片手間MySQLチューニング戦略yoku0825
21.2K views78 slides
07 Using Oracle-Supported Package in Application Development by
07 Using Oracle-Supported Package in Application Development07 Using Oracle-Supported Package in Application Development
07 Using Oracle-Supported Package in Application Developmentrehaniltifat
954 views16 slides
ProxySQL and the Tricks Up Its Sleeve - Percona Live 2022.pdf by
ProxySQL and the Tricks Up Its Sleeve - Percona Live 2022.pdfProxySQL and the Tricks Up Its Sleeve - Percona Live 2022.pdf
ProxySQL and the Tricks Up Its Sleeve - Percona Live 2022.pdfJesmar Cannao'
202 views65 slides
Open Source 101 2022 - MySQL Indexes and Histograms by
Open Source 101 2022 - MySQL Indexes and HistogramsOpen Source 101 2022 - MySQL Indexes and Histograms
Open Source 101 2022 - MySQL Indexes and HistogramsFrederic Descamps
358 views185 slides
ACL in CodeIgniter by
ACL in CodeIgniterACL in CodeIgniter
ACL in CodeIgnitermirahman
13.5K views16 slides
PostgreSQL WAL for DBAs by
PostgreSQL WAL for DBAs PostgreSQL WAL for DBAs
PostgreSQL WAL for DBAs PGConf APAC
4.6K views35 slides

More Related Content

What's hot

Introduction to PostgreSQL by
Introduction to PostgreSQLIntroduction to PostgreSQL
Introduction to PostgreSQLJoel Brewer
881 views33 slides
PostgreSQL for Oracle Developers and DBA's by
PostgreSQL for Oracle Developers and DBA'sPostgreSQL for Oracle Developers and DBA's
PostgreSQL for Oracle Developers and DBA'sGerger
799 views37 slides
Ngrx slides by
Ngrx slidesNgrx slides
Ngrx slidesChristoffer Noring
2.8K views97 slides
MySQL InnoDB Cluster - Advanced Configuration & Operations by
MySQL InnoDB Cluster - Advanced Configuration & OperationsMySQL InnoDB Cluster - Advanced Configuration & Operations
MySQL InnoDB Cluster - Advanced Configuration & OperationsFrederic Descamps
729 views160 slides
Understanding MySQL Performance through Benchmarking by
Understanding MySQL Performance through BenchmarkingUnderstanding MySQL Performance through Benchmarking
Understanding MySQL Performance through BenchmarkingLaine Campbell
12.9K views181 slides
MySQL InnoDB Cluster and Group Replication in a nutshell hands-on tutorial by
MySQL InnoDB Cluster and Group Replication in a nutshell  hands-on tutorialMySQL InnoDB Cluster and Group Replication in a nutshell  hands-on tutorial
MySQL InnoDB Cluster and Group Replication in a nutshell hands-on tutorialFrederic Descamps
1.4K views145 slides

What's hot(20)

Introduction to PostgreSQL by Joel Brewer
Introduction to PostgreSQLIntroduction to PostgreSQL
Introduction to PostgreSQL
Joel Brewer881 views
PostgreSQL for Oracle Developers and DBA's by Gerger
PostgreSQL for Oracle Developers and DBA'sPostgreSQL for Oracle Developers and DBA's
PostgreSQL for Oracle Developers and DBA's
Gerger799 views
MySQL InnoDB Cluster - Advanced Configuration & Operations by Frederic Descamps
MySQL InnoDB Cluster - Advanced Configuration & OperationsMySQL InnoDB Cluster - Advanced Configuration & Operations
MySQL InnoDB Cluster - Advanced Configuration & Operations
Frederic Descamps729 views
Understanding MySQL Performance through Benchmarking by Laine Campbell
Understanding MySQL Performance through BenchmarkingUnderstanding MySQL Performance through Benchmarking
Understanding MySQL Performance through Benchmarking
Laine Campbell12.9K views
MySQL InnoDB Cluster and Group Replication in a nutshell hands-on tutorial by Frederic Descamps
MySQL InnoDB Cluster and Group Replication in a nutshell  hands-on tutorialMySQL InnoDB Cluster and Group Replication in a nutshell  hands-on tutorial
MySQL InnoDB Cluster and Group Replication in a nutshell hands-on tutorial
Frederic Descamps1.4K views
PostgreSQL Database Slides by metsarin
PostgreSQL Database SlidesPostgreSQL Database Slides
PostgreSQL Database Slides
metsarin5.3K views
Oracle PLSQL Step By Step Guide by Srinimf-Slides
Oracle PLSQL Step By Step GuideOracle PLSQL Step By Step Guide
Oracle PLSQL Step By Step Guide
Srinimf-Slides 4.2K views
Intro ProxySQL by I Goo Lee
Intro ProxySQLIntro ProxySQL
Intro ProxySQL
I Goo Lee1.7K views
Tanel Poder - Scripts and Tools short by Tanel Poder
Tanel Poder - Scripts and Tools shortTanel Poder - Scripts and Tools short
Tanel Poder - Scripts and Tools short
Tanel Poder4.7K views
Federated Engine 실무적용사례 by I Goo Lee
Federated Engine 실무적용사례Federated Engine 실무적용사례
Federated Engine 실무적용사례
I Goo Lee3.2K views
MySQL Database Architectures - InnoDB ReplicaSet & Cluster by Kenny Gryp
MySQL Database Architectures - InnoDB ReplicaSet & ClusterMySQL Database Architectures - InnoDB ReplicaSet & Cluster
MySQL Database Architectures - InnoDB ReplicaSet & Cluster
Kenny Gryp2.6K views
11 Understanding and Influencing the PL/SQL Compilar by rehaniltifat
11 Understanding and Influencing the PL/SQL Compilar11 Understanding and Influencing the PL/SQL Compilar
11 Understanding and Influencing the PL/SQL Compilar
rehaniltifat1.7K views
Maria db 이중화구성_고민하기 by NeoClova
Maria db 이중화구성_고민하기Maria db 이중화구성_고민하기
Maria db 이중화구성_고민하기
NeoClova3.4K views
Oracle REST Data Services Best Practices/ Overview by Kris Rice
Oracle REST Data Services Best Practices/ OverviewOracle REST Data Services Best Practices/ Overview
Oracle REST Data Services Best Practices/ Overview
Kris Rice16.4K views
PostgreSQL Deep Internal by EXEM
PostgreSQL Deep InternalPostgreSQL Deep Internal
PostgreSQL Deep Internal
EXEM2.5K views
Reactive design: languages, and paradigms by Dean Wampler
Reactive design: languages, and paradigmsReactive design: languages, and paradigms
Reactive design: languages, and paradigms
Dean Wampler27.2K views
Oracle 21c: New Features and Enhancements of Data Pump & TTS by Christian Gohmann
Oracle 21c: New Features and Enhancements of Data Pump & TTSOracle 21c: New Features and Enhancements of Data Pump & TTS
Oracle 21c: New Features and Enhancements of Data Pump & TTS
Christian Gohmann530 views
[2018] MySQL 이중화 진화기 by NHN FORWARD
[2018] MySQL 이중화 진화기[2018] MySQL 이중화 진화기
[2018] MySQL 이중화 진화기
NHN FORWARD10.7K views
텔레그램을 이용한 양방향 모니터링 시스템 구축 by I Goo Lee
텔레그램을 이용한 양방향 모니터링 시스템 구축텔레그램을 이용한 양방향 모니터링 시스템 구축
텔레그램을 이용한 양방향 모니터링 시스템 구축
I Goo Lee1.7K views

Similar to JPA and Hibernate Performance Tips

Spring data requery by
Spring data requerySpring data requery
Spring data requerySunghyouk Bae
1.9K views46 slides
Spring 3: What's New by
Spring 3: What's NewSpring 3: What's New
Spring 3: What's NewTed Pennings
723 views30 slides
Sqlapi0.1 by
Sqlapi0.1Sqlapi0.1
Sqlapi0.1jitendral
384 views32 slides
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010 by
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010Arun Gupta
5.3K views34 slides
JDBC programming by
JDBC programmingJDBC programming
JDBC programmingFulvio Corno
893 views31 slides
Jdbc[1] by
Jdbc[1]Jdbc[1]
Jdbc[1]Fulvio Corno
452 views31 slides

Similar to JPA and Hibernate Performance Tips(20)

Spring 3: What's New by Ted Pennings
Spring 3: What's NewSpring 3: What's New
Spring 3: What's New
Ted Pennings723 views
Sqlapi0.1 by jitendral
Sqlapi0.1Sqlapi0.1
Sqlapi0.1
jitendral384 views
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010 by Arun Gupta
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010
Arun Gupta5.3K views
Scala Frameworks for Web Application 2016 by takezoe
Scala Frameworks for Web Application 2016Scala Frameworks for Web Application 2016
Scala Frameworks for Web Application 2016
takezoe7.2K views
Spring Day | Spring and Scala | Eberhard Wolff by JAX London
Spring Day | Spring and Scala | Eberhard WolffSpring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard Wolff
JAX London1.5K views
Integrating Wicket with Java EE 6 by Michael Plöd
Integrating Wicket with Java EE 6Integrating Wicket with Java EE 6
Integrating Wicket with Java EE 6
Michael Plöd3.7K views
Introduction to JDBC and database access in web applications by Fulvio Corno
Introduction to JDBC and database access in web applicationsIntroduction to JDBC and database access in web applications
Introduction to JDBC and database access in web applications
Fulvio Corno2.7K views
Multi Client Development with Spring by Joshua Long
Multi Client Development with SpringMulti Client Development with Spring
Multi Client Development with Spring
Joshua Long4K views
Spring Framework Petclinic sample application by Antoine Rey
Spring Framework Petclinic sample applicationSpring Framework Petclinic sample application
Spring Framework Petclinic sample application
Antoine Rey31.7K views
JavaOne India 2011 - Servlets 3.0 by Arun Gupta
JavaOne India 2011 - Servlets 3.0JavaOne India 2011 - Servlets 3.0
JavaOne India 2011 - Servlets 3.0
Arun Gupta829 views
Integrating SAP the Java EE Way - JBoss One Day talk 2012 by hwilming
Integrating SAP the Java EE Way - JBoss One Day talk 2012Integrating SAP the Java EE Way - JBoss One Day talk 2012
Integrating SAP the Java EE Way - JBoss One Day talk 2012
hwilming6.2K views
Red Hat Agile integration Workshop Labs by Judy Breedlove
Red Hat Agile integration Workshop LabsRed Hat Agile integration Workshop Labs
Red Hat Agile integration Workshop Labs
Judy Breedlove769 views
20151010 my sq-landjavav2a by Ivan Ma
20151010 my sq-landjavav2a20151010 my sq-landjavav2a
20151010 my sq-landjavav2a
Ivan Ma327 views

More from Vlad Mihalcea

Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019 by
 Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019 Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019
Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019Vlad Mihalcea
974 views124 slides
Transactions and Concurrency Control Patterns - 2019 by
Transactions and Concurrency Control Patterns - 2019Transactions and Concurrency Control Patterns - 2019
Transactions and Concurrency Control Patterns - 2019Vlad Mihalcea
1.2K views122 slides
High-Performance Hibernate - JDK.io 2018 by
High-Performance Hibernate - JDK.io 2018High-Performance Hibernate - JDK.io 2018
High-Performance Hibernate - JDK.io 2018Vlad Mihalcea
1.2K views77 slides
Transactions and Concurrency Control Patterns by
Transactions and Concurrency Control PatternsTransactions and Concurrency Control Patterns
Transactions and Concurrency Control PatternsVlad Mihalcea
2.7K views58 slides
High Performance Hibernate JavaZone 2016 by
High Performance Hibernate JavaZone 2016High Performance Hibernate JavaZone 2016
High Performance Hibernate JavaZone 2016Vlad Mihalcea
4K views68 slides
High-Performance Hibernate Devoxx France 2016 by
High-Performance Hibernate Devoxx France 2016High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016Vlad Mihalcea
17.3K views49 slides

More from Vlad Mihalcea(7)

Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019 by Vlad Mihalcea
 Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019 Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019
Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019
Vlad Mihalcea974 views
Transactions and Concurrency Control Patterns - 2019 by Vlad Mihalcea
Transactions and Concurrency Control Patterns - 2019Transactions and Concurrency Control Patterns - 2019
Transactions and Concurrency Control Patterns - 2019
Vlad Mihalcea1.2K views
High-Performance Hibernate - JDK.io 2018 by Vlad Mihalcea
High-Performance Hibernate - JDK.io 2018High-Performance Hibernate - JDK.io 2018
High-Performance Hibernate - JDK.io 2018
Vlad Mihalcea1.2K views
Transactions and Concurrency Control Patterns by Vlad Mihalcea
Transactions and Concurrency Control PatternsTransactions and Concurrency Control Patterns
Transactions and Concurrency Control Patterns
Vlad Mihalcea2.7K views
High Performance Hibernate JavaZone 2016 by Vlad Mihalcea
High Performance Hibernate JavaZone 2016High Performance Hibernate JavaZone 2016
High Performance Hibernate JavaZone 2016
Vlad Mihalcea4K views
High-Performance Hibernate Devoxx France 2016 by Vlad Mihalcea
High-Performance Hibernate Devoxx France 2016High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016
Vlad Mihalcea17.3K views
High-Performance JDBC Voxxed Bucharest 2016 by Vlad Mihalcea
High-Performance JDBC Voxxed Bucharest 2016High-Performance JDBC Voxxed Bucharest 2016
High-Performance JDBC Voxxed Bucharest 2016
Vlad Mihalcea11.8K views

Recently uploaded

Best Mics For Your Live Streaming by
Best Mics For Your Live StreamingBest Mics For Your Live Streaming
Best Mics For Your Live Streamingontheflystream
6 views6 slides
DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM... by
DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM...DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM...
DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM...Deltares
7 views40 slides
Cycleops - Automate deployments on top of bare metal.pptx by
Cycleops - Automate deployments on top of bare metal.pptxCycleops - Automate deployments on top of bare metal.pptx
Cycleops - Automate deployments on top of bare metal.pptxThanassis Parathyras
30 views12 slides
DevsRank by
DevsRankDevsRank
DevsRankdevsrank786
10 views1 slide
SUGCON ANZ Presentation V2.1 Final.pptx by
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptxJack Spektor
21 views34 slides
Software testing company in India.pptx by
Software testing company in India.pptxSoftware testing company in India.pptx
Software testing company in India.pptxSakshiPatel82
7 views9 slides

Recently uploaded(20)

DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM... by Deltares
DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM...DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM...
DSD-INT 2023 Next-Generation Flood Inundation Mapping for Taiwan - Delft3D FM...
Deltares7 views
Cycleops - Automate deployments on top of bare metal.pptx by Thanassis Parathyras
Cycleops - Automate deployments on top of bare metal.pptxCycleops - Automate deployments on top of bare metal.pptx
Cycleops - Automate deployments on top of bare metal.pptx
SUGCON ANZ Presentation V2.1 Final.pptx by Jack Spektor
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptx
Jack Spektor21 views
Software testing company in India.pptx by SakshiPatel82
Software testing company in India.pptxSoftware testing company in India.pptx
Software testing company in India.pptx
SakshiPatel827 views
Upgrading Incident Management with Icinga - Icinga Camp Milan 2023 by Icinga
Upgrading Incident Management with Icinga - Icinga Camp Milan 2023Upgrading Incident Management with Icinga - Icinga Camp Milan 2023
Upgrading Incident Management with Icinga - Icinga Camp Milan 2023
Icinga36 views
DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove... by Deltares
DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove...DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove...
DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove...
Deltares15 views
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by Deltares
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
Deltares7 views
Tridens DevOps by Tridens
Tridens DevOpsTridens DevOps
Tridens DevOps
Tridens9 views
Roadmap y Novedades de producto by Neo4j
Roadmap y Novedades de productoRoadmap y Novedades de producto
Roadmap y Novedades de producto
Neo4j43 views
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ... by Donato Onofri
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ...Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ...
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ...
Donato Onofri643 views
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida by Deltares
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - PridaDSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida
Deltares17 views
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ... by marksimpsongw
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...
marksimpsongw74 views
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ... by Deltares
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
Deltares9 views
DSD-INT 2023 Simulating a falling apron in Delft3D 4 - Engineering Practice -... by Deltares
DSD-INT 2023 Simulating a falling apron in Delft3D 4 - Engineering Practice -...DSD-INT 2023 Simulating a falling apron in Delft3D 4 - Engineering Practice -...
DSD-INT 2023 Simulating a falling apron in Delft3D 4 - Engineering Practice -...
Deltares6 views

JPA and Hibernate Performance Tips

  • 1. @vlad_mihalcea vladmihalcea.com JPA and Hibernate Performance Tips
  • 3. @vlad_mihalcea vladmihalcea.com Mappings CC BY-SA 2.0 - https://www.flickr.com/photos/47515486@N05/44129053595/
  • 4. @vlad_mihalcea vladmihalcea.com TABLE generator @Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id; private String title; //Getters and setters omitted for brevity }
  • 5. @vlad_mihalcea vladmihalcea.com TABLE generator CREATE TABLE hibernate_sequences ( sequence_name (255) NOT NULL, next_val, PRIMARY KEY (sequence_name) ) CREATE TABLE post ( id NOT NULL, title (255), PRIMARY KEY (id) )
  • 6. @vlad_mihalcea vladmihalcea.com TABLE generator public IntegralDataTypeHolder getNextValue() { return session.getTransactionCoordinator().createIsolationDelegate() .delegateWork(new AbstractReturningWork<IntegralDataTypeHolder>() { public IntegralDataTypeHolder execute( Connection connection) throws SQLException { … }, true ); } for (int i = 0; i < 3; i++) { Post post = new Post(); post.setTitle( String.format("High-Performance Java Persistence, Part %d", i + 1) ); entityManager.persist(post); }
  • 7. @vlad_mihalcea vladmihalcea.com SELECT tbl.next_val FROM hibernate_sequences tbl WHERE tbl.sequence_name = 'default' FOR UPDATE INSERT INTO hibernate_sequences (sequence_name, next_val) VALUES ('default', 1) UPDATE hibernate_sequences SET next_val = 2 WHERE next_val = 1 AND sequence_name = 'default' SELECT tbl.next_val FROM hibernate_sequences tbl WHERE tbl.sequence_name = 'default' FOR UPDATE UPDATE hibernate_sequences SET next_val = 3 WHERE next_val = 2 AND sequence_name = 'default' SELECT tbl.next_val FROM hibernate_sequences tbl WHERE tbl.sequence_name = 'default' FOR UPDATE UPDATE hibernate_sequences SET next_val = 4 WHERE next_val = 3 AND sequence_name = 'default' TABLE generator
  • 8. @vlad_mihalcea vladmihalcea.com INSERT INTO post (title, id) VALUES ('High-Performance Java Persistence, Part 1', 1) INSERT INTO post (title, id) VALUES ('High-Performance Java Persistence, Part 2', 2) INSERT INTO post (title, id) VALUES ('High-Performance Java Persistence, Part 3', 3) TABLE generator
  • 11. @vlad_mihalcea vladmihalcea.com AUTO Generator @Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; //Getters and setters omitted for brevity }
  • 12. @vlad_mihalcea vladmihalcea.com AUTO Generator • Prior to Hibernate 5 – native strategy. • Hibernate 5 – SequenceStyleGenerator strategy (falls back to TABLE)
  • 13. @vlad_mihalcea vladmihalcea.com AUTO Generator – Hibernate 5 and MySQL @Id @GeneratedValue(generator="native") @GenericGenerator(name = "native", strategy = "native") private Long id; INSERT INTO post (title) VALUES ('High-Performance Java Persistence, Part 1') INSERT INTO post (title) VALUES ('High-Performance Java Persistence, Part 2') INSERT INTO post (title) VALUES ('High-Performance Java Persistence, Part 3')
  • 14. @vlad_mihalcea vladmihalcea.com Identifier portability – annotation mapping @Entity(name = "Post") @Table(name = "post") public class Post { @Id @GeneratedValue(generator = "sequence", strategy = GenerationType.SEQUENCE) @SequenceGenerator(name = "sequence", allocationSize = 10) private Long id; private String title; //Getters and setters omitted for brevity }
  • 15. @vlad_mihalcea vladmihalcea.com Identifier portability – XML mapping <?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm_2_2.xsd" version="2.2"> <package>com.vladmihalcea.book.hpjp.hibernate.identifier.global</package> <entity class="Post" access="FIELD"> <attributes> <id name="id"> <generated-value strategy="IDENTITY"/> </id> </attributes> </entity> </entity-mappings
  • 16. @vlad_mihalcea vladmihalcea.com Custom types – IP address column • IP address stored in the Classless Inter-Domain Routing format • VARCHAR(18) • BIGINT column encoding – 4 bytes (e.g. 192.168.123.231) + 1 byte (e.g. 24) • PostgreSQL cidr or inet type (requires 7 bytes)
  • 17. @vlad_mihalcea vladmihalcea.com Custom types – PostgreSQL inet column Event matchingEvent = (Event) entityManager .createNativeQuery( "SELECT e.* " + "FROM event e " + "WHERE " + " e.ip && CAST(:network AS inet) = TRUE") .setParameter("network", "192.168.0.1/24") .getSingleResult(); assertEquals("192.168.0.123", matchingEvent.getIp().getAddress());
  • 18. @vlad_mihalcea vladmihalcea.com Custom types – PostgreSQL inet column @Entity(name = "Event") @Table(name = "event") @TypeDef(typeClass = IPv4Type.class, defaultForType = IPv4.class) public class Event { @Id @GeneratedValue private Long id; @Column(name = "ip", columnDefinition = "inet") private IPv4 ip; //Getters and setters omitted for brevity }
  • 19. @vlad_mihalcea vladmihalcea.com The hibernate-types project <dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types.version}</version> </dependency> <dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-5</artifactId> <version>${hibernate-types.version}</version> </dependency>
  • 20. @vlad_mihalcea vladmihalcea.com JSON for unstructured data @Entity(name = "Book") @Table(name = "book") @TypeDef(typeClass = JsonNodeBinaryType.class, defaultForType = JsonNode.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId private String isbn; @Column(columnDefinition = "jsonb") private JsonNode properties; //Getters and setters omitted for brevity }
  • 21. @vlad_mihalcea vladmihalcea.com JSON for unstructured data Book book = new Book(); book.setIsbn("978-9730228236"); book.setProperties( JacksonUtil.toJsonNode( "{" + " "title": "High-Performance Java Persistence"," + " "author": "Vlad Mihalcea"," + " "publisher": "Amazon"," + " "price": 44.99" + "}" ) );
  • 22. @vlad_mihalcea vladmihalcea.com JSON for unstructured data Book book = entityManager.unwrap(Session.class).bySimpleNaturalId(Book.class) .load("978-9730228236"); book.setProperties( JacksonUtil.toJsonNode( "{" + " "title": "High-Performance Java Persistence"," + " "author": "Vlad Mihalcea"," + " "publisher": "Amazon"," + " "price": 44.99," + " "url": "https://www.amzn.com/973022823X/"" + "}" ) );
  • 23. @vlad_mihalcea vladmihalcea.com Statement caching CC BY 2.0 - https://www.flickr.com/photos/southbeachcars/16065861514/
  • 25. @vlad_mihalcea vladmihalcea.com Oracle server-side statement caching • Soft parse • Hard parse • Bind peeking • Adaptive cursor sharing (since 11g)
  • 26. @vlad_mihalcea vladmihalcea.com SQL Server server-side statement caching • Execution plan cache • Parameter sniffing • Prepared statements should use the qualified object name SELECT * FROM etl.dbo.task WHERE status = ?
  • 27. @vlad_mihalcea vladmihalcea.com PostgreSQL server-side statement caching • Prior to 9.2 – execution plan cache • 9.2 – deferred optimization • The prepareThreshold connection property
  • 28. @vlad_mihalcea vladmihalcea.com MySQL server-side statement caching • No execution plan cache • Since Connector/J 5.0.5 PreparedStatements are emulated • To activate server-side prepared statements: • useServerPrepStmts • cachePrepStmts
  • 29. @vlad_mihalcea vladmihalcea.com Client-side statement caching • Recycling Statement, PreparedStatement or CallableStatement objects • Reusing database cursors
  • 30. @vlad_mihalcea vladmihalcea.com Oracle implicit client-side statement caching • Connection-level cache • PreparedStatement and CallabledStatement only • Caches metadata only connectionProperties.put( "oracle.jdbc.implicitStatementCacheSize", Integer.toString(cacheSize)); dataSource.setConnectionProperties(connectionProperties);
  • 31. @vlad_mihalcea vladmihalcea.com Oracle implicit client-side statement caching • Once enabled, all statements are cached. • Can be disabled on a per statement basis if (statement.isPoolable()) { statement.setPoolable(false); }
  • 32. @vlad_mihalcea vladmihalcea.com Oracle explicit client-side statement caching • Caches both metadata, execution state and data OracleConnection oracleConnection = (OracleConnection) connection; oracleConnection.setExplicitCachingEnabled(true); oracleConnection.setStatementCacheSize(cacheSize);
  • 33. @vlad_mihalcea vladmihalcea.com Oracle explicit client-side statement caching PreparedStatement statement = oracleConnection. getStatementWithKey(SELECT_POST_KEY); if (statement == null) { statement = connection.prepareStatement(SELECT_POST); } try { statement.setInt(1, 10); statement.execute(); } finally { ((OraclePreparedStatement) statement).closeWithKey(SELECT_POST_KEY); }
  • 34. @vlad_mihalcea vladmihalcea.com SQL Server client-side statement caching • The SQL Server JDBC driver version 6.3 added support for statement caching. • Prepared Statement caching is disabled by default. connection.setStatementPoolingCacheSize(10); connection.setDisableStatementPooling(false);
  • 35. @vlad_mihalcea vladmihalcea.com PostgreSQL Server client-side statement caching • PostgreSQL JDBC Driver 9.4-1202 makes client-side statement connection-bound instead of statement-bound • Configurable: • preparedStatementCacheQueries (default is 256) • preparedStatementCacheSizeMiB (default is 5MB) • Statement.setPoolable(false) is not supported
  • 36. @vlad_mihalcea vladmihalcea.com MySQL Server client-side statement caching • Configurable: • cachePrepStmts (default is false) Required for server-side statement caching as well • prepStmtCacheSize (default is 25) • prepStmtCacheSqlLimit (default is 256) • Statement.setPoolable(false) works for server-side statements only
  • 37. @vlad_mihalcea vladmihalcea.com Statement caching gain (one minute interval) Database System No Caching Throughput (SPM) Caching Throughput (SPM) Percentage Gain DB_A 419 833 507 286 20.83% DB_B 194 837 303 100 55.56% DB_C 116 708 166 443 42.61% DB_D 15 522 15 550 0.18%
  • 38. @vlad_mihalcea vladmihalcea.com Queries CC BY 2.0 - https://www.flickr.com/photos/justinbaeder/5317820857/
  • 39. @vlad_mihalcea vladmihalcea.com Criteria API literal handling CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Book> cq = cb.createQuery(Book.class); Root<Book> root = cq.from(Book.class); cq.select(root); cq.where(cb.equal(root.get("name"), "High-Performance Java Persistence")); Book book = entityManager.createQuery(cq).getSingleResult(); SELECT b.id AS id1_0_, b.isbn AS isbn2_0_, b.name AS name3_0_ FROM book b WHERE b.name = ?
  • 40. @vlad_mihalcea vladmihalcea.com Criteria API literal handling CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Book> cq = cb.createQuery(Book.class); Root<Book> root = cq.from(Book.class); cq.select(root); cq.where(cb.equal(root.get("isbn"), 978_9730228236L)); Book book = entityManager.createQuery(cq).getSingleResult(); SELECT b.id AS id1_0_, b.isbn AS isbn2_0_, b.name AS name3_0_ FROM book b WHERE b.isbn = 9789730228236
  • 42. @vlad_mihalcea vladmihalcea.com Criteria API literal handling <property name="hibernate.criteria.literal_handling_mode" value="bind" /> <property name="hibernate.criteria.literal_handling_mode" value="inline" /> <property name="hibernate.criteria.literal_handling_mode" value="auto" />
  • 43. @vlad_mihalcea vladmihalcea.com IN query padding optimization List<Post> getPostByIds(EntityManager entityManager, Integer... ids) { return entityManager.createQuery( "select p " + "from Post p " + "where p.id in :ids", Post.class) .setParameter("ids", Arrays.asList(ids)) .getResultList(); }
  • 44. @vlad_mihalcea vladmihalcea.com IN query padding optimization SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?) SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?) assertEquals(3, getPostByIds(entityManager, 1, 2, 3).size()); assertEquals(4, getPostByIds(entityManager, 1, 2, 3, 4).size());
  • 45. @vlad_mihalcea vladmihalcea.com IN query padding optimization SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?, ?) SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?, ?, ?) assertEquals(5, getPostByIds(entityManager, 1, 2, 3, 4, 5).size()); assertEquals(6, getPostByIds(entityManager, 1, 2, 3, 4, 5, 6).size());
  • 47. @vlad_mihalcea vladmihalcea.com IN query padding optimization <property name="hibernate.query.in_clause_parameter_padding" value="true" /> SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?) -- Params: (1, 2, 3, 3) SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?) -- Params: (1, 2, 3, 4) assertEquals(3, getPostByIds(entityManager, 1, 2, 3).size()); assertEquals(4, getPostByIds(entityManager, 1, 2, 3, 4).size());
  • 48. @vlad_mihalcea vladmihalcea.com IN query padding optimization assertEquals(5, getPostByIds(entityManager, 1, 2, 3, 4, 5).size()); SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?, ?, ?, ?, ?) -- Params: (1, 2, 3, 4, 5, 5, 5, 5) assertEquals(6, getPostByIds(entityManager, 1, 2, 3, 4, 5, 6).size()); SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id IN (?, ?, ?, ?, ?, ?, ?, ?) -- Params: (1, 2, 3, 4, 5, 6, 6, 6)
  • 49. @vlad_mihalcea vladmihalcea.com Query plan cache • Entity queries (JPQL, Criteria API) need to be compiled to SQL • Configurable: • hibernate.query.plan_cache_max_size (2048) • hibernate.query.plan_parameter_metadata_max_size (128)
  • 51. @vlad_mihalcea vladmihalcea.com Native SQL query plan cache improvement
  • 52. @vlad_mihalcea vladmihalcea.com JPQL DISTINCT scalar query List<Integer> publicationYears = entityManager.createQuery( "select distinct year(p.createdOn) " + "from Post p " + "order by year(p.createdOn)", Integer.class) .getResultList(); SELECT DISTINCT extract(YEAR FROM p.created_on) AS col_0_0_ FROM post p ORDER BY (YEAR FROM p.created_on)
  • 53. @vlad_mihalcea vladmihalcea.com JPQL DISTINCT entity query List<Post> posts = entityManager.createQuery( "select distinct p " + "from Post p " + "left join fetch p.comments " + "where p.title = :title", Post.class) .setParameter("title", "High-Performance Java Persistence") .getResultList(); SELECT DISTINCT p.id AS id1_0_0_, pc.id AS id1_1_1_, p.title AS title2_0_0_, pc.review AS review2_1_1_, pc.post_id AS post_id3_1_0__, pc.id AS id1_1_0__ FROM post p LEFT OUTER JOIN post_comment pc ON p.id=pc.post_id WHERE p.title = ?
  • 55. @vlad_mihalcea vladmihalcea.com JPQL DISTINCT entity query List<Post> posts = entityManager.createQuery( "select distinct p " + "from Post p " + "left join fetch p.comments " + "where p.title = :title", Post.class) .setParameter("title", "High-Performance Java Persistence") .setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false) .getResultList(); SELECT p.id AS id1_0_0_, pc.id AS id1_1_1_, p.title AS title2_0_0_, pc.review AS review2_1_1_, pc.post_id AS post_id3_1_0__, pc.id AS id1_1_0__ FROM post p LEFT OUTER JOIN post_comment pc ON p.id=pc.post_id WHERE p.title = ?
  • 57. @vlad_mihalcea vladmihalcea.com Fetching CC BY 2.0 - https://www.flickr.com/photos/bala_/3544603505/
  • 60. @vlad_mihalcea vladmihalcea.com Persistence Context size //session-level configuration Session session = entityManager.unwrap(Session.class); session.setDefaultReadOnly(true); //query-level configuration List<Post> posts = entityManager.createQuery( "select p from Post p", Post.class) .setHint(QueryHints.HINT_READONLY, true) .getResultList();
  • 61. @vlad_mihalcea vladmihalcea.com @Transactional(readOnly = true) public List<Post> findAllByTitle(String title) { List<Post> posts = postDAO.findByTitle(title); org.hibernate.engine.spi.PersistenceContext persistenceContext = getHibernatePersistenceContext(); for(Post post : posts) { assertTrue(entityManager.contains(post)); EntityEntry entityEntry = persistenceContext.getEntry(post); assertNull(entityEntry.getLoadedState()); } return posts; } Spring 5.1 read-only optimization
  • 62. @vlad_mihalcea vladmihalcea.com Fetching – Pagination • JPA / Hibernate API works for both entity and native SQL queries List<PostCommentSummary> summaries = entityManager.createQuery( "select new PostCommentSummary( " + " p.id, p.title, c.review ) " + "from PostComment c " + "join c.post p") .setFirstResult(pageStart) .setMaxResults(pageSize) .getResultList();
  • 63. @vlad_mihalcea vladmihalcea.com Fetching – 100k vs 100 rows Fetch all Fetch limit 0 500 1000 1500 2000 2500 3000 3500 4000 4500 5000 Time(ms) DB_A DB_B DB_C DB_D
  • 65. @vlad_mihalcea vladmihalcea.com JPA 2.2 Streaming default Stream getResultStream() { return getResultList().stream(); } final ScrollableResultsImplementor scrollableResults = scroll( ScrollMode.FORWARD_ONLY );
  • 66. @vlad_mihalcea vladmihalcea.com JDBC-level streaming support • You still need to consider the Statement.setFetchSize. • On MySQL, you need to set it to Integer.MIN_VALUE. • On PostgreSQL, you need to set it to a positive integer value. • What about the Execution Plan?
  • 67. @vlad_mihalcea vladmihalcea.com JPA 2.2 Streaming – Execution Plans List<String> executionPlanLines = doInJPA(entityManager -> { try(Stream<String> postStream = entityManager .createNativeQuery( "EXPLAIN ANALYZE " + "SELECT p " + "FROM post p " + "ORDER BY p.created_on DESC") .setHint(QueryHints.HINT_FETCH_SIZE, 50) .getResultStream() ) { return postStream.limit(50).collect(Collectors.toList()); } }); CREATE INDEX idx_post_created_on ON post (created_on DESC)
  • 68. @vlad_mihalcea vladmihalcea.com JPA 2.2 Streaming – Execution Plans LOGGER.info( "Execution plan: {}", executionPlanLines .stream() .collect(Collectors.joining( "n" )) ); Execution plan: Sort (cost=65.53..66.83 rows=518 width=564) (actual time=2.876..3.399 rows=5000 loops=1) Sort Key: created_on DESC Sort Method: quicksort Memory: 896kB -> Seq Scan on post p (cost=0.00..42.18 rows=518 width=564) (actual time=0.050..1.371 rows=5000 loops=1) Planning time: 1.586 ms Execution time: 4.061 ms
  • 69. @vlad_mihalcea vladmihalcea.com JPA 2.2 Streaming – Execution Plans List<String> executionPlanLines = doInJPA(entityManager -> { return entityManager .createNativeQuery( "EXPLAIN ANALYZE " + "SELECT p " + "FROM post p " + "ORDER BY p.created_on DESC") .setMaxResults(50) .getResultList(); }); LOGGER.info("Execution plan: {}", executionPlanLines .stream() .collect(Collectors.joining("n")) );
  • 70. @vlad_mihalcea vladmihalcea.com JPA 2.2 Streaming – Execution Plans Execution plan: Limit (cost=0.28..25.35 rows=50 width=564) (actual time=0.038..0.051 rows=50 loops=1) -> Index Scan using idx_post_created_on on post p (cost=0.28..260.04 rows=518 width=564) (actual time=0.037..0.049 rows=50 loops=1) Planning time: 1.511 ms Execution time: 0.148 ms
  • 71. @vlad_mihalcea vladmihalcea.com Fetching – Open Session in View Anti-Pattern
  • 72. @vlad_mihalcea vladmihalcea.com Fetching – Open Session in View Anti-Pattern
  • 73. @vlad_mihalcea vladmihalcea.com Fetching – Temporary Session Anti-Pattern • “Band aid” for LazyInitializationException • One temporary Session/Connection for every lazily fetched association <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
  • 74. @vlad_mihalcea vladmihalcea.com Batching CC BY-SA 2.0 - https://www.flickr.com/photos/dozodomo/6975654335/
  • 78. @vlad_mihalcea vladmihalcea.com Batch processing – flush-clear-commit try { entityManager.getTransaction().begin(); for ( int i = 0; i < entityCount; ++i ) { if ( i > 0 && i % batchSize == 0 ) { flush( entityManager ); } entityManager.persist( new Post( String.format( "Post %d", i + 1 ) ) ); } entityManager.getTransaction().commit(); } catch (RuntimeException e) { if ( entityManager.getTransaction().isActive()) { entityManager.getTransaction().rollback(); } throw e; } finally { entityManager.close(); }
  • 79. @vlad_mihalcea vladmihalcea.com private void flush(EntityManager entityManager) { entityManager.flush(); entityManager.clear(); entityManager.getTransaction().commit(); entityManager.getTransaction().begin(); } Batch processing – flush and commit
  • 80. @vlad_mihalcea vladmihalcea.com Hibernate batching – default behavior for (int i = 0; i < 3; i++) { entityManager.persist( new Post(String.format("Post no. %d", i + 1)) ); } INSERT INTO post (title, id) VALUES ('Post no. 1', 1) INSERT INTO post (title, id) VALUES ('Post no. 2', 2) INSERT INTO post (title, id) VALUES ('Post no. 3', 3)
  • 81. @vlad_mihalcea vladmihalcea.com Enable JDBC batching <property name="hibernate.jdbc.batch_size" value="5"/> Query: ["INSERT INTO post (title, id) VALUES (?, ?)"], Params: [('Post no. 1', 1), ('Post no. 2', 2), ('Post no. 3', 3)] entityManager.unwrap(Session.class).setJdbcBatchSize(10); for (long i = 0; i < 10; ++i) { Post post = new Post(); post.setTitle(String.format("Post nr %d", i)); entityManager.persist(post); }
  • 82. @vlad_mihalcea vladmihalcea.com PostgreSQL batch statements log_statement = 'all' LOG: execute S_2: insert into post (title, id) values ($1, $2) DETAIL: parameters: $1 = 'Post no. 1', $2 = '1' LOG: execute S_2: insert into post (title, id) values ($1, $2) DETAIL: parameters: $1 = 'Post no. 2', $2 = '2' LOG: execute S_2: insert into post (title, id) values ($1, $2) DETAIL: parameters: $1 = 'Post no. 3', $2 = '3'
  • 83. @vlad_mihalcea vladmihalcea.com PostgreSQL rewrite batch statements PGSimpleDataSource dataSource = (PGSimpleDataSource) dataSource(); dataSource.setReWriteBatchedInserts(true); LOG: execute <unnamed>: insert into post (title, id) values ($1, $2),($3, $4),($5, $6) DETAIL: parameters: $1 = 'Post no. 1', $2 = '1’, $3 = 'Post no. 2', $4 = '2’, $5 = 'Post no. 3', $6 = '3'
  • 84. @vlad_mihalcea vladmihalcea.com Default UPDATE - Post entity mapping @Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; private long likes; //Getters and setters omitted for brevity }
  • 85. @vlad_mihalcea vladmihalcea.com Default UPDATE - Post entity data Post post1 = new Post(); post1.setId(1L); post1.setTitle("High-Performance Java Persistence"); entityManager.persist(post1); Post post2 = new Post(); post2.setId(2L); post2.setTitle("Java Persistence with Hibernate"); entityManager.persist(post2);
  • 86. @vlad_mihalcea vladmihalcea.com Default UPDATE - statement batching Post post1 = entityManager.find(Post.class, 1L); post1.setTitle("High-Performance Java Persistence 2nd Edition"); Post post2 = entityManager.find(Post.class, 2L); post2.setLikes(12); entityManager.flush(); Query :[ "update post set likes=?, title=? where id=?" ], Params:[ (0, High-Performance Java Persistence 2nd Edition, 1), (12, Java Persistence with Hibernate, 2) ]
  • 87. @vlad_mihalcea vladmihalcea.com Default UPDATE disadvantages • Column size • Indexes • Replication • Undo and redo log • Triggers
  • 88. @vlad_mihalcea vladmihalcea.com Hibernate dynamic update @Entity(name = "Post") @Table(name = "post") @DynamicUpdate public class Post { @Id private Long id; private String title; private long likes; //Getters and setters omitted for brevity }
  • 89. @vlad_mihalcea vladmihalcea.com Hibernate dynamic update Query:["update post set title=? where id=?"], Params:[(High-Performance Java Persistence 2nd Edition, 1)] Query:["update post set likes=? where id=?"], Params:[(12, 2)] Post post1 = entityManager.find(Post.class, 1L); post1.setTitle("High-Performance Java Persistence 2nd Edition"); Post post2 = entityManager.find(Post.class, 2L); post2.setlikes(12); entityManager.flush();
  • 90. @vlad_mihalcea vladmihalcea.com Updating detached entities - Post and PostComment List<Post> posts = doInJPA(entityManager -> { return entityManager.createQuery( "select p " + "from Post p " + "join fetch p.comments ", Post.class) .getResultList(); }); for (Post post: posts) { post.setTitle("Vlad Mihalcea's " + post.getTitle()); for (PostComment comment: post.getComments()) { comment.setReview(comment.getReview() + " read!"); } }
  • 91. @vlad_mihalcea vladmihalcea.com • JPA merge • Hibernate update JPA merge and Hibernate update for (Post post: posts) { entityManager.merge(post); } Session session = entityManager.unwrap(Session.class); for (Post post: posts) { session.update(post); }
  • 92. @vlad_mihalcea vladmihalcea.com JPA merge – SELECT statements SELECT p.id AS id1_0_1_, p.title AS title2_0_1_, c.post_id AS post_id3_1_3_, c.id AS id1_1_3_, c.review AS review2_1_0_ FROM post p LEFT OUTER JOIN post_comment c ON p.id = c.post_id WHERE p.id = 1 SELECT p.id AS id1_0_1_, p.title AS title2_0_1_, c.post_id AS post_id3_1_3_, c.id AS id1_1_3_, c.review AS review2_1_0_ FROM post p LEFT OUTER JOIN post_comment c ON p.id = c.post_id WHERE p.id = 3 SELECT p.id AS id1_0_1_, p.title AS title2_0_1_, c.post_id AS post_id3_1_3_, c.id AS id1_1_3_, c.review AS review2_1_0_ FROM post p LEFT OUTER JOIN post_comment c ON p.id = c.post_id WHERE p.id = 5 …
  • 93. @vlad_mihalcea vladmihalcea.com JPA merge – UPDATE statements Query:[ "update post set title=? where id=?" ], Params:[ (Vlad Mihalcea's High-Performance Java Persistence, Part no. 0, 1), (Vlad Mihalcea's High-Performance Java Persistence, Part no. 1, 3), (Vlad Mihalcea's High-Performance Java Persistence, Part no. 2, 5) ] Query:[ "update post_comment set post_id=?, review=? where id=?" ], Params:[ (1, Excellent read!, 2), (3, Excellent read!, 4), (5, Excellent read!, 6) ]
  • 94. @vlad_mihalcea vladmihalcea.com Hibernate-specific update operation Query:[ "update post set title=? where id=?" ], Params:[ (Vlad Mihalcea's High-Performance Java Persistence, Part no. 0, 1), (Vlad Mihalcea's High-Performance Java Persistence, Part no. 1, 3), (Vlad Mihalcea's High-Performance Java Persistence, Part no. 2, 5) ] Query:[ "update post_comment set post_id=?, review=? where id=?" ], Params:[ (1, Excellent read!, 2), (3, Excellent read!, 4), (5, Excellent read!, 6) ]
  • 95. @vlad_mihalcea vladmihalcea.com Connections CC BY 2.0 - https://www.flickr.com/photos/justinbaeder/5317820857/
  • 97. @vlad_mihalcea vladmihalcea.com Immediate connection acquisition @PersistenceContext private EntityManager entityManager; @Transactional public void importForecasts(String dataFilePath) { Document forecastXmlDocument = readXmlDocument( dataFilePath ); List<Forecast> forecasts = parseForecasts(forecastXmlDocument); for(Forecast forecast : forecasts) { entityManager.persist( forecast ); } }
  • 98. @vlad_mihalcea vladmihalcea.com Resource-local delay connection acquisition <property name="hibernate.connection.provider_disables_autocommit" value="true" /> HikariConfig hikariConfig = new HikariConfig(); hikariConfig.setDataSourceClassName(dataSourceClassName); hikariConfig.setDataSourceProperties(dataSourceProperties); hikariConfig.setMinimumPoolSize(minPoolSize); hikariConfig.setMaximumPoolSize(maxPoolSize); hikariConfig.setAutoCommit(false); DataSource datasource = new HikariDataSource(hikariConfig);
  • 100. @vlad_mihalcea vladmihalcea.com Thank you • Twitter: @vlad_mihalcea • Blog: https://vladmihalcea.com • Courses: https://vladmihalcea.com/courses • Book: https://vladmihalcea.com/books/high-performance-java-persistence/