Show last authors
1 Some DTO in complex systems can grow huge and you may start seeing performance issued with GeDA.
2
3 However, at this point we stand back to take a look at the bigger picture and try to remember what DTO's try to achieve:
4
5 * Provide only the necessary information
6 * Be lightweight to reduce traffic
7
8 So lets go through an example problem step by step and re-think how we do things.
9
10 == Problem ==
11
12 We have a blog system. Within it as a moderator we want to list all users and for each one we want to view all their post and then if we wish to we want to see replies to those posts.
13
14 So pretty simple domain model may look like this:
15
16 [[image:geda-complex-problem-domain.png||alt=""]]
17
18 == Domain model ==
19
20 (((
21 {{code language="java"}}
22
23 /*
24 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
25 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
26 *
27 * Copyright Denis Pavlov 2009
28 * Web: http://www.inspire-software.com
29 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
30 */
31
32 package com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl;
33
34 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
35 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
36
37 import java.util.ArrayList;
38 import java.util.Collection;
39
40 /**
41 * .
42 * <p/>
43 * User: denispavlov
44 * Date: Jul 1, 2012
45 * Time: 12:26:52 PM
46 */
47 public class UserImpl implements User {
48
49 private static Long dbCounter = 0L;
50
51 private Long userId;
52 private String username;
53 private Collection<UserEntry> entries = new ArrayList<UserEntry>();
54
55 public UserImpl() {
56 this.userId = ++dbCounter;
57 }
58
59 public Long getUserId() {
60 return userId;
61 }
62
63 public void setUserId(final Long userId) {
64 this.userId = userId;
65 }
66
67 public String getUsername() {
68 return username;
69 }
70
71 public void setUsername(final String username) {
72 this.username = username;
73 }
74
75 public Collection<UserEntry> getEntries() {
76 return entries;
77 }
78
79 public void setEntries(final Collection<UserEntry> entries) {
80 this.entries = entries;
81 }
82
83 public UserEntry createEntry() {
84 final UserEntry entry = new UserEntryImpl(this);
85 entries.add(entry);
86 return entry;
87 }
88 }
89 {{/code}}
90 )))
91
92 (((
93 {{code language="java"}}
94
95 /*
96 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
97 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
98 *
99 * Copyright Denis Pavlov 2009
100 * Web: http://www.inspire-software.com
101 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
102 */
103
104 package com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl;
105
106 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
107 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
108 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntryReply;
109
110 import java.util.ArrayList;
111 import java.util.Collection;
112
113 /**
114 * .
115 * <p/>
116 * User: denispavlov
117 * Date: Jul 1, 2012
118 * Time: 12:29:39 PM
119 */
120 public class UserEntryImpl implements UserEntry {
121
122 private static Long dbCounter = 0L;
123
124 private Long entryId;
125 private User user;
126 private String title;
127 private String body;
128 private Collection<UserEntryReply> replies = new ArrayList<UserEntryReply>();
129
130 public UserEntryImpl() {
131 this.entryId = ++dbCounter;
132 }
133
134 public UserEntryImpl(final User user) {
135 this();
136 this.user = user;
137 }
138
139 public Long getEntryId() {
140 return entryId;
141 }
142
143 public User getUser() {
144 return user;
145 }
146
147 public String getTitle() {
148 return title;
149 }
150
151 public void setTitle(final String title) {
152 this.title = title;
153 }
154
155 public String getBody() {
156 return body;
157 }
158
159 public void setBody(final String body) {
160 this.body = body;
161 }
162
163 public Collection<UserEntryReply> getReplies() {
164 return replies;
165 }
166
167 public void setReplies(final Collection<UserEntryReply> replies) {
168 this.replies = replies;
169 }
170
171 public UserEntryReply createReply(final User replier) {
172 final UserEntryReply reply = new UserEntryReplyImpl(this, replier);
173 replies.add(reply);
174 return reply;
175 }
176 }
177 {{/code}}
178 )))
179
180 (((
181 {{code language="java"}}
182
183 /*
184 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
185 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
186 *
187 * Copyright Denis Pavlov 2009
188 * Web: http://www.inspire-software.com
189 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
190 */
191
192 package com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl;
193
194 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
195 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
196 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntryReply;
197
198 /**
199 * .
200 * <p/>
201 * User: denispavlov
202 * Date: Jul 1, 2012
203 * Time: 12:37:53 PM
204 */
205 public class UserEntryReplyImpl implements UserEntryReply {
206
207 private UserEntry originalEntry;
208 private UserEntry replyEntry;
209
210 public UserEntryReplyImpl() {
211 }
212
213 public UserEntryReplyImpl(final UserEntry originalEntry, final User replier) {
214 this();
215 this.originalEntry = originalEntry;
216 this.replyEntry = replier.createEntry();
217 this.replyEntry.setTitle("RE: " + this.originalEntry.getTitle());
218 }
219
220 public UserEntry getOriginalEntry() {
221 return originalEntry;
222 }
223
224 public UserEntry getReplyEntry() {
225 return replyEntry;
226 }
227
228 }
229 {{/code}}
230 )))
231
232 == Some observations and decisions ==
233
234 Immediately we can see that depending on how we map the DTO object to our domain model we may end up loading quite a bit of information.
235
236 In fact it is even possible here to create mapping in such a way that we get infinite paths, like user.getEntries().next().getUser() ....
237
238 So what would be the best way to deal with this?
239
240 Lets outline our goals and what information we need to achieve those
241
242 |=Goals |=What we need
243 |We want to see list of users |list of user objects with username + id
244 |We want to see their posts |a user object and list of posts with body + title + id
245 |We want to be able to see replies to those post |an entry object and list of replies with body + title + id + userId
246
247 == DTO model ==
248
249 Therefore we would need:
250
251 * BaseUserDTO object that will contain basic info without user blog entries for list
252 * UserDTO<extends BaseUserDTO> that has blog entries as BaseUserEntryDTO collection for viewing account
253 * UserEntryDTO<extends BaseUserEntryDTO> that has replies collection which are BaseUserEntryDTO and also we need BaseUserDTO here, just in case we need to look up the user - this is for viewing blog entry
254 * With replies we pretty much can get away with reusing the UserEntryDTO and BaseUserEntryDTO
255
256 So here it is:
257
258 (((
259 {{code language="java"}}
260
261 /*
262 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
263 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
264 *
265 * Copyright Denis Pavlov 2009
266 * Web: http://www.inspire-software.com
267 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
268 */
269
270 package com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl;
271
272 import com.inspiresoftware.lib.dto.geda.annotations.Dto;
273 import com.inspiresoftware.lib.dto.geda.annotations.DtoField;
274 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserDTO;
275
276 /**
277 * Lightweight object to be used in lists.
278 * <p/>
279 * User: denispavlov
280 * Date: Jul 1, 2012
281 * Time: 1:08:11 PM
282 */
283 @Dto
284 public class BaseUserDTOImpl implements BaseUserDTO {
285
286 @DtoField(readOnly = true)
287 private Long userId;
288 @DtoField
289 private String username;
290
291 public Long getUserId() {
292 return userId;
293 }
294
295 public void setUserId(final Long userId) {
296 this.userId = userId;
297 }
298
299 public String getUsername() {
300 return username;
301 }
302
303 public void setUsername(final String username) {
304 this.username = username;
305 }
306
307 }
308 {{/code}}
309 )))
310
311 (((
312 {{code language="java"}}
313
314 /*
315 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
316 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
317 *
318 * Copyright Denis Pavlov 2009
319 * Web: http://www.inspire-software.com
320 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
321 */
322
323 package com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl;
324
325 import com.inspiresoftware.lib.dto.geda.annotations.Dto;
326 import com.inspiresoftware.lib.dto.geda.annotations.DtoCollection;
327 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
328 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
329 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.UserDTO;
330
331 import java.util.List;
332
333 /**
334 * Full user object + entries - prossible use is CRUD.
335 * Note that entries are BaseUserEntryDTO to prevent loading of
336 * user and replies info until we really need it.
337 * <p/>
338 * User: denispavlov
339 * Date: Jul 1, 2012
340 * Time: 1:29:34 PM
341 */
342 @Dto
343 public class UserDTOImpl extends BaseUserDTOImpl implements UserDTO {
344
345 @DtoCollection(dtoBeanKey = "BaseUserEntryDTO",
346 dtoToEntityMatcherKey = "BaseUserEntryDTOMatcher",
347 entityGenericType = UserEntry.class)
348 private List<BaseUserEntryDTO> entries;
349
350 public List<BaseUserEntryDTO> getEntries() {
351 return entries;
352 }
353
354 public void setEntries(final List<BaseUserEntryDTO> entries) {
355 this.entries = entries;
356 }
357 }
358 {{/code}}
359 )))
360
361 (((
362 {{code language="java"}}
363
364 /*
365 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
366 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
367 *
368 * Copyright Denis Pavlov 2009
369 * Web: http://www.inspire-software.com
370 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
371 */
372
373 package com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl;
374
375 import com.inspiresoftware.lib.dto.geda.annotations.Dto;
376 import com.inspiresoftware.lib.dto.geda.annotations.DtoField;
377 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
378 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
379
380 /**
381 * Lightweight object to generate list of entries.
382 * <p/>
383 * User: denispavlov
384 * Date: Jul 1, 2012
385 * Time: 1:26:18 PM
386 */
387 @Dto
388 public class BaseUserEntryDTOImpl implements BaseUserEntryDTO {
389
390 @DtoField(readOnly = true)
391 private Long entryId;
392 @DtoField
393 private String title;
394 @DtoField
395 private String body;
396
397 public Long getEntryId() {
398 return entryId;
399 }
400
401 public void setEntryId(final Long entryId) {
402 this.entryId = entryId;
403 }
404
405 public String getTitle() {
406 return title;
407 }
408
409 public void setTitle(final String title) {
410 this.title = title;
411 }
412
413 public String getBody() {
414 return body;
415 }
416
417 public void setBody(final String body) {
418 this.body = body;
419 }
420 }
421 {{/code}}
422 )))
423
424 (((
425 {{code language="java"}}
426
427 /*
428 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
429 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
430 *
431 * Copyright Denis Pavlov 2009
432 * Web: http://www.inspire-software.com
433 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
434 */
435
436 package com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl;
437
438 import com.inspiresoftware.lib.dto.geda.annotations.Dto;
439 import com.inspiresoftware.lib.dto.geda.annotations.DtoCollection;
440 import com.inspiresoftware.lib.dto.geda.annotations.DtoField;
441 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntryReply;
442 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserDTO;
443 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
444 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.UserEntryDTO;
445
446 import java.util.List;
447
448 /**
449 * Full loaded entry object so we may examine replies.
450 * This one contains BaseUserDTO (so we know who this entry belongs to if we
451 * need to look up used record) and replies as BaseUserEntryDTO as we do not want
452 * to load replies of replies until we need them.
453 * <p/>
454 * User: denispavlov
455 * Date: Jul 1, 2012
456 * Time: 1:39:15 PM
457 */
458 @Dto
459 public class UserEntryDTOImpl extends BaseUserEntryDTOImpl implements UserEntryDTO {
460
461 @DtoField(dtoBeanKey = "BaseUserDTO", readOnly = true)
462 private BaseUserDTO user;
463
464 @DtoCollection(dtoBeanKey = "BaseUserEntryReplyDTO",
465 dtoToEntityMatcherKey = "BaseUserEntryDTOMatcher",
466 entityGenericType = UserEntryReply.class, readOnly = true)
467 private List<BaseUserEntryDTO> replies;
468
469
470 public BaseUserDTO getUser() {
471 return user;
472 }
473
474 public void setUser(final BaseUserDTO user) {
475 this.user = user;
476 }
477
478 public List<BaseUserEntryDTO> getReplies() {
479 return replies;
480 }
481
482 public void setReplies(final List<BaseUserEntryDTO> replies) {
483 this.replies = replies;
484 }
485 }
486 {{/code}}
487 )))
488
489 (((
490 {{code language="java"}}
491
492 /*
493 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
494 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
495 *
496 * Copyright Denis Pavlov 2009
497 * Web: http://www.inspire-software.com
498 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
499 */
500
501 package com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl;
502
503 import com.inspiresoftware.lib.dto.geda.annotations.Dto;
504 import com.inspiresoftware.lib.dto.geda.annotations.DtoField;
505 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
506
507 /**
508 * The reply DTO object is in essence the same as base user entry,
509 * so we can reuse the interface but provide a "carry over" mapping
510 * for replyEntry property of domain entity.
511 * <p/>
512 * User: denispavlov
513 * Date: Jul 1, 2012
514 * Time: 1:35:10 PM
515 */
516 @Dto
517 public class BaseUserEntryReplyDTOImpl implements BaseUserEntryDTO {
518
519 @DtoField(value = "replyEntry.entryId", readOnly = true)
520 private Long entryId;
521 @DtoField(value = "replyEntry.title", readOnly = true)
522 private String title;
523 @DtoField(value = "replyEntry.body", readOnly = true)
524 private String body;
525
526 public Long getEntryId() {
527 return entryId;
528 }
529
530 public void setEntryId(final Long entryId) {
531 this.entryId = entryId;
532 }
533
534 public String getTitle() {
535 return title;
536 }
537
538 public void setTitle(final String title) {
539 this.title = title;
540 }
541
542 public String getBody() {
543 return body;
544 }
545
546 public void setBody(final String body) {
547 this.body = body;
548 }
549 }
550 {{/code}}
551 )))
552
553 We also need a matcher for collections:
554
555 (((
556 {{code language="java"}}
557
558 /*
559 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
560 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
561 *
562 * Copyright Denis Pavlov 2009
563 * Web: http://www.inspire-software.com
564 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
565 */
566
567 package com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl.match;
568
569 import com.inspiresoftware.lib.dto.geda.adapter.DtoToEntityMatcher;
570 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
571 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntryReply;
572 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
573
574 /**
575 * .
576 * <p/>
577 * User: denispavlov
578 * Date: Jul 1, 2012
579 * Time: 1:41:32 PM
580 */
581 public class BaseUserEntryDTOMatcher implements DtoToEntityMatcher<BaseUserEntryDTO, Object> {
582
583 public boolean match(final BaseUserEntryDTO baseUserEntryDTO, final Object o) {
584 if (o instanceof UserEntry) {
585 return baseUserEntryDTO.getEntryId().equals(((UserEntry) o).getEntryId());
586 } else if (o instanceof UserEntryReply) {
587 return baseUserEntryDTO.getEntryId().equals(((UserEntryReply) o).getReplyEntry().getEntryId());
588 }
589 throw new IllegalArgumentException("Invalid use");
590 }
591 }
592 {{/code}}
593 )))
594
595 == DAO and Service layer ==
596
597 Now it is very important to have Service layer API that will allow us to achieve our goals. Here is what a sample implementation may look like:
598
599 **DAO**
600
601 (((
602 {{code language="java"}}
603
604 /*
605 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
606 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
607 *
608 * Copyright Denis Pavlov 2009
609 * Web: http://www.inspire-software.com
610 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
611 */
612
613 package com.inspiresoftware.lib.dto.geda.examples.blog.service.impl;
614
615 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
616 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
617 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl.UserImpl;
618 import com.inspiresoftware.lib.dto.geda.examples.blog.service.UserDAO;
619
620 import java.util.ArrayList;
621 import java.util.HashMap;
622 import java.util.List;
623 import java.util.Map;
624
625 /**
626 * .
627 * <p/>
628 * User: denispavlov
629 * Date: Jul 1, 2012
630 * Time: 12:50:08 PM
631 */
632 public class UserDAOImpl implements UserDAO {
633
634 private final Map<Long, User> db = new HashMap<Long, User>();
635
636 public User create(final String username) {
637 final User user = new UserImpl();
638 user.setUsername(username);
639 db.put(user.getUserId(), user);
640 return user;
641 }
642
643 public List<User> list() {
644 return new ArrayList<User>(db.values());
645 }
646
647 public User findUser(final Long userId) {
648 return db.get(userId);
649 }
650
651 public UserEntry findEntry(final Long entryId) {
652 for (final User user : db.values()) {
653 for (final UserEntry entry : user.getEntries()) {
654 if (entry.getEntryId().equals(entryId)) {
655 return entry;
656 }
657 }
658 }
659 return null;
660 }
661 }
662 {{/code}}
663 )))
664
665 **Service**
666
667 (((
668 {{code language="java"}}
669
670 /*
671 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
672 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
673 *
674 * Copyright Denis Pavlov 2009
675 * Web: http://www.inspire-software.com
676 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
677 */
678
679 package com.inspiresoftware.lib.dto.geda.examples.blog.service.impl;
680
681 import com.inspiresoftware.lib.dto.geda.adapter.BeanFactory;
682 import com.inspiresoftware.lib.dto.geda.assembler.DTOAssembler;
683 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
684 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
685 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserDTO;
686 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
687 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.UserDTO;
688 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.UserEntryDTO;
689 import com.inspiresoftware.lib.dto.geda.examples.blog.service.UserDAO;
690 import com.inspiresoftware.lib.dto.geda.examples.blog.service.UserService;
691
692 import java.util.ArrayList;
693 import java.util.HashMap;
694 import java.util.List;
695 import java.util.Map;
696
697 /**
698 * .
699 * <p/>
700 * User: denispavlov
701 * Date: Jul 1, 2012
702 * Time: 2:01:13 PM
703 */
704 public class UserServiceImpl implements UserService {
705
706 private final UserDAO dao;
707 private final BeanFactory bf;
708
709 private final Map<String, Object> converters = new HashMap<String, Object>() {{
710
711 }};
712
713 public UserServiceImpl(final UserDAO dao, final BeanFactory bf) {
714 this.dao = dao;
715 this.bf = bf;
716 }
717
718 public List<BaseUserDTO> list() {
719 final List<User> users = dao.list();
720 final List<BaseUserDTO> dtos = new ArrayList<BaseUserDTO>();
721 DTOAssembler.newAssembler(bf.get("BaseUserDTO").getClass(), bf.get("User").getClass())
722 .assembleDtos(dtos, users, converters, bf);
723 return dtos;
724 }
725
726 public List<UserDTO> list(final String filter) {
727 final List<User> users = dao.list();
728 final List<UserDTO> dtos = new ArrayList<UserDTO>();
729
730 final DTOAssembler asm = DTOAssembler.newAssembler(bf.get(filter).getClass(), bf.get("User").getClass());
731
732 for (final User user : users) {
733 final UserDTO dto = (UserDTO) bf.get("UserDTO");
734 asm.assembleDto(dto, user, converters, bf);
735 dtos.add(dto);
736 }
737 return dtos;
738 }
739
740 public UserDTO findUser(final Long userId) {
741 final User user = dao.findUser(userId);
742 final UserDTO dto = (UserDTO) bf.get("UserDTO");
743 DTOAssembler.newAssembler(dto.getClass(), bf.get("User").getClass())
744 .assembleDto(dto, user, converters, bf);
745 return dto;
746 }
747
748 public UserEntryDTO findEntry(final Long entryId) {
749 final UserEntry entry = dao.findEntry(entryId);
750 final UserEntryDTO dto = (UserEntryDTO) bf.get("UserEntryDTO");
751 DTOAssembler.newAssembler(dto.getClass(), bf.get("UserEntry").getClass())
752 .assembleDto(dto, entry, converters, bf);
753 return dto;
754 }
755 }
756 {{/code}}
757 )))
758
759 **BeanFactory for DTO and Entities**
760
761 (((
762 {{code language="java"}}
763
764 /*
765 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
766 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
767 *
768 * Copyright Denis Pavlov 2009
769 * Web: http://www.inspire-software.com
770 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
771 */
772
773 package com.inspiresoftware.lib.dto.geda.examples.blog.service.impl;
774
775 import com.inspiresoftware.lib.dto.geda.adapter.BeanFactory;
776 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl.UserEntryImpl;
777 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl.UserEntryReplyImpl;
778 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.impl.UserImpl;
779 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.impl.*;
780
781 /**
782 * .
783 * <p/>
784 * User: denispavlov
785 * Date: Jul 1, 2012
786 * Time: 2:03:19 PM
787 */
788 public class BlogBeanFactory implements BeanFactory {
789
790 public Object get(final String entityBeanKey) {
791 if ("BaseUserDTO".equals(entityBeanKey)) {
792 return new BaseUserDTOImpl();
793 } else if ("BaseUserEntryDTO".equals(entityBeanKey)) {
794 return new BaseUserEntryDTOImpl();
795 } else if ("BaseUserEntryReplyDTO".equals(entityBeanKey)) {
796 return new BaseUserEntryReplyDTOImpl();
797 } else if ("UserDTO".equals(entityBeanKey)) {
798 return new UserDTOImpl();
799 } else if ("UserEntryDTO".equals(entityBeanKey)) {
800 return new UserEntryDTOImpl();
801 }
802
803 if ("User".equals(entityBeanKey)) {
804 return new UserImpl();
805 } else if ("UserEntry".equals(entityBeanKey)) {
806 return new UserEntryImpl();
807 } else if ("UserEntryReply".equals(entityBeanKey)) {
808 return new UserEntryReplyImpl();
809 }
810
811 throw new IllegalArgumentException("No entry for : " + entityBeanKey);
812 }
813 }
814 {{/code}}
815 )))
816
817 == Testing this API ==
818
819 The best way to see if all is well is by testing your API. So we are going to have a jUnit test that will simulate the user journey where we can achieve every objective that we have set in our goals:
820
821 (((
822 {{code language="java"}}
823
824 /*
825 * This code is distributed under The GNU Lesser General Public License (LGPLv3)
826 * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
827 *
828 * Copyright Denis Pavlov 2009
829 * Web: http://www.inspire-software.com
830 * SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
831 */
832
833 package com.inspiresoftware.lib.dto.geda.examples.blog;
834
835 import com.inspiresoftware.lib.dto.geda.adapter.BeanFactory;
836 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.User;
837 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntry;
838 import com.inspiresoftware.lib.dto.geda.examples.blog.domain.UserEntryReply;
839 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserDTO;
840 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.BaseUserEntryDTO;
841 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.UserDTO;
842 import com.inspiresoftware.lib.dto.geda.examples.blog.dto.UserEntryDTO;
843 import com.inspiresoftware.lib.dto.geda.examples.blog.service.UserDAO;
844 import com.inspiresoftware.lib.dto.geda.examples.blog.service.UserService;
845 import com.inspiresoftware.lib.dto.geda.examples.blog.service.impl.BlogBeanFactory;
846 import com.inspiresoftware.lib.dto.geda.examples.blog.service.impl.UserDAOImpl;
847 import com.inspiresoftware.lib.dto.geda.examples.blog.service.impl.UserServiceImpl;
848 import org.junit.Test;
849
850 import java.util.List;
851
852 import static org.junit.Assert.assertEquals;
853 import static org.junit.Assert.assertNotNull;
854
855 /**
856 * .
857 * <p/>
858 * User: denispavlov
859 * Date: Jul 1, 2012
860 * Time: 1:01:07 PM
861 */
862 public class BlogExampleRun {
863
864 private final UserDAO dao = new UserDAOImpl();
865 private final BeanFactory bf = new BlogBeanFactory();
866 private final UserService srv = new UserServiceImpl(dao, bf);
867
868 @Test
869 public void testBlogExample() {
870
871 this.setupUsers();
872
873 final List<BaseUserDTO> list = srv.list();
874 assertNotNull(list);
875 assertEquals(list.size(), 2);
876
877 // imitate user found Bob's account in list
878 final BaseUserDTO bob = selectBob(list);
879 assertNotNull(bob);
880
881 // user clicks the Bob's account entry
882 final UserDTO bobFull = srv.findUser(bob.getUserId());
883 assertNotNull(bobFull);
884
885 assertNotNull(bobFull.getEntries());
886 assertEquals(bobFull.getEntries().size(), 1);
887
888 // user selects first entry
889 final BaseUserEntryDTO bobEntry = bobFull.getEntries().get(0);
890 assertNotNull(bobEntry);
891
892 // user clicks bob's entry to see replies
893 final UserEntryDTO bobEntryFull = srv.findEntry(bobEntry.getEntryId());
894 assertNotNull(bobEntryFull);
895 assertEquals(bobEntryFull.getTitle(), "GeDA");
896 assertEquals(bobEntryFull.getBody(), "Hey all, This GeDA stuff really works!!!");
897 assertNotNull(bobEntryFull.getReplies());
898 assertEquals(bobEntryFull.getReplies().size(), 1);
899
900
901 // user finds the reply
902 final BaseUserEntryDTO replyToBob = bobEntryFull.getReplies().get(0);
903 assertNotNull(replyToBob);
904 assertEquals(replyToBob.getTitle(), "RE: GeDA");
905 assertEquals(replyToBob.getBody(), "Awesome!");
906
907 // user clicks the reply to see full details
908 final UserEntryDTO replyToBobFull = srv.findEntry(replyToBob.getEntryId());
909 assertNotNull(replyToBobFull);
910 assertNotNull(replyToBobFull.getUser());
911 assertEquals(replyToBobFull.getUser().getUsername(), "John");
912
913 // and so on...
914
915 }
916
917 private BaseUserDTO selectBob(final List<BaseUserDTO> list) {
918 for (final BaseUserDTO user : list) {
919 if ("Bob".equals(user.getUsername())) {
920 return user;
921 }
922 }
923 return null;
924 }
925
926 private void setupUsers() {
927
928 final User bob = dao.create("Bob");
929
930 final UserEntry entry = bob.createEntry();
931 entry.setTitle("GeDA");
932 entry.setBody("Hey all, This GeDA stuff really works!!!");
933
934 final User john = dao.create("John");
935 final UserEntryReply reply = entry.createReply(john);
936 reply.getReplyEntry().setBody("Awesome!");
937
938 }
939
940 @Test
941 public void testBlogExampleListFilter() {
942
943 this.setupUsers();
944
945 // Here we load UserDTO but we will be using BaseUserDTO as
946 // assembler class so we do not populate the object fully
947 final List<UserDTO> list = srv.list("BaseUserDTO");
948 assertNotNull(list);
949 assertEquals(list.size(), 2);
950
951 }
952 }
953 {{/code}}
954 )))
955
956 |(% style="color:#337ab7" %){{icon name="info-circle"/}} Note how elegant the solution ended up if we used interfaces for our DTO's and Entities. Since our API stays clean and neat. All the dirty work is done via BeanFactory and DTOAssembler.
957 However this is not always the case in real world. So if you absolutely must use the same classes you can use filtering feature as described in the testBlogExampleListFilter() whereby we use assembler for Base* class to generate full DTOs. Therefore you keep your object types (if you absolutely must) but do not load extra data.
958 This is however not recommended as you end up with blanks in object and you cannot determine if this is blank data or simply it has not been loaded.
959
960
961 |(% style="color:red" %){{icon name="times-circle"/}} Filtering feature has been implemented OOTB in Spring3 integration module, so you should use that
962
963 This example is in the SVN and will be available from v 2.0.2
GeDA - Generic DTO Assembler © Denys Pavlov 2009 - 2019
v.1.0.0