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