blob: e0652c80f80671257a252eb8f9ed9444d0cdaee9 [file] [log] [blame]
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +01001= Highly Efficient Enterprise Data Access
2Matthias Andreas Benkard
3// Meta
4:experimental:
5:data-uri:
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +01006:toc:
7:stem:
8:toclevels: 2
9:description: Slides for my Hibernate Patterns and Antipatterns talk
10:keywords: mulk
11// Settings
12:icons: font
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010013:revealjs_theme: simple
14:revealjs_width: 1280
15:revealjs_height: 800
16//:revealjs_width: 1920
17//:revealjs_height: 1200
18//:revealjs_customtheme: SLIDES.css
19:customcss: SLIDES.css
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010020//:source-highlighter: rouge
21:source-highlighter: highlightjs
22//:highlightjs-theme: https://cdn.jsdelivr.net/npm/reveal.js@3.9.2/lib/css/zenburn.css
23//:highlightjs-theme: https://cdn.jsdelivr.net/npm/reveal.js@3.9.2/lib/css/monokai.css
24//:highlightjs-theme: tomorrow.css
25:highlightjs-theme: grayscale.css
26:stem:
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +010027
28
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010029[%notitle]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010030== Setting the Stage
31
32image:UML.png[]
33
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010034[%notitle]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010035[.columns]
36=== Posts
37
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010038[source,java,data-lines=]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010039[.column]
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010040[.is-two-thirds]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010041----
42@Entity
43public class Post extends PanacheEntity {
44
45 public String title;
46 public Instant publicationDate;
47 public String body;
48
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010049 @ManyToOne(fetch = FetchType.LAZY) //<1>
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010050 public Author author;
51
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010052 @ManyToMany(fetch = FetchType.LAZY) //<2>
53 public Collection<Category> categories;
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010054
55 @OneToMany(fetch = FetchType.LAZY,
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010056 mappedBy = "post") //<3>
57 public Collection<Comment> comments;
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010058}
59----
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010060
61[.column]
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010062<1> lazy is good (can always `JOIN FETCH` later)
63<2> `@ManyToMany` always implies a join table
64<3> mapping without a join table, non-owning side
65
66
67[%notitle]
68=== Comments and Categories
69
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010070[source,java]
71----
72@Entity
73public class Comment extends PanacheEntity {
74
75 public String authorName;
76 public Instant publicationDate;
77 public String text;
78
79 @Enumerated(EnumType.STRING)
80 public SpamStatus spamStatus;
81
82 @ManyToOne(fetch = FetchType.LAZY)
83 public Post post;
84}
85----
86
87[source,java]
88----
89@Entity
90public class Category extends PanacheEntity {
91
92 public String name;
93}
94----
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010095
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010096
97[%notitle]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010098=== Authors, Login Credentials
99
100[source,java]
101----
102@Entity
103public class Author extends PanacheEntity {
104
105 public String name;
106
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100107 @OneToOne(fetch = FetchType.LAZY, //<1>
Matthias Andreas Benkard08492672021-02-27 13:57:48 +0100108 mappedBy = "author")
109 @LazyToOne(LazyToOneOption.NO_PROXY)
110 public BasicCredentials basicCredentials;
111}
112----
113
114[source,java]
115----
116@Entity
117public class BasicCredentials extends PanacheEntity {
118
119 @OneToOne(fetch = FetchType.LAZY)
120 @MapsId
121 public Author author;
122
123 public String username;
124 public String password;
125}
126----
127
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100128
129[%notitle]
130//[.columns]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +0100131== Cartesian Blowup
132
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100133[source,java]
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100134----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100135@Transactional
136public List<Post> getAllWithCommentsAndCategories() {
137
138 return Post.find(
139 """
140 SELECT p FROM Post p
141 LEFT JOIN FETCH p.comments
142 LEFT JOIN FETCH p.categories
143 """)
144 .list();
145}
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100146----
147
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100148[%notitle]
149=== Bad SQL
150
151[source,sql]
152//[.column]
153//[.is-two-thirds]
154----
155select
156 post0_.id,
157 comments1_.id,
158 category3_.id,
159 post0_.author_id,
160 post0_.body,
161 post0_.publication_date,
162 post0_.title,
163 comments1_.author_name,
164 comments1_.post_id,
165 comments1_.publication_date,
166 comments1_.spam_status,
167 comments1_.text,
168 comments1_.post_id,
169 comments1_.id,
170 category3_.name,
171 categories2_.post_id,
172 categories2_.categories_id
173from
174 post post0_
175left outer join
176 comment comments1_
177 on post0_.id=comments1_.post_id
178left outer join
179 post_category categories2_
180 on post0_.id=categories2_.post_id
181left outer join
182 category category3_
183 on categories2_.categories_id=category3_.id;
184----
185
186//[.column]
187//[.is-one-third]
188
189[%notitle]
190=== Bad Results (1)
191
192--
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100193[source]
194----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100195[2021-02-27 10:32:58] 60 rows retrieved
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100196----
197
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100198stem:[60 = ubrace|"posts"|_10 *
199ubrace|("comments")/("post")|_3 * ubrace|"categories"|_2]
200
201Cartesian explosion! 🙀
202
203What gives?
204--
205
206[%notitle]
207=== Bad Results (2)
208
209[%header,format=tsv]
210[.supersmall]
211|===
212post_id com_id cat_id aut_id body pubdate title author_name postid2 com_pubdate spamp com_text postid3 com_id cat_name postid4 cat_id
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +01002134 16 44 1 "" 2021-02-27 10:32:16.129627 Post #0 Anonymous Coward 4 2021-02-27 10:32:16.133969 UNKNOWN First post 4 16 Category #1 4 44
2144 15 44 1 "" 2021-02-27 10:32:16.129627 Post #0 Anonymous Coward 4 2021-02-27 10:32:16.133963 UNKNOWN First post 4 15 Category #1 4 44
2154 14 44 1 "" 2021-02-27 10:32:16.129627 Post #0 Anonymous Coward 4 2021-02-27 10:32:16.133870 UNKNOWN First post 4 14 Category #1 4 44
2164 16 45 1 "" 2021-02-27 10:32:16.129627 Post #0 Anonymous Coward 4 2021-02-27 10:32:16.133969 UNKNOWN First post 4 16 Category #0 4 45
2174 15 45 1 "" 2021-02-27 10:32:16.129627 Post #0 Anonymous Coward 4 2021-02-27 10:32:16.133963 UNKNOWN First post 4 15 Category #0 4 45
2184 14 45 1 "" 2021-02-27 10:32:16.129627 Post #0 Anonymous Coward 4 2021-02-27 10:32:16.133870 UNKNOWN First post 4 14 Category #0 4 45
2195 19 44 2 "" 2021-02-27 10:32:16.129674 Post #1 Anonymous Coward 5 2021-02-27 10:32:16.135200 UNKNOWN First post 5 19 Category #1 5 44
2205 18 44 2 "" 2021-02-27 10:32:16.129674 Post #1 Anonymous Coward 5 2021-02-27 10:32:16.135192 UNKNOWN First post 5 18 Category #1 5 44
2215 17 44 2 "" 2021-02-27 10:32:16.129674 Post #1 Anonymous Coward 5 2021-02-27 10:32:16.135205 UNKNOWN First post 5 17 Category #1 5 44
2225 19 45 2 "" 2021-02-27 10:32:16.129674 Post #1 Anonymous Coward 5 2021-02-27 10:32:16.135200 UNKNOWN First post 5 19 Category #0 5 45
2235 18 45 2 "" 2021-02-27 10:32:16.129674 Post #1 Anonymous Coward 5 2021-02-27 10:32:16.135192 UNKNOWN First post 5 18 Category #0 5 45
2245 17 45 2 "" 2021-02-27 10:32:16.129674 Post #1 Anonymous Coward 5 2021-02-27 10:32:16.135205 UNKNOWN First post 5 17 Category #0 5 45
2256 22 44 3 "" 2021-02-27 10:32:16.129700 Post #2 Anonymous Coward 6 2021-02-27 10:32:16.136043 UNKNOWN First post 6 22 Category #1 6 44
2266 21 44 3 "" 2021-02-27 10:32:16.129700 Post #2 Anonymous Coward 6 2021-02-27 10:32:16.136038 UNKNOWN First post 6 21 Category #1 6 44
2276 20 44 3 "" 2021-02-27 10:32:16.129700 Post #2 Anonymous Coward 6 2021-02-27 10:32:16.136031 UNKNOWN First post 6 20 Category #1 6 44
2286 22 45 3 "" 2021-02-27 10:32:16.129700 Post #2 Anonymous Coward 6 2021-02-27 10:32:16.136043 UNKNOWN First post 6 22 Category #0 6 45
2296 21 45 3 "" 2021-02-27 10:32:16.129700 Post #2 Anonymous Coward 6 2021-02-27 10:32:16.136038 UNKNOWN First post 6 21 Category #0 6 45
2306 20 45 3 "" 2021-02-27 10:32:16.129700 Post #2 Anonymous Coward 6 2021-02-27 10:32:16.136031 UNKNOWN First post 6 20 Category #0 6 45
2317 25 44 1 "" 2021-02-27 10:32:16.129724 Post #3 Anonymous Coward 7 2021-02-27 10:32:16.136904 UNKNOWN First post 7 25 Category #1 7 44
2327 24 44 1 "" 2021-02-27 10:32:16.129724 Post #3 Anonymous Coward 7 2021-02-27 10:32:16.136897 UNKNOWN First post 7 24 Category #1 7 44
2337 23 44 1 "" 2021-02-27 10:32:16.129724 Post #3 Anonymous Coward 7 2021-02-27 10:32:16.136909 UNKNOWN First post 7 23 Category #1 7 44
2347 25 45 1 "" 2021-02-27 10:32:16.129724 Post #3 Anonymous Coward 7 2021-02-27 10:32:16.136904 UNKNOWN First post 7 25 Category #0 7 45
2357 24 45 1 "" 2021-02-27 10:32:16.129724 Post #3 Anonymous Coward 7 2021-02-27 10:32:16.136897 UNKNOWN First post 7 24 Category #0 7 45
2367 23 45 1 "" 2021-02-27 10:32:16.129724 Post #3 Anonymous Coward 7 2021-02-27 10:32:16.136909 UNKNOWN First post 7 23 Category #0 7 45
2378 28 44 2 "" 2021-02-27 10:32:16.129746 Post #4 Anonymous Coward 8 2021-02-27 10:32:16.137743 UNKNOWN First post 8 28 Category #1 8 44
2388 27 44 2 "" 2021-02-27 10:32:16.129746 Post #4 Anonymous Coward 8 2021-02-27 10:32:16.137739 UNKNOWN First post 8 27 Category #1 8 44
2398 26 44 2 "" 2021-02-27 10:32:16.129746 Post #4 Anonymous Coward 8 2021-02-27 10:32:16.137731 UNKNOWN First post 8 26 Category #1 8 44
2408 28 45 2 "" 2021-02-27 10:32:16.129746 Post #4 Anonymous Coward 8 2021-02-27 10:32:16.137743 UNKNOWN First post 8 28 Category #0 8 45
2418 27 45 2 "" 2021-02-27 10:32:16.129746 Post #4 Anonymous Coward 8 2021-02-27 10:32:16.137739 UNKNOWN First post 8 27 Category #0 8 45
2428 26 45 2 "" 2021-02-27 10:32:16.129746 Post #4 Anonymous Coward 8 2021-02-27 10:32:16.137731 UNKNOWN First post 8 26 Category #0 8 45
2439 31 44 3 "" 2021-02-27 10:32:16.129767 Post #5 Anonymous Coward 9 2021-02-27 10:32:16.138536 UNKNOWN First post 9 31 Category #1 9 44
2449 30 44 3 "" 2021-02-27 10:32:16.129767 Post #5 Anonymous Coward 9 2021-02-27 10:32:16.138548 UNKNOWN First post 9 30 Category #1 9 44
2459 29 44 3 "" 2021-02-27 10:32:16.129767 Post #5 Anonymous Coward 9 2021-02-27 10:32:16.138543 UNKNOWN First post 9 29 Category #1 9 44
2469 31 45 3 "" 2021-02-27 10:32:16.129767 Post #5 Anonymous Coward 9 2021-02-27 10:32:16.138536 UNKNOWN First post 9 31 Category #0 9 45
2479 30 45 3 "" 2021-02-27 10:32:16.129767 Post #5 Anonymous Coward 9 2021-02-27 10:32:16.138548 UNKNOWN First post 9 30 Category #0 9 45
2489 29 45 3 "" 2021-02-27 10:32:16.129767 Post #5 Anonymous Coward 9 2021-02-27 10:32:16.138543 UNKNOWN First post 9 29 Category #0 9 45
24910 34 44 1 "" 2021-02-27 10:32:16.129789 Post #6 Anonymous Coward 10 2021-02-27 10:32:16.139349 UNKNOWN First post 10 34 Category #1 10 44
25010 33 44 1 "" 2021-02-27 10:32:16.129789 Post #6 Anonymous Coward 10 2021-02-27 10:32:16.139354 UNKNOWN First post 10 33 Category #1 10 44
25110 32 44 1 "" 2021-02-27 10:32:16.129789 Post #6 Anonymous Coward 10 2021-02-27 10:32:16.139337 UNKNOWN First post 10 32 Category #1 10 44
25210 34 45 1 "" 2021-02-27 10:32:16.129789 Post #6 Anonymous Coward 10 2021-02-27 10:32:16.139349 UNKNOWN First post 10 34 Category #0 10 45
25310 33 45 1 "" 2021-02-27 10:32:16.129789 Post #6 Anonymous Coward 10 2021-02-27 10:32:16.139354 UNKNOWN First post 10 33 Category #0 10 45
25410 32 45 1 "" 2021-02-27 10:32:16.129789 Post #6 Anonymous Coward 10 2021-02-27 10:32:16.139337 UNKNOWN First post 10 32 Category #0 10 45
25511 37 44 2 "" 2021-02-27 10:32:16.129809 Post #7 Anonymous Coward 11 2021-02-27 10:32:16.140032 UNKNOWN First post 11 37 Category #1 11 44
25611 36 44 2 "" 2021-02-27 10:32:16.129809 Post #7 Anonymous Coward 11 2021-02-27 10:32:16.140025 UNKNOWN First post 11 36 Category #1 11 44
25711 35 44 2 "" 2021-02-27 10:32:16.129809 Post #7 Anonymous Coward 11 2021-02-27 10:32:16.140037 UNKNOWN First post 11 35 Category #1 11 44
25811 37 45 2 "" 2021-02-27 10:32:16.129809 Post #7 Anonymous Coward 11 2021-02-27 10:32:16.140032 UNKNOWN First post 11 37 Category #0 11 45
25911 36 45 2 "" 2021-02-27 10:32:16.129809 Post #7 Anonymous Coward 11 2021-02-27 10:32:16.140025 UNKNOWN First post 11 36 Category #0 11 45
26011 35 45 2 "" 2021-02-27 10:32:16.129809 Post #7 Anonymous Coward 11 2021-02-27 10:32:16.140037 UNKNOWN First post 11 35 Category #0 11 45
26112 40 44 3 "" 2021-02-27 10:32:16.129839 Post #8 Anonymous Coward 12 2021-02-27 10:32:16.140766 UNKNOWN First post 12 40 Category #1 12 44
26212 39 44 3 "" 2021-02-27 10:32:16.129839 Post #8 Anonymous Coward 12 2021-02-27 10:32:16.140786 UNKNOWN First post 12 39 Category #1 12 44
26312 38 44 3 "" 2021-02-27 10:32:16.129839 Post #8 Anonymous Coward 12 2021-02-27 10:32:16.140779 UNKNOWN First post 12 38 Category #1 12 44
26412 40 45 3 "" 2021-02-27 10:32:16.129839 Post #8 Anonymous Coward 12 2021-02-27 10:32:16.140766 UNKNOWN First post 12 40 Category #0 12 45
26512 39 45 3 "" 2021-02-27 10:32:16.129839 Post #8 Anonymous Coward 12 2021-02-27 10:32:16.140786 UNKNOWN First post 12 39 Category #0 12 45
26612 38 45 3 "" 2021-02-27 10:32:16.129839 Post #8 Anonymous Coward 12 2021-02-27 10:32:16.140779 UNKNOWN First post 12 38 Category #0 12 45
26713 43 44 1 "" 2021-02-27 10:32:16.129860 Post #9 Anonymous Coward 13 2021-02-27 10:32:16.141651 UNKNOWN First post 13 43 Category #1 13 44
26813 42 44 1 "" 2021-02-27 10:32:16.129860 Post #9 Anonymous Coward 13 2021-02-27 10:32:16.141655 UNKNOWN First post 13 42 Category #1 13 44
26913 41 44 1 "" 2021-02-27 10:32:16.129860 Post #9 Anonymous Coward 13 2021-02-27 10:32:16.141642 UNKNOWN First post 13 41 Category #1 13 44
27013 43 45 1 "" 2021-02-27 10:32:16.129860 Post #9 Anonymous Coward 13 2021-02-27 10:32:16.141651 UNKNOWN First post 13 43 Category #0 13 45
27113 42 45 1 "" 2021-02-27 10:32:16.129860 Post #9 Anonymous Coward 13 2021-02-27 10:32:16.141655 UNKNOWN First post 13 42 Category #0 13 45
27213 41 45 1 "" 2021-02-27 10:32:16.129860 Post #9 Anonymous Coward 13 2021-02-27 10:32:16.141642 UNKNOWN First post 13 41 Category #0 13 45
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100273|===
274
275
276[%notitle]
277=== Fix: Sequential Fetch Queries
278
279[source,java]
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100280----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100281@Transactional
282public List<Post> getAllWithCommentsAndCategories2() {
283
284 List<Post> posts = Post.find(
285 """
286 SELECT p FROM Post p
287 LEFT JOIN FETCH p.comments
288 """)
289 .list();
290
291 posts = Post.find(
292 """
293 SELECT DISTINCT p FROM Post p
294 LEFT JOIN FETCH p.categories
295 WHERE p IN (?1)
296 """,
297 posts)
298 .list();
299
300 return posts;
301}
302----
303
304
305[%notitle]
306[.columns]
307=== Good SQL
308
309[source,sql]
310[.column]
311----
312select
313 post0_.id,
314 comments1_.id,
315 post0_.author_id,
316 post0_.body,
317 post0_.publication_date,
318 post0_.title,
319 comments1_.author_name,
320 comments1_.post_id,
321 comments1_.publication_date,
322 comments1_.spam_status,
323 comments1_.text,
324 comments1_.post_id,
325 comments1_.id
326from
327 post post0_
328left outer join
329 comment comments1_
330 on post0_.id=comments1_.post_id
331----
332
333[source,sql]
334[.column]
335----
336select
337 distinct post0_.id,
338 cat2_.id,
339 post0_.author_id,
340 post0_.body,
341 post0_.publication_date,
342 post0_.title,
343 cat2_.name,
344 cats1_.post_id,
345 cats1_.categories_id
346from
347 post post0_
348left outer join
349 post_category cats1_
350 on post0_.id=cats1_.post_id
351left outer join
352 category cat2_
353 on cats1_.categories_id=cat2_.id
354where
355 post0_.id in (
356 ? , ? , ? , ? , ? , ? , ? , ? ,
357 ? , ? , ? , ? , ? , ? , ? , ? ,
358 ? , ? , ? , ? , ? , ? , ? , ? ,
359 ? , ? , ? , ? , ? , ?
360 )
361----
362
363
364[%notitle]
365== Update without `#persist`
366
367[source,java]
368----
369@Transactional
370public void resetCommentStatus() {
371
372 List<Comment> comments = Comment.find(
373 """
374 SELECT c FROM Comment c
375 WHERE c.spamStatus <> 'UNKNOWN'
376 """)
377 .list();
378
379 comments.forEach(c -> c.spamStatus = SpamStatus.UNKNOWN);
380}
381----
382
383
384[%notitle]
385=== Good SQL
386
387[source,sql]
388----
389select
390 comment0_.id,
391 comment0_.author_name,
392 comment0_.post_id,
393 comment0_.publication_date,
394 comment0_.spam_status,
395 comment0_.text
396from
397 comment comment0_
398where
399 comment0_.spam_status<>'UNKNOWN'
400----
401
402[source,sql]
403----
404update
405 comment
406set
407 author_name=?,
408 post_id=?,
409 publication_date=?,
410 spam_status=?,
411 text=?
412where
413 id=?
414
415update
416...
417----
418
419
420[%notitle]
421== First-Level Caching for the Win
422
423[source,java]
424----
425@Transactional
426public void updateCommentStatus() {
427
428 List<Comment> comments = Comment.find( //<1>
429 """
430 SELECT c FROM Comment c
431 WHERE c.spamStatus = 'UNKNOWN'
432 """)
433 .list();
434
435 var assessments = spamAssessmentService.assess(comments); //<2>
436
437 for (var assessment : assessments.entrySet()) {
438 Comment comment = Comment.findById(assessment.getKey()); //<3>
439 comment.spamStatus = assessment.getValue();
440 }
441}
442----
443
444
445[%notitle]
446=== Good SQL
447
448[source,sql]
449----
450select
451 comment0_.id,
452 comment0_.author_name,
453 comment0_.post_id,
454 comment0_.publication_date,
455 comment0_.spam_status,
456 comment0_.text
457from
458 comment comment0_
459where
460 comment0_.spam_status='UNKNOWN'
461----
462
463[source,sql]
464----
465update
466 comment
467set
468 author_name=?,
469 post_id=?,
470 publication_date=?,
471 spam_status=?,
472 text=?
473where
474 id=?
475
476update
477...
478----
479
480[%notitle]
481== `@OneToOne` Surprises
482
483[source,java]
484----
485@Transactional
486public List<Post> getAllWithAuthors() {
487
488 return Post.find(
489 """
490 SELECT p FROM Post p
491 LEFT JOIN FETCH p.author
492 """)
493 .list();
494}
495----
496
497
498[%notitle]
499[.columns]
500=== Bad SQL
501
502[source,sql]
503[.column]
504----
505select
506 post0_.id,
507 author1_.id,
508 post0_.author_id,
509 post0_.body,
510 post0_.publication_date,
511 post0_.title,
512 author1_.name
513from
514 post post0_
515left outer join
516 author author1_
517 on post0_.author_id=author1_.id
518
519select
520 basiccrede0_.author_id,
521 basiccrede0_.password,
522 basiccrede0_.username
523from
524 basic_credentials basiccrede0_
525where
526 basiccrede0_.author_id=?
527----
528
529[source,sql]
530[.column]
531----
532select
533 basiccrede0_.author_id,
534 basiccrede0_.password,
535 basiccrede0_.username
536from
537 basic_credentials basiccrede0_
538where
539 basiccrede0_.author_id=?
540
541select
542 basiccrede0_.author_id,
543 basiccrede0_.password,
544 basiccrede0_.username
545from
546 basic_credentials basiccrede0_
547where
548 basiccrede0_.author_id=?
549----
550
551
552[%notitle]
553=== Another Look at the Author
554
555[source,java]
556----
557@Entity
558public class Author extends PanacheEntity {
559
560 public String name;
561
562 @OneToOne(fetch = FetchType.LAZY,
563 mappedBy = "author")
564 public BasicCredentials basicCredentials; //<1>
565}
566----
567
568[source,java]
569----
570@Entity
571public class BasicCredentials extends PanacheEntity {
572
573 @OneToOne(fetch = FetchType.LAZY)
574 @MapsId
575 public Author author; //<2>
576
577 public String username;
578 public String password;
579}
580----
581
582
583=== Why?
584
585Hibernate has no way of knowing whether `Author#basicCredentials` is `null`!
586
587
588[%notitle]
589[.columns]
590=== Fix: `@LazyToOne(NO_PROXY)`
591
592[source,java,data-line-numbers=8]
593[.column]
594[.is-two-thirds]
595----
596@Entity
597public class Author extends PanacheEntity {
598
599 public String name;
600
601 @OneToOne(fetch = FetchType.LAZY,
602 mappedBy = "author")
603 @LazyToOne(LazyToOneOption.NO_PROXY) //<1>
604 public BasicCredentials basicCredentials;
605}
606----
607
608[.column]
609<1> `NO_PROXY` + bytecode enhancement = profit
610
611
612[%notitle]
613=== Good SQL
614
615[source,sql]
616----
617select
618 post0_.id,
619 author1_.id,
620 post0_.author_id,
621 post0_.body,
622 post0_.publication_date,
623 post0_.title,
624 author1_.name
625from
626 post post0_
627left outer join
628 author author1_
629 on post0_.author_id=author1_.id
630----
631
632[%notitle]
633== Projection Queries
634
635[source,java]
636----
637@Transactional
638public List<PostSummary> overview() {
639
640 return entityManager.createQuery(
641 """
642 SELECT NEW eu.mulk.demos.blog.posts.PostSummary(
643 p.author.name, p.title, p.publicationDate, size(p.comments))
644 FROM Post p
645 """,
646 PostSummary.class)
647 .getResultList();
648}
649----
650
651
652[%notitle]
653=== Projection DTOs
654
655[source,java]
656----
657public final class PostSummary {
658
659 public final String authorName;
660 public final String title;
661 public final Instant publicationDate;
662 public final int commentCount;
663
664 public PostSummary( //<1>
665 String authorName,
666 String title,
667 Instant publicationDate,
668 int commentCount) {
669 this.authorName = authorName;
670 this.title = title;
671 this.publicationDate = publicationDate;
672 this.commentCount = commentCount;
673 }
674}
675----
676
677
678[%notitle]
679=== Good SQL
680
681[source,sql]
682----
683select
684 author1_.name,
685 post0_.title,
686 post0_.publication_date,
687 (select
688 count(comments2_.post_id)
689 from
690 comment comments2_
691 where
692 post0_.id = comments2_.post_id)
693from
694 post post0_
695cross join
696 author author1_
697where
698 post0_.author_id=author1_.id
699----
700
701
702[%notitle]
703== Conclusion
704
705- Lazy is good
706- Lazier is better
707- Lazy with bytecode enhancement is best
708- Sequential queries avoid cartesian blowup
709- The 1st-level cache is your friend
710- Use DTO projections everywhere all the time