blob: 0775d42348a41e1cd64d8328e2d6ca5d4c1326f4 [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
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010016:customcss: SLIDES.css
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010017:source-highlighter: highlightjs
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010018:highlightjs-theme: grayscale.css
19:stem:
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +010020
21
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010022[%notitle]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010023== Setting the Stage
24
25image:UML.png[]
26
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010027[%notitle]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010028[.columns]
29=== Posts
30
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010031[source,java,data-lines=]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010032[.column]
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010033[.is-two-thirds]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010034----
35@Entity
36public class Post extends PanacheEntity {
37
38 public String title;
39 public Instant publicationDate;
40 public String body;
41
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010042 @ManyToOne(fetch = FetchType.LAZY) //<1>
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010043 public Author author;
44
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010045 @ManyToMany(fetch = FetchType.LAZY) //<2>
46 public Collection<Category> categories;
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010047
48 @OneToMany(fetch = FetchType.LAZY,
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010049 mappedBy = "post") //<3>
50 public Collection<Comment> comments;
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010051}
52----
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010053
54[.column]
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010055<1> lazy is good (can always `JOIN FETCH` later)
56<2> `@ManyToMany` always implies a join table
57<3> mapping without a join table, non-owning side
58
59
60[%notitle]
61=== Comments and Categories
62
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010063[source,java]
64----
65@Entity
66public class Comment extends PanacheEntity {
67
68 public String authorName;
69 public Instant publicationDate;
70 public String text;
71
72 @Enumerated(EnumType.STRING)
73 public SpamStatus spamStatus;
74
75 @ManyToOne(fetch = FetchType.LAZY)
76 public Post post;
77}
78----
79
80[source,java]
81----
82@Entity
83public class Category extends PanacheEntity {
84
85 public String name;
86}
87----
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010088
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +010089
90[%notitle]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +010091=== Authors, Login Credentials
92
93[source,java]
94----
95@Entity
96public class Author extends PanacheEntity {
97
98 public String name;
99
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100100 @OneToOne(fetch = FetchType.LAZY, //<1>
Matthias Andreas Benkard08492672021-02-27 13:57:48 +0100101 mappedBy = "author")
Matthias Andreas Benkard08492672021-02-27 13:57:48 +0100102 public BasicCredentials basicCredentials;
103}
104----
105
106[source,java]
107----
108@Entity
109public class BasicCredentials extends PanacheEntity {
110
111 @OneToOne(fetch = FetchType.LAZY)
112 @MapsId
113 public Author author;
114
115 public String username;
116 public String password;
117}
118----
119
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100120
121[%notitle]
122//[.columns]
Matthias Andreas Benkard08492672021-02-27 13:57:48 +0100123== Cartesian Blowup
124
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100125[source,java]
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100126----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100127@Transactional
128public List<Post> getAllWithCommentsAndCategories() {
129
130 return Post.find(
131 """
132 SELECT p FROM Post p
133 LEFT JOIN FETCH p.comments
134 LEFT JOIN FETCH p.categories
135 """)
136 .list();
137}
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100138----
139
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100140[%notitle]
141=== Bad SQL
142
143[source,sql]
144//[.column]
145//[.is-two-thirds]
146----
147select
148 post0_.id,
149 comments1_.id,
150 category3_.id,
151 post0_.author_id,
152 post0_.body,
153 post0_.publication_date,
154 post0_.title,
155 comments1_.author_name,
156 comments1_.post_id,
157 comments1_.publication_date,
158 comments1_.spam_status,
159 comments1_.text,
160 comments1_.post_id,
161 comments1_.id,
162 category3_.name,
163 categories2_.post_id,
164 categories2_.categories_id
165from
166 post post0_
167left outer join
168 comment comments1_
169 on post0_.id=comments1_.post_id
170left outer join
171 post_category categories2_
172 on post0_.id=categories2_.post_id
173left outer join
174 category category3_
175 on categories2_.categories_id=category3_.id;
176----
177
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100178[%notitle]
179=== Bad Results (1)
180
181--
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100182[source]
183----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100184[2021-02-27 10:32:58] 60 rows retrieved
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100185----
186
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100187stem:[60 = ubrace|"posts"|_10 *
188ubrace|("comments")/("post")|_3 * ubrace|"categories"|_2]
189
190Cartesian explosion! 🙀
191
192What gives?
193--
194
195[%notitle]
196=== Bad Results (2)
197
198[%header,format=tsv]
199[.supersmall]
200|===
201post_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 +01002024 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
2034 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
2044 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
2054 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
2064 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
2074 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
2085 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
2095 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
2105 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
2115 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
2125 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
2135 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
2146 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
2156 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
2166 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
2176 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
2186 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
2196 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
2207 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
2217 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
2227 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
2237 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
2247 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
2257 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
2268 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
2278 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
2288 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
2298 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
2308 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
2318 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
2329 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
2339 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
2349 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
2359 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
2369 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
2379 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
23810 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
23910 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
24010 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
24110 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
24210 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
24310 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
24411 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
24511 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
24611 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
24711 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
24811 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
24911 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
25012 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
25112 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
25212 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
25312 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
25412 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
25512 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
25613 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
25713 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
25813 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
25913 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
26013 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
26113 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 +0100262|===
263
264
265[%notitle]
266=== Fix: Sequential Fetch Queries
267
268[source,java]
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100269----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100270@Transactional
271public List<Post> getAllWithCommentsAndCategories2() {
272
273 List<Post> posts = Post.find(
274 """
275 SELECT p FROM Post p
276 LEFT JOIN FETCH p.comments
277 """)
278 .list();
279
280 posts = Post.find(
281 """
282 SELECT DISTINCT p FROM Post p
283 LEFT JOIN FETCH p.categories
284 WHERE p IN (?1)
285 """,
286 posts)
287 .list();
288
289 return posts;
290}
291----
292
293
294[%notitle]
295[.columns]
296=== Good SQL
297
298[source,sql]
299[.column]
300----
301select
302 post0_.id,
303 comments1_.id,
304 post0_.author_id,
305 post0_.body,
306 post0_.publication_date,
307 post0_.title,
308 comments1_.author_name,
309 comments1_.post_id,
310 comments1_.publication_date,
311 comments1_.spam_status,
312 comments1_.text,
313 comments1_.post_id,
314 comments1_.id
315from
316 post post0_
317left outer join
318 comment comments1_
319 on post0_.id=comments1_.post_id
320----
321
322[source,sql]
323[.column]
324----
325select
326 distinct post0_.id,
327 cat2_.id,
328 post0_.author_id,
329 post0_.body,
330 post0_.publication_date,
331 post0_.title,
332 cat2_.name,
333 cats1_.post_id,
334 cats1_.categories_id
335from
336 post post0_
337left outer join
338 post_category cats1_
339 on post0_.id=cats1_.post_id
340left outer join
341 category cat2_
342 on cats1_.categories_id=cat2_.id
343where
344 post0_.id in (
345 ? , ? , ? , ? , ? , ? , ? , ? ,
346 ? , ? , ? , ? , ? , ? , ? , ? ,
347 ? , ? , ? , ? , ? , ? , ? , ? ,
348 ? , ? , ? , ? , ? , ?
349 )
350----
351
352
Matthias Andreas Benkard11391172021-02-27 21:54:00 +0100353//[%notitle]
354//== Update without `#persist`
355//
356//[source,java]
357//----
358//@Transactional
359//public void resetCommentStatus() {
360//
361// List<Comment> comments = Comment.find(
362// """
363// SELECT c FROM Comment c
364// WHERE c.spamStatus <> 'UNKNOWN'
365// """)
366// .list();
367//
368// comments.forEach(c -> c.spamStatus = SpamStatus.UNKNOWN);
369//}
370//----
371//
372//
373//[%notitle]
374//=== Good SQL
375//
376//[source,sql]
377//----
378//select
379// comment0_.id,
380// comment0_.author_name,
381// comment0_.post_id,
382// comment0_.publication_date,
383// comment0_.spam_status,
384// comment0_.text
385//from
386// comment comment0_
387//where
388// comment0_.spam_status<>'UNKNOWN'
389//----
390//
391//[source,sql]
392//----
393//update
394// comment
395//set
396// author_name=?,
397// post_id=?,
398// publication_date=?,
399// spam_status=?,
400// text=?
401//where
402// id=?
403//
404//update
405//...
406//----
Matthias Andreas Benkard9d00b2e2021-02-27 21:05:29 +0100407
408
409[%notitle]
410== First-Level Caching for the Win
411
412[source,java]
413----
414@Transactional
415public void updateCommentStatus() {
416
417 List<Comment> comments = Comment.find( //<1>
418 """
419 SELECT c FROM Comment c
420 WHERE c.spamStatus = 'UNKNOWN'
421 """)
422 .list();
423
424 var assessments = spamAssessmentService.assess(comments); //<2>
425
426 for (var assessment : assessments.entrySet()) {
427 Comment comment = Comment.findById(assessment.getKey()); //<3>
428 comment.spamStatus = assessment.getValue();
429 }
430}
431----
432
433
434[%notitle]
435=== Good SQL
436
437[source,sql]
438----
439select
440 comment0_.id,
441 comment0_.author_name,
442 comment0_.post_id,
443 comment0_.publication_date,
444 comment0_.spam_status,
445 comment0_.text
446from
447 comment comment0_
448where
449 comment0_.spam_status='UNKNOWN'
450----
451
452[source,sql]
453----
454update
455 comment
456set
457 author_name=?,
458 post_id=?,
459 publication_date=?,
460 spam_status=?,
461 text=?
462where
463 id=?
464
465update
466...
467----
468
469[%notitle]
470== `@OneToOne` Surprises
471
472[source,java]
473----
474@Transactional
475public List<Post> getAllWithAuthors() {
476
477 return Post.find(
478 """
479 SELECT p FROM Post p
480 LEFT JOIN FETCH p.author
481 """)
482 .list();
483}
484----
485
486
487[%notitle]
488[.columns]
489=== Bad SQL
490
491[source,sql]
492[.column]
493----
494select
495 post0_.id,
496 author1_.id,
497 post0_.author_id,
498 post0_.body,
499 post0_.publication_date,
500 post0_.title,
501 author1_.name
502from
503 post post0_
504left outer join
505 author author1_
506 on post0_.author_id=author1_.id
507
508select
509 basiccrede0_.author_id,
510 basiccrede0_.password,
511 basiccrede0_.username
512from
513 basic_credentials basiccrede0_
514where
515 basiccrede0_.author_id=?
516----
517
518[source,sql]
519[.column]
520----
521select
522 basiccrede0_.author_id,
523 basiccrede0_.password,
524 basiccrede0_.username
525from
526 basic_credentials basiccrede0_
527where
528 basiccrede0_.author_id=?
529
530select
531 basiccrede0_.author_id,
532 basiccrede0_.password,
533 basiccrede0_.username
534from
535 basic_credentials basiccrede0_
536where
537 basiccrede0_.author_id=?
538----
539
540
541[%notitle]
542=== Another Look at the Author
543
544[source,java]
545----
546@Entity
547public class Author extends PanacheEntity {
548
549 public String name;
550
551 @OneToOne(fetch = FetchType.LAZY,
552 mappedBy = "author")
553 public BasicCredentials basicCredentials; //<1>
554}
555----
556
557[source,java]
558----
559@Entity
560public class BasicCredentials extends PanacheEntity {
561
562 @OneToOne(fetch = FetchType.LAZY)
563 @MapsId
564 public Author author; //<2>
565
566 public String username;
567 public String password;
568}
569----
570
571
572=== Why?
573
574Hibernate has no way of knowing whether `Author#basicCredentials` is `null`!
575
576
577[%notitle]
578[.columns]
579=== Fix: `@LazyToOne(NO_PROXY)`
580
581[source,java,data-line-numbers=8]
582[.column]
583[.is-two-thirds]
584----
585@Entity
586public class Author extends PanacheEntity {
587
588 public String name;
589
590 @OneToOne(fetch = FetchType.LAZY,
591 mappedBy = "author")
592 @LazyToOne(LazyToOneOption.NO_PROXY) //<1>
593 public BasicCredentials basicCredentials;
594}
595----
596
597[.column]
598<1> `NO_PROXY` + bytecode enhancement = profit
599
600
601[%notitle]
602=== Good SQL
603
604[source,sql]
605----
606select
607 post0_.id,
608 author1_.id,
609 post0_.author_id,
610 post0_.body,
611 post0_.publication_date,
612 post0_.title,
613 author1_.name
614from
615 post post0_
616left outer join
617 author author1_
618 on post0_.author_id=author1_.id
619----
620
621[%notitle]
622== Projection Queries
623
624[source,java]
625----
626@Transactional
627public List<PostSummary> overview() {
628
629 return entityManager.createQuery(
630 """
631 SELECT NEW eu.mulk.demos.blog.posts.PostSummary(
632 p.author.name, p.title, p.publicationDate, size(p.comments))
633 FROM Post p
634 """,
635 PostSummary.class)
636 .getResultList();
637}
638----
639
640
641[%notitle]
642=== Projection DTOs
643
644[source,java]
645----
646public final class PostSummary {
647
648 public final String authorName;
649 public final String title;
650 public final Instant publicationDate;
651 public final int commentCount;
652
653 public PostSummary( //<1>
654 String authorName,
655 String title,
656 Instant publicationDate,
657 int commentCount) {
658 this.authorName = authorName;
659 this.title = title;
660 this.publicationDate = publicationDate;
661 this.commentCount = commentCount;
662 }
663}
664----
665
666
667[%notitle]
668=== Good SQL
669
670[source,sql]
671----
672select
673 author1_.name,
674 post0_.title,
675 post0_.publication_date,
676 (select
677 count(comments2_.post_id)
678 from
679 comment comments2_
680 where
681 post0_.id = comments2_.post_id)
682from
683 post post0_
684cross join
685 author author1_
686where
687 post0_.author_id=author1_.id
688----
689
690
691[%notitle]
692== Conclusion
693
694- Lazy is good
695- Lazier is better
696- Lazy with bytecode enhancement is best
697- Sequential queries avoid cartesian blowup
698- The 1st-level cache is your friend
699- Use DTO projections everywhere all the time