SlideShare a Scribd company logo
1 of 100
Download to read offline
@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/

More Related Content

What's hot

Synchronization.37
Synchronization.37Synchronization.37
Synchronization.37
myrajendra
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!
Jakub Kubrynski
 

What's hot (20)

Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introduction
 
Express js
Express jsExpress js
Express js
 
Modern JS with ES6
Modern JS with ES6Modern JS with ES6
Modern JS with ES6
 
Learn Java with Dr. Rifat Shahriyar
Learn Java with Dr. Rifat ShahriyarLearn Java with Dr. Rifat Shahriyar
Learn Java with Dr. Rifat Shahriyar
 
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Node js introduction
Node js introductionNode js introduction
Node js introduction
 
Introduction to jest
Introduction to jestIntroduction to jest
Introduction to jest
 
Spring Core
Spring CoreSpring Core
Spring Core
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentation
 
Java Introduction
Java IntroductionJava Introduction
Java Introduction
 
Synchronization.37
Synchronization.37Synchronization.37
Synchronization.37
 
Introduction to node.js
Introduction to node.jsIntroduction to node.js
Introduction to node.js
 
Expressjs
ExpressjsExpressjs
Expressjs
 
SOLID Design Principles
SOLID Design PrinciplesSOLID Design Principles
SOLID Design Principles
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!
 
Java basic
Java basicJava basic
Java basic
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
 
Nodejs vatsal shah
Nodejs vatsal shahNodejs vatsal shah
Nodejs vatsal shah
 

Similar to JPA and Hibernate Performance Tips

Spring 3: What's New
Spring 3: What's NewSpring 3: What's New
Spring 3: What's New
Ted Pennings
 

Similar to JPA and Hibernate Performance Tips (20)

Spring data requery
Spring data requerySpring data requery
Spring data requery
 
Spring 3: What's New
Spring 3: What's NewSpring 3: What's New
Spring 3: What's New
 
Sqlapi0.1
Sqlapi0.1Sqlapi0.1
Sqlapi0.1
 
Servlets 3.0 - Asynchronous, Extensibility, Ease-of-use @ JavaOne Brazil 2010
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
 
Jdbc[1]
Jdbc[1]Jdbc[1]
Jdbc[1]
 
JDBC programming
JDBC programmingJDBC programming
JDBC programming
 
Ppt on web development and this has all details
Ppt on web development and this has all detailsPpt on web development and this has all details
Ppt on web development and this has all details
 
Scala Frameworks for Web Application 2016
Scala Frameworks for Web Application 2016Scala Frameworks for Web Application 2016
Scala Frameworks for Web Application 2016
 
Scala and Spring
Scala and SpringScala and Spring
Scala and Spring
 
Spring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard WolffSpring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard Wolff
 
Integrating Wicket with Java EE 6
Integrating Wicket with Java EE 6Integrating Wicket with Java EE 6
Integrating Wicket with Java EE 6
 
Introduction to JDBC and database access in web applications
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
 
Multi Client Development with Spring
Multi Client Development with SpringMulti Client Development with Spring
Multi Client Development with Spring
 
Advance java session 5
Advance java session 5Advance java session 5
Advance java session 5
 
Backbone js
Backbone jsBackbone js
Backbone js
 
Spring Framework Petclinic sample application
Spring Framework Petclinic sample applicationSpring Framework Petclinic sample application
Spring Framework Petclinic sample application
 
JavaOne India 2011 - Servlets 3.0
JavaOne India 2011 - Servlets 3.0JavaOne India 2011 - Servlets 3.0
JavaOne India 2011 - Servlets 3.0
 
Integrating SAP the Java EE Way - JBoss One Day talk 2012
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
 
Dropwizard
DropwizardDropwizard
Dropwizard
 
Red Hat Agile integration Workshop Labs
Red Hat Agile integration Workshop LabsRed Hat Agile integration Workshop Labs
Red Hat Agile integration Workshop Labs
 

More from Vlad Mihalcea

More from Vlad Mihalcea (7)

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
Awesome SQL Tips and Tricks - Voxxed Days Cluj - 2019
 
Transactions and Concurrency Control Patterns - 2019
Transactions and Concurrency Control Patterns - 2019Transactions and Concurrency Control Patterns - 2019
Transactions and Concurrency Control Patterns - 2019
 
High-Performance Hibernate - JDK.io 2018
High-Performance Hibernate - JDK.io 2018High-Performance Hibernate - JDK.io 2018
High-Performance Hibernate - JDK.io 2018
 
Transactions and Concurrency Control Patterns
Transactions and Concurrency Control PatternsTransactions and Concurrency Control Patterns
Transactions and Concurrency Control Patterns
 
High Performance Hibernate JavaZone 2016
High Performance Hibernate JavaZone 2016High Performance Hibernate JavaZone 2016
High Performance Hibernate JavaZone 2016
 
High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016
 
High-Performance JDBC Voxxed Bucharest 2016
High-Performance JDBC Voxxed Bucharest 2016High-Performance JDBC Voxxed Bucharest 2016
High-Performance JDBC Voxxed Bucharest 2016
 

Recently uploaded

Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
masabamasaba
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 

Recently uploaded (20)

Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 

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/