blob: aced15b1cd66a9344e0625ed9b7242ab848f44d3 [file] [log] [blame]
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +01001package eu.mulk.demos.blog.posts;
2
3import eu.mulk.demos.blog.authors.Author;
4import eu.mulk.demos.blog.comments.Comment;
5import eu.mulk.demos.blog.comments.SpamAssessmentService;
6import eu.mulk.demos.blog.comments.SpamStatus;
7import java.util.List;
8import java.util.Set;
Matthias Andreas Benkard10d53fb2021-03-02 16:34:58 +01009import java.util.stream.Collectors;
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +010010import javax.inject.Inject;
11import javax.persistence.EntityManager;
12import javax.persistence.PersistenceContext;
13import javax.transaction.Transactional;
14import javax.ws.rs.GET;
15import javax.ws.rs.POST;
16import javax.ws.rs.Path;
17import javax.ws.rs.Produces;
18import javax.ws.rs.core.MediaType;
19import org.hibernate.annotations.LazyToOne;
20import org.hibernate.annotations.LazyToOneOption;
21import org.jboss.logging.Logger;
22
23@Path("/posts")
24@Produces(MediaType.APPLICATION_JSON)
25public class PostResource {
26
27 static final Logger log = Logger.getLogger(PostResource.class);
28
29 @Inject
30 SpamAssessmentService spamAssessmentService;
31
32 @PersistenceContext
33 EntityManager entityManager;
34
35 /**
36 * Fetches all posts with no extra information.
37 *
38 * Simple. No surprises.
39 */
40 @GET
41 @Transactional
42 public List<Post> getAll() {
43 clearLog();
44
45 return Post.findAll().list();
46 }
47
48 /**
49 * Fetches all posts with comments included.
50 *
51 * Lazy fetching. Simple. No surprises.
52 */
53 @GET
54 @Transactional
55 @Path("/q1")
56 public List<Post> getAllWithComments() {
57 clearLog();
58
59 return Post.find(
60 """
61 SELECT p FROM Post p
62 LEFT JOIN FETCH p.comments
63 """)
64 .list();
65 }
66
67 /**
68 * Fetches all posts with author info included.
69 *
70 * <strong>Oops!</strong>
71 *
72 * {@link LazyToOne} with {@link LazyToOneOption#NO_PROXY} on {@link Author#basicCredentials} is
73 * needed to make this efficient.
74 */
75 @GET
76 @Transactional
77 @Path("/q2")
78 public List<Post> getAllWithAuthors() {
79 clearLog();
80
81 return Post.find(
82 """
83 SELECT p FROM Post p
84 LEFT JOIN FETCH p.author
85 """)
86 .list();
87 }
88
89 /**
90 * Fetches all posts with comments and category info included.
91 *
92 * <strong>Oops!</strong> Crashes.
93 *
94 * Either use {@link Set} and get bad performance or do it as in {@link
95 * #getAllWithCommentsAndCategories2()}.
96 */
97 @GET
98 @Transactional
99 @Path("/q3")
100 public List<Post> getAllWithCommentsAndCategories() {
101 clearLog();
102
103 return Post.find(
104 """
105 SELECT p FROM Post p
106 LEFT JOIN FETCH p.comments
107 LEFT JOIN FETCH p.categories
108 """)
109 .list();
110 }
111
112 /**
113 * Fetches all posts with comments and category info included.
114 *
115 * 2 queries, but hey, no cartesian explosion! Works really well.
116 */
117 @GET
118 @Transactional
119 @Path("/q4")
120 public List<Post> getAllWithCommentsAndCategories2() {
121 clearLog();
122
123 List<Post> posts = Post.find(
124 """
125 SELECT p FROM Post p
126 LEFT JOIN FETCH p.comments
127 """)
128 .list();
129
130 posts = Post.find(
131 """
132 SELECT DISTINCT p FROM Post p
133 LEFT JOIN FETCH p.categories
134 WHERE p IN (?1)
135 """,
136 posts)
137 .list();
138
139 return posts;
140 }
141
142 /**
143 * Fetches all posts with comments and category info included.
144 *
145 * 2 queries, but hey, no cartesian explosion! Works really well.
146 */
147 @POST
148 @Transactional
149 @Path("/q5")
150 public void updateCommentStatus() {
151 clearLog();
152
153 List<Comment> comments = Comment.find(
154 """
155 SELECT c FROM Comment c
156 WHERE c.spamStatus = 'UNKNOWN'
157 """)
158 .list();
159
160 var assessments = spamAssessmentService.assess(comments);
161
162 for (var assessment : assessments.entrySet()) {
163 Comment comment = Comment.findById(assessment.getKey());
164 comment.spamStatus = assessment.getValue();
165 }
166 }
167
168 /**
169 * Resets the {@link Comment#spamStatus} to {@link SpamStatus#UNKNOWN} on all comments.
170 *
171 * This issues a lot of UPDATE statements, but semantically speaking it works.
172 */
173 @POST
174 @Transactional
175 @Path("/q6")
176 public void resetCommentStatus() {
177 clearLog();
178
179 List<Comment> comments = Comment.find(
180 """
181 SELECT c FROM Comment c
182 WHERE c.spamStatus <> 'UNKNOWN'
183 """)
184 .list();
185 comments.forEach(c -> c.spamStatus = SpamStatus.UNKNOWN);
186 }
187
188 /**
189 * Resets the {@link Comment#spamStatus} to {@link SpamStatus#UNKNOWN} on all comments.
190 *
191 * This is exactly equivalent to {@link #resetCommentStatus()} and just as efficient or
192 * inefficient.
193 */
194 @POST
195 @Transactional
196 @Path("/q6")
197 public void resetCommentStatus2() {
198 clearLog();
199
200 Comment.update("UPDATE Comment c SET c.spamStatus = 'UNKNOWN' WHERE c.spamStatus <> 'UNKNOWN'");
201 }
202
203 /**
204 * Resets the {@link Comment#spamStatus} to {@link SpamStatus#UNKNOWN} on all comments.
205 *
206 * This is exactly equivalent to {@link #resetCommentStatus()} and just as efficient or
207 * inefficient.
208 */
209 @POST
210 @Transactional
211 @Path("/q7")
212 public void resetCommentStatus3() {
213 clearLog();
214
215 entityManager.createNativeQuery(
216 "UPDATE comment SET spam_status = 'UNKNOWN' WHERE spam_status <> 'UNKNOWN'")
217 .executeUpdate();
218 }
219
220 /**
221 * Fetches all posts with all the relevant info for an overview included.
Matthias Andreas Benkard10d53fb2021-03-02 16:34:58 +0100222 *
223 * Bad version.
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100224 */
225 @GET
226 @Transactional
227 @Path("/q8")
228 @Produces(MediaType.APPLICATION_JSON)
Matthias Andreas Benkard10d53fb2021-03-02 16:34:58 +0100229 public List<PostSummary> overview1() {
230 clearLog();
231
232 return Post.find(
233 """
234 SELECT p FROM Post p
235 LEFT JOIN FETCH p.author
236 LEFT JOIN FETCH p.comments
237 """)
238 .<Post>stream()
239 .map((Post p) ->
240 new PostSummary(p.author.name, p.title, p.publicationDate, p.comments.size()))
241 .collect(Collectors.toList());
242 }
243
244 /**
245 * Fetches all posts with all the relevant info for an overview included.
246 *
247 * Good version.
248 */
249 @GET
250 @Transactional
251 @Path("/q9")
252 @Produces(MediaType.APPLICATION_JSON)
253 public List<PostSummary> overview2() {
Matthias Andreas Benkard36b0f042021-02-27 10:46:04 +0100254 clearLog();
255
256 return entityManager.createQuery(
257 """
258 SELECT NEW eu.mulk.demos.blog.posts.PostSummary(
259 p.author.name, p.title, p.publicationDate, size(p.comments))
260 FROM Post p
261 """,
262 PostSummary.class)
263 .getResultList();
264 }
265
266 private static void clearLog() {
267 log.infof("""
268
269 -----------------------------------------------------
270 -------------------- NEW REQUEST --------------------
271 -----------------------------------------------------
272 """);
273 }
274
275}