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
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
}
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 = ?
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
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);
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
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
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)
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 = ?
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
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"/>
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)
]
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!");
}
}
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)
]