Show last authors
1 {{toc/}}
2
3 = Overview =
4
5 == 1. Include GeDA in your code ==
6
7 You can either include latest [[maven dependency>>doc:GeDA - Generic DTO Assembler.Maven.WebHome]] of GeDA or simply download jar files manually (see [[home page>>doc:GeDA - Generic DTO Assembler.WebHome]] links) and add them to class path (with manual install you'll have to manage dependencies of byte code generation and logging on your own - just examine POM but in most cases you'll get away with javassist and slf4j - chances are you already have those).
8
9 There are three working modules:
10
11 **core** - is the actual bare generic DTO assembler (this module is mandatory)
12
13 **spring-integration** - is integration with Spring 3.x.x (this module is recommended if you are using Spring framework)
14
15 **osgi** - OSGi bundle with OSGi service to provide full support to GeDA core. A genuine OSGi bundle (not just another OSGi-fied library)
16
17 One examples module:
18
19 **examples** - here we try to provide end to end examples of how GeDA may be used (there is also lots of examples in the JUnit test of each module)
20
21 Three additional testing modules:
22
23 **core-ptest** - tests GeDA behaviour and performance using multithreading
24
25 **core-btest** - test for GeDA benchmark on performance (please help us keep this up to date and send [[feedback>>http://www.inspire-software.com/en/feedback.html]] if you think other libraries should be benchmarked)
26
27 **osgi-itest** - PAX exam for OSGi bundle
28
29 == 2. [option1] Annotate your DTO objects ==
30
31 GeDA offers a lot of options on this but there are some rules on how to use annotations provided.
32
33 **@Dto** - this annotation is a mandatory marker for all GeDA enabled DTO classes (this is not strictly speaking necessary for GeDA but it provides you with more control over what classes GeDA should "understand"). It also provides means for [[autobinding>>doc:GeDA - Generic DTO Assembler.Topics.DTO autobinding.WebHome]] when you have a one-to-one relationship between DTO and Entities.
34
35 **@DtoField** - simple annotation placed on a field to indicate that it will accept and/or provide basic java data (i.e. immutable basic java types such as primitives, wrappers, String etc) or DTO object if dtoBeanKey is specified.
36
37 **@DtoParent** - this is a complementing annotation to @DtoField to indicate a field that is actually a [[relation dependency>>doc:GeDA - Generic DTO Assembler.Topics.DTO parent.WebHome]] DTO. The easiest way to think of it is relational entity - in most cases you only want to update the linking ID and this annotation allows GeDA to understand that.
38
39 **@DtoCollection** - allows complex collections synchronisation for collection of object. Please refer to detailed [[guide>>doc:GeDA - Generic DTO Assembler.Topics.Mapping collections.WebHome]] on some peculiarities around this.
40
41 **@DtoMap** - allows complex map synchronisation in a similar way as @DtoCollection but also allowing you to specify keys.
42
43 **@DtoVirtualField** - this one allows you to have full control over your DTO and [[derive data for field>>doc:GeDA - Generic DTO Assembler.Topics.DtoVirtualField.WebHome]] on your DTO even if no such field exist on the Entities
44
45 |(% style="color:green" %){{icon name="check"/}} There are lots of examples of these mappings in the core's JUnit tests in the com.inspiresoftware.lib.dto.geda.assembler.examples package
46
47 == 2. [option2] Use DSL to create your mappings ==
48
49 Since v. 2.1.0 GeDA offers a [[DSL registry>>doc:GeDA - Generic DTO Assembler.Topics.GeDA DSL mappings.WebHome]] interface with DefaultDSLRegistry default implementation that allows you to do everything that you were able to do with annotations using DSL.
50
51 Use the **dto(?).forEntity(?)** to establish class to class mapping and then invoke **withField**, **withCollection** and **withMap** methods for fields that would otherwise had annotations on them.
52
53 == 3. Create Bean Factory (optional) ==
54
55 If you want to extract complex object graphs and let GeDA synchronize some of the objects instantiation you will need to implement [[BeanFactory>>doc:GeDA - Generic DTO Assembler.Topics.How to use BeanFactory.WebHome]]. It has only one method "Object #get(String key)" which allows GeDA to instantiate classes if you follow [[IoC>>doc:GeDA - Generic DTO Assembler.Topics.IoC with GeDA.WebHome]] principles. The key used would be key you specify for dtoBeanKey and entityBeanKeys in your annotations.
56
57 |(% style="color:#337ab7" %){{icon name="info-circle"/}} Architecture of GeDA follows SOLID principles. The responsibility of Assembler instance is to assemble - not to create instances - that is why GeDA library has BeanFactory API.
58
59 |(% style="color:red" %){{icon name="times-circle"/}} GeDA **core** provides only interfaces for BeanFactory and ExtensibleBeanFactory. The implementations are in //most cases project specific and influence performance a lot//. Hence it was architectural decision to leave those out. However there are default implementations in **spring-integration** (DTOFactoryImpl) and **osgi** (OSGiBundleDTOFactoryImpl) modules. You can use those for reference if you use GeDA **core** only.
60
61 == 4. Register value adapters (optional) ==
62
63 If your Entity-DTO relationship is complex and does not just involve copying values from one java bean to another - then you will probably need value converters. You would use them if:
64
65 * you have [[@DtoVirtualField>>doc:GeDA - Generic DTO Assembler.Topics.DtoVirtualField.WebHome]]
66 * you have non matching data types in your fields (say boolean and enum (YES, NO))
67
68 To implement a converter you need to implement ValueConverter interface that has two methods #convertToDto and #convertToEntity.
69
70 If you are using [[@DtoParent>>doc:GeDA - Generic DTO Assembler.Topics.DTO parent.WebHome]] annotation you will need to have EntityRetriever adapter that is pretty much just one method #retrieveByPrimaryKey which you specify in your annotations.
71
72 Lastly if you are using [[@DtoCollection and @DtoMap>>doc:GeDA - Generic DTO Assembler.Topics.Mapping collections.WebHome]] you will need to implement DtoToEntityMatcher with just one method #match to allow GeDA understand how to synchronize objects in collections and maps.
73
74 All of the above are Adapter instances that can be bundled (but do not have to) into a AdaptersRepository.
75
76 |(% style="color:green" %){{icon name="check"/}} It is advisable that you do use this repository to minimise number of instances of the Adapters in your system. When assembly time comes you can use repo.getAll() to get them.
77
78 |(% style="color:red" %){{icon name="times-circle"/}} It is very important to keep all adapters stateless (and hence thread-safe!)
79
80 == 5. Finally - some code. ==
81
82 Once you have completed the necessary steps for your particular case - basic snippet for assembly will look like this:
83
84 (((
85 {{code language="java"}}
86 ...
87
88 // get all adapters
89 final Map<String, Object> adapters = adaptersRepo.getAll();
90
91
92 // [option 1] get assembler instance from cache or create new using annotated dto class
93 final Assembler asm = DTOAssembler.newAssembler(dto.getClass(), entity.getClass());
94
95
96 // OR [option 2] do the same for the DSL approach that does not require dto class to be annotated
97 final Assembler asm = DTOAssembler.newAssembler(dto.getClass(), entity.getClass(), registry);
98 ...
99
100
101 asm.assembleDto(dto, entity, adapters, beanFactory);
102
103 ...
104
105 asm.assembleEntity(dto, entity, adapters, beanFactory);
106
107 ...
108 {{/code}}
109 )))
110
111 |(% style="color:green" %){{icon name="check"/}} GeDA caches assembler instances internally so you do not need to keep internal references. Call to DTOAssembler.new will check if appropriate instance already exists prior attempting to create a new one.
112
113 = Learn by Example =
114
115 |(% style="color:green" %){{icon name="check"/}} We strongly recommend reviewing all examples as some of them contain interesting combinations that may be useful.
116
117 |(% style="color:green" %){{icon name="check"/}} See this article about [[setting up GeDA project>>doc:GeDA - Generic DTO Assembler.Setting up GeDA project.WebHome]]
118
119 The overal API is quite simple as could be seen in step 5 above. The complexity comes from how you use the anootations or DSL and combines them together to achive maximum performance from your DTOs.
120
121 Unfortunately covering every single case is a thankless task as each case would be very specific. Below you will find some basic use cases for different mappings. However we **strongly encourage** checking out the source code as we have a huge section with many working examples showing how simple, complex, collections, recursive, generics mapping work (See last section [[More examples>>doc:]] to see where those are in source code).
122
123 == Simple field mapping ==
124
125 === Field Annotations ===
126
127 **Mapping TestDto2Class to an arbitrary entity**
128
129 (((
130 {{code language="java"}}
131 @Dto
132 public class TestDto2Class {
133
134 @DtoField("entityId")
135 private Long myLong; // maps dto.myLong to entity.entityId
136 @DtoField(value = "name", readOnly = true)
137 private String myString; // maps dto.myString to entity.name in read only mode
138 @DtoField("number")
139 private Double myDouble; // maps dto.myDouble to entity.number
140 @DtoField("subentity.decision", converter = "boolToEnum")
141 private Boolean myBoolean; // maps dto.myBoolean to entity.subentity.decision using converter
142
143 ...getters/setter go here...
144
145 }
146 {{/code}}
147 )))
148
149 === Field DSL ===
150
151 |(% style="color:green" %){{icon name="check"/}} This example uses the same DTO POJO's as annotations version but without the need for the annotations on classes
152
153 **Mapping TestDto2Class to an arbitrary entity**
154
155 (((
156 {{code language="java"}}
157 final ExtensibleBeanFactory bf = ...;
158
159 final Registry registry = new DefaultDSLRegistry(bf);
160
161 registry
162 // main mapping of TestDto2Class to a generic entity that has the below mentioned properties
163 // "TestDto2Class" - is a key in BeanFactory
164 .dto("TestDto2Class").forEntityGeneric()
165 // maps dto.myLong to entity.entityId in read only mode
166 .withField("myLong").forField("entityId").readOnly()
167 // maps dto.myString to entity.name
168 .and()
169 .withField("myString").forField("name")
170 // maps dto.myDouble to entity.number
171 .and()
172 .withField("myDouble").forField("number")
173 // maps dto.myBoolean to entity.subentity.decision using converter
174 .and()
175 .withField("myBoolean").forField("subentity.decision").converter("boolToEnum");
176
177 {{/code}}
178 )))
179
180 **Mapping TestDto2Class to an specific entity TestEntity2Class**
181
182 (((
183 {{code language="java"}}
184 final ExtensibleBeanFactory bf = ...;
185
186 final Registry registry = new DefaultDSLRegistry(bf);
187
188 registry
189 // main mapping of TestDto2Class to a generic entity that has the below mentioned properties
190 // "TestDto2Class" and "TestEntity2Class" - are keys in BeanFactory
191 .dto("TestDto2Class").forEntity("TestEntity2Class")
192 // maps dto.myLong to entity.entityId
193 .withField("myLong").forField("entityId")
194 ...
195
196 {{/code}}
197 )))
198
199 **Mapping TestDto2Class to an specific entity TestEntity2Class using class**
200
201 (((
202 {{code language="java"}}
203 final ExtensibleBeanFactory bf = ...;
204
205 final Registry registry = new DefaultDSLRegistry(bf);
206
207 registry
208 // main mapping of TestDto2Class to a generic entity that has the below mentioned properties
209 // TestDto2Class and TestEntity2Class - are actual classes (Entity can be an interface as well)
210 // alias("TestDto2Class") registers TestDto2Class.class with key "TestDto2Class" in BeanFactory
211 .dto(TestDto2Class.class).alias("TestDto2Class").forEntity(TestEntity2Class.class)
212 // maps dto.myLong to entity.entityId
213 .withField("myLong").forField("entityId")
214 // maps dto.myString to entity.name
215 ...
216
217 {{/code}}
218 )))
219
220 == Collection field mapping ==
221
222 === Collections Annotations ===
223
224 **Mapping TestDto7CollectionClass that holds collection of TestDto7CollectionSubInterface elements to an arbitrary entity**
225
226 (((
227 {{code language="java"}}
228 /** DTO that holds collection of other DTOs */
229 @Dto
230 @Ignore
231 public class TestDto7CollectionClass {
232
233 @DtoCollection(
234 // name of the collection field
235 value = "collection",
236 // Bean key for BeanFactory to create new entity beans during synchronisation
237 entityBeanKeys = "com.inspiresoftware.lib.dto.geda.assembler.TestEntity7CollectionSubClass",
238 // Bean key for BeanFactory to create new instances of DTO for collection elements
239 dtoBeanKey = "com.inspiresoftware.lib.dto.geda.assembler.TestDto7CollectionSubClass",
240 // Matcher that allows to aid synchronisation process
241 dtoToEntityMatcher = Test7Matcher.class,
242 // Generic collection element class (ideally should be an interface)
243 entityGenericType = TestEntity7CollectionSubClass.class
244 )
245 private java.util.Collection<TestDto7CollectionSubInterface> nestedString;
246
247 ... getters/setters go here ...
248
249 }
250
251 /** DTO that describes collection item. */
252
253 @Dto
254 @Ignore
255 public class TestDto7CollectionSubClass implements TestDto7CollectionSubInterface {
256
257 // default mapping to entity property with same name
258 @DtoField
259 private String name;
260
261 ... getters/setters go here ...
262
263 }
264
265 /** Matcher for the above entities. */
266 public class Test7Matcher implements DtoToEntityMatcher<TestDto7CollectionSubClass, TestEntity7CollectionSubClass> {
267
268 public boolean match(final TestDto7CollectionSubClass testDto7CollectionSubClass,
269 final TestEntity7CollectionSubClass testEntity7CollectionSubClass) {
270 final String dtoName = testDto7CollectionSubClass.getName();
271 final String entityName = testEntity7CollectionSubClass.getName();
272
273 return dtoName != null && entityName != null && dtoName.equals(entityName);
274 }
275 }
276
277
278 {{/code}}
279 )))
280
281 === Collections DSL ===
282
283 |(% style="color:green" %){{icon name="check"/}} This example uses the same DTO POJO's as annotations version but without the need for the annotations on classes
284
285 **Mapping TestDto7CollectionClass that holds collection of TestDto7CollectionSubInterface elements to an arbitrary entity**
286
287 (((
288 {{code language="java"}}
289 final ExtensibleBeanFactory bf = ...;
290
291 final Registry registry = new DefaultDSLRegistry(bf);
292
293 registry
294 // main mapping
295 .dto(TestDto7CollectionClass.class).forEntity("com.inspiresoftware.lib.dto.geda.assembler.TestEntity7CollectionSubClass")
296 // map collection field
297 .withCollection("nestedString").forField("collection")
298 // Bean key for BeanFactory to create new instances of DTO for collection elements
299 .dtoBeanKey("com.inspiresoftware.lib.dto.geda.assembler.TestDto7CollectionSubClass")
300 // Bean key for BeanFactory to create new entity beans during synchronisation
301 .entityBeanKeys("com.inspiresoftware.lib.dto.geda.assembler.TestEntity7CollectionSubClass")
302 // Matcher that allows to aid synchronisation process
303 .dtoToEntityMatcherKey(Test7Matcher.class)
304 // NO NEED to specify entityGenericType - since this is all runtime
305
306 registry
307 // item mapping
308 .dto(TestDto7CollectionSubClass.class).forEntityGeneric()
309 // default mapping to entity property with same name
310 .withField("name")
311
312
313 {{/code}}
314 )))
315
316 == Map field mapping ==
317
318 === Maps Annotations ===
319
320 **Maps Example 1: Mapping TestDto12MapToCollectionClass map property from a collection on entity TestEntity12CollectionItemInterface**
321
322 (((
323 {{code language="java"}}
324 @Dto
325 @Ignore
326 public class TestDto12MapToCollectionClass implements TestDto12MapIterface {
327
328 @DtoMap(
329 // map this DTO property to a nested entity property
330 value = "collectionWrapper.items",
331 // bean key for new DTO objects
332 dtoBeanKey = "dtoItem",
333 // entity bean keys - need two because we have two levels "collectionWrapper" then "items"
334 entityBeanKeys = { "nestedEntity", "entityItem" },
335 // matcher to be used for synchronisation
336 dtoToEntityMatcher = Test12KeyMapToEntityMatcher.class,
337 // generic interface for item entity
338 entityGenericType = TestEntity12CollectionItemInterface.class,
339 // default collection class for creating new collection on entity
340 entityMapOrCollectionClass = ArrayList.class,
341 // property that will be used on collection element as
342 // key (i.e. Map.Entry[items[i].name = items[i]])
343 entityCollectionMapKey = "name")
344 private Map<String, TestDto12CollectionItemIterface> items;
345
346 ... getters/setters go here ...
347
348 }
349
350 ...mapping for item follow...
351
352 /** Matcher for the above entities. */
353 public class Test12KeyMapToEntityMatcher implements
354 DtoToEntityMatcher<String, TestEntity12CollectionItemInterface> {
355
356 public boolean match(final String dtoKey,
357 final TestEntity12CollectionItemInterface entity) {
358 final String dtoName = dtoKey;
359 final String entityName = entity.getName();
360
361 return dtoName != null && entityName != null && dtoName.equals(entityName);
362 }
363 }
364 {{/code}}
365 )))
366
367 **Maps Example 2: Mapping TestDto12MapToMapByKeyClass map property from a map on entity TestEntity12CollectionItemInterface and use value for DTO**
368
369 (((
370 {{code language="java"}}
371 @Dto
372 @Ignore
373 public class TestDto12MapToMapClass implements TestDto12MapIterface {
374
375 @DtoMap(
376 // map DTO map property to an entity collection property
377 value = "items",
378 // bean key for new DTO objects
379 dtoBeanKey = "dtoItem",
380 // entity bean key
381 entityBeanKeys = "entityItem",
382 // matcher to be used for synchronisation
383 dtoToEntityMatcher = Test12MapKeysMatcher.class,
384 // generic interface for item entity
385 entityGenericType = TestEntity12CollectionItemInterface.class)
386 private Map<String, TestDto12CollectionItemIterface> items;
387
388 ... getters/setters go here ...
389
390 }
391
392 ...mapping for item follow...
393
394 /** Matcher for the above entities. */
395 public class Test12MapKeysMatcher implements DtoToEntityMatcher<String, String> {
396
397 public boolean match(final String dtoKey,
398 final String entityKey) {
399 final String dtoName = dtoKey;
400 final String entityName = entityKey;
401
402 return dtoName != null && entityName != null && dtoName.equals(entityName);
403 }
404 }
405
406 {{/code}}
407 )))
408
409 **Maps Example 3: Mapping TestDto12MapToMapByKeyClass map property from a map on entity TestEntity12CollectionItemInterface and use key for DTO**
410
411 (((
412 {{code language="java"}}
413 @Dto
414 @Ignore
415 public class TestDto12MapToMapByKeyClass implements TestDto12MapByKeyIterface {
416
417 @DtoMap(
418 // map DTO map property to an entity collection property
419 value = "items",
420 // bean key for new DTO objects
421 dtoBeanKey = "dtoItem",
422 // entity bean key
423 entityBeanKeys = "entityItem",
424 // matcher to be used for synchronisation
425 dtoToEntityMatcher = Test12MapEntityByKeyMatcher.class,
426 // generic interface for item entity
427 entityGenericType = TestEntity12CollectionItemInterface.class,
428 // entity's map entry key should be used for conversion rather
429 // than value (i.e. Map.Entry[items[i].key = items[i].value])
430 useEntityMapKey = true)
431 private Map<TestDto12CollectionItemIterface, String> items;
432
433
434 ... getters/setters go here ...
435
436 }
437
438 ...mapping for item follow...
439
440 /** Matcher for the above entities. */
441 public class Test12MapEntityByKeyMatcher implements
442 DtoToEntityMatcher<TestDto12CollectionItemIterface, TestEntity12CollectionItemInterface> {
443
444 public boolean match(final TestDto12CollectionItemIterface dto,
445 final TestEntity12CollectionItemInterface entity) {
446 final String dtoName = dto.getName();
447 final String entityName = entity.getName();
448
449 return dtoName != null && entityName != null && dtoName.equals(entityName);
450 }
451 }
452
453 {{/code}}
454 )))
455
456 === Maps DSL ===
457
458 |(% style="color:green" %){{icon name="check"/}} This example uses the same DTO POJO's as annotations version but without the need for the annotations on classes
459
460 **Maps Example 1: Mapping TestDto12MapToCollectionClass map property from a collection on entity TestEntity12CollectionItemInterface**
461
462 (((
463 {{code language="java"}}
464 final ExtensibleBeanFactory bf = ...;
465 final Registry registry = new DefaultDSLRegistry(bf);
466 registry
467 // main mapping
468 .dto(TestDto12MapToCollectionClass.class).forEntityGeneric()
469 // map this DTO property to a nested entity property
470 .withMap("items").forField("collectionWrapper.items")
471 // bean key for new DTO objects
472 .dtoBeanKey("dtoItem")
473 // entity bean keys - need two because we have two levels "collectionWrapper" then "items"
474 .entityBeanKeys("nestedEntity", "entityItem")
475 // matcher to be used for synchronisation
476 .dtoToEntityMatcherKey(Test12KeyMapToEntityMatcher.class)
477 // default collection class for creating new collection on entity
478 .entityMapOrCollectionClass(ArrayList.class)
479 // property that will be used on collection element as key (i.e. Map.Entry[items[i].name = items[i]])
480 .entityCollectionMapKey("name")
481 // NO NEED to specify entityGenericType - since this is all runtime
482
483
484 registry
485 // item mapping
486 .dto(TestDto12MapToCollectionClass.class).forEntityGeneric()
487 ...mapping for item follow...
488
489 {{/code}}
490 )))
491
492 **Maps Example 2: Mapping TestDto12MapToMapByKeyClass map property from a map on entity TestEntity12CollectionItemInterface and use value for DTO**
493
494 (((
495 {{code language="java"}}
496 final ExtensibleBeanFactory bf = ...;
497 final Registry registry = new DefaultDSLRegistry(bf);
498 registry
499 // main mapping
500 .dto(TestDto12MapToMapClass.class).forEntityGeneric()
501 // map this DTO property to entity property with same name
502 .withMap("items")
503 // bean key for new DTO objects
504 .dtoBeanKey("dtoItem")
505 // entity bean key
506 .entityBeanKeys("entityItem")
507 // matcher to be used for synchronisation
508 .dtoToEntityMatcherKey(Test12MapKeysMatcher.class)
509 // NO NEED to specify entityGenericType - since this is all runtime
510
511 registry
512 // item mapping
513 .dto("dtoItem").forEntityGeneric()
514 ...mapping for item follow...
515
516 {{/code}}
517 )))
518
519 **Maps Example 3: Mapping TestDto12MapToMapByKeyClass map property from a map on entity TestEntity12CollectionItemInterface and use key for DTO**
520
521 (((
522 {{code language="java"}}
523 final ExtensibleBeanFactory bf = ...;
524 final Registry registry = new DefaultDSLRegistry(bf);
525 registry
526 // main mapping
527 .dto(TestDto12MapToMapByKeyClass.class).forEntityGeneric()
528 // map this DTO property to entity property with same name
529 .withMap("items")
530 // bean key for new DTO objects
531 .dtoBeanKey("dtoItem")
532 // entity bean key
533 .entityBeanKeys("entityItem")
534 // matcher to be used for synchronisation
535 .dtoToEntityMatcherKey(Test12MapEntityByKeyMatcher.class)
536 // entity's map entry key should be used for conversion rather than value
537 .useEntityMapKey();
538 // NO NEED to specify entityGenericType - since this is all runtime
539
540
541 registry
542 // item mapping
543 .dto("dtoItem").forEntityGeneric()
544 ...mapping for item follow...
545
546 {{/code}}
547 )))
548
549 == Parent entities ==
550
551 Some entities are not purely writable. E.g. if we are working with database relations and we do not want to re-create Parent entity but have a way to retrieve it and set by primary key. This is the purpose of Parent annotation / DSL.
552
553 === Parent Annotations ===
554
555 **Mapping TestDto11ChildClass property "parent" to a TestDto11ParentInterface, so that we do not create a blank instance but retrieve this parent object**
556
557 (((
558 {{code language="java"}}
559 @Dto
560 @Ignore
561 public class TestDto11ChildClass implements TestDto11ChildInterface {
562
563 @DtoParent(
564 // Defines the primary key to use to retrieve TestDto11ParentClass i.e. parent.getParentId()
565 value = "parentId",
566 // Adapter key which will be used to retrieve this entity
567 // HibernateGet.retrieveByPrimaryKey(TestDto11ParentInterface.class, TestDto11ParentClass.class, parent.getParentId())
568 retriever = "HibernateGet"
569 )
570 @DtoField(
571 // Property that defines getter for this field
572 value = "parent",
573 // Bean key that will be used for creating instances of TestDto11ParentInterface
574 dtoBeanKey = "TestDto11ParentClass",
575 // Bean key that will be used for creating instances of TestEntity11ParentClass
576 entityBeanKeys = "TestEntity11ParentClass"
577 )
578 private TestDto11ParentInterface parent;
579
580 ... getters/setters go here ...
581
582 }
583
584 public class HibernateGet implements ExposedEntityRetriever {
585
586 private Session session;
587
588 public HibernateGet(Session session) {
589 this.session = session;
590 }
591
592 public Object retrieveByPrimaryKey(final Class entityInterface,
593 final Class entityClass,
594 final Object primaryKey) {
595 return session.get(entityClass, primaryKey);
596 }
597
598 }
599 {{/code}}
600 )))
601
602 === Parent DSL ===
603
604 |(% style="color:green" %){{icon name="check"/}} This example uses the same DTO POJO's as annotations version but without the need for the annotations on classes
605
606 **Mapping TestDto11ChildClass property "parent" to a TestDto11ParentInterface, so that we do not create a blank instance but retrieve this parent object**
607
608 (((
609 {{code language="java"}}
610 final ExtensibleBeanFactory bf = ...;
611
612 final Registry registry = new DefaultDSLRegistry(bf);
613
614 registry
615 // main mapping
616 .dto(TestDto11ChildClass.class).forEntityGeneric()
617 // Property that defines getter for this field
618 .withField("parent")
619 // Bean key that will be used for creating instances of TestDto11ParentInterface
620 .dtoBeanKey("TestDto11ParentClass")
621 // Bean key that will be used for creating instances of TestEntity11ParentClass
622 .entityBeanKeys("TestEntity11ParentClass")
623 // Defines the primary key to use to retrieve TestDto11ParentClass i.e. parent.getParentId()
624 .dtoParent("parentId")
625 // HibernateGet.retrieveByPrimaryKey(TestDto11ParentInterface.class, TestDto11ParentClass.class, parent.getParentId())
626 .retriever("HibernateGet")
627 registry
628 // parent mapping
629 .dto(TestDto11ParentClass.class).alias("TestEntity11ParentClass").forEntityGeneric()
630 ...mapping for parent follow...
631
632 {{/code}}
633 )))
634
635 == Sub Entities ==
636
637 Sub entities are simply fields that represent DTO's, so we need to create on-the-fly sub assemblers to transfer data. This is easily accomplished by providing dtoBeanKey and entityBeanKeys to DtoField annotation / withField() DSL.
638
639 === Sub Entities Annotations ===
640
641 **Mapping TestDto11Class property "sub" to a TestEntity11SubClass in "chained.subentity"**
642
643 (((
644 {{code language="java"}}
645 @Dto
646 @Ignore
647 public class TestDto11Class implements TestDto11Interface {
648
649 @DtoField(
650 // map property for this field
651 value = "chained.subentity",
652 // Bean key that will be used for creating instances of TestDto11SubInterface
653 dtoBeanKey = "TestDto11SubClass",
654 // Bean keys that will be used for creating instances of TestEntity11ChainSubClass and TestEntity11SubClass
655 entityBeanKeys = { "TestEntity11ChainSubClass", "TestEntity11SubClass" }
656 )
657 private TestDto11SubInterface sub;
658
659 ... getters/setters go here ...
660
661 }
662
663 {{/code}}
664 )))
665
666 === Sub Entities DSL ===
667
668 |(% style="color:green" %){{icon name="check"/}} This example uses the same DTO POJO's as annotations version but without the need for the annotations on classes
669
670 **Mapping TestDto11Class property "sub" to a TestEntity11SubClass in "chained.subentity"**
671
672 (((
673 {{code language="java"}}
674 final ExtensibleBeanFactory bf = ...;
675
676 final Registry registry = new DefaultDSLRegistry(bf);
677
678 registry
679 // main mapping
680 .dto(TestDto11Class.class).forEntityGeneric()
681 // map property for this field
682 .withField("sub").forField("chained.subentity")
683 // Bean key that will be used for creating instances of TestDto11SubInterface
684 .dtoBeanKey("TestDto11SubClass")
685 // Bean key that will be used for creating instances of TestEntity11ChainSubClass and TestEntity11SubClass
686 .entityBeanKeys("TestEntity11ChainSubClass", "TestEntity11SubClass")
687 ...mapping for parent follow...
688
689 {{/code}}
690 )))
691
692 == Virtual fields ==
693
694 Virtual field allow to process incompatible or inexistent fields and map them to dto. The rationale is that some fields values must be computed from two or more other field on the source entity and hence no clear mapping for entity field can be defined. Therefore GeDA provides virtual field mapping to account for this.
695
696 === Virtual fields Annotations ===
697
698 **Mapping TestDto20Class virtual field**
699
700 (((
701 {{code language="java"}}
702 @Dto
703 @Ignore
704 public class TestDto20Class {
705
706 @DtoVirtualField(converter = "VirtualMyBoolean")
707 private Boolean myBoolean;
708
709 ... getters/setters go here ...
710
711 }
712
713 public class VirtualMyBooleanConverter implements ValueConverter {
714
715 public static final ThreadLocal userThreadLocal = new ThreadLocal();
716
717
718 /** {@inheritDoc} */
719 public Object convertToDto(final Object object, final BeanFactory beanFactory) {
720 final TestEntity20Class entity = (TestEntity20Class) object;
721 return entity.whatWasComplexDecision(userThreadLocal.get());
722 }
723
724 /** {@inheritDoc} */
725 public Object convertToEntity(final Object object, final Object oldEntity,
726 final BeanFactory beanFactory) {
727 final TestEntity20Class entity = (TestEntity20Class) oldEntity;
728 entity.makeComplexDecision((Boolean) object, userThreadLocal.get());
729 return entity;
730 }
731
732 }
733
734 {{/code}}
735 )))
736
737 === Virtual fields DSL ===
738
739 **Mapping TestDto20Class virtual field**
740
741 (((
742 {{code language="java"}}
743 final ExtensibleBeanFactory bf = ...;
744
745 final Registry registry = new DefaultDSLRegistry(bf);
746
747 registry
748 // main mapping
749 .dto(TestDto20Class.class).forEntityGeneric()
750 // map property for this field
751 .withField("myBoolean").forVirtual().converter("VirtualMyBoolean")
752
753 {{/code}}
754 )))
755
756 == Beyond entities (Maps and Lists) ==
757
758 From time to time it is necessary to work with Lists and Maps instead of objects themselves. For example consider the following scenario: We have an application supported by Hibernate ORM but we also use Full text search capabilities backed by Lucene. Hibernate search allows to wrap that and get the entities - but we do not want that. We do not want to hit the database unless we are really sure we get the right result from search. So we use projectsion which return as Map<String,Object>. We still want to use the same fields names but apply it to map. No fear - GeDA is here...
759
760 === Beyond entities Annotations ===
761
762 **Mapping TestDto2Class and use it for entities, maps and lists**
763
764 (((
765 {{code language="java"}}
766 /* Basic mapping */
767 @Dto
768 @Ignore
769 public class TestDto2Class {
770
771 @DtoField("name")
772 private String myString;
773
774 ... getters/setters go here ...
775
776 }
777
778 {{/code}}
779 )))
780
781 **Using assembler for entity**
782
783 (((
784 {{code language="java"}}
785 final TestEntity2Class entity = ...;
786 final TestDto2Class dto = new TestDto2Class();
787 final Assembler assembler =
788 DTOAssembler.newCustomAssembler(TestDto2Class.class, TestEntity2Class.class);
789 // assemble DTO from entity
790 assembler.assembleDto(dto, entity, null, null);
791 final TestEntity2Class copy = new TestEntity2Class();
792 // assemble Entity from Dto
793 assembler.assembleEntity(dto, copy, null, null);
794
795 {{/code}}
796 )))
797
798 Okay - what about Map?
799
800 **Using assembler for map**
801
802 (((
803 {{code language="java"}}
804 final Map<String, Object> map = new HashMap<String, Object>;
805 map.put("name", "My name is HashMap");
806 final TestDto2Class dtoFromMap = new TestDto2Class();
807 final Assembler assembler =
808 DTOAssembler.newCustomAssembler(TestDto2Class.class, Map.class);
809 // assemble DTO from map
810 assembler.assembleDto(dtoFromMap, map, null, null);
811 final Map<String, Object> mapCopy = new HashMap<String, Object>;
812 // assemble Map from Dto
813 assembler.assembleEntity(dtoFromMap, mapCopy, null, null);
814
815 {{/code}}
816 )))
817
818 Was it cool or what? All we needed to change the target entity class
819
820 Okay - what about List?
821
822 **Using assembler for list**
823
824 (((
825 {{code language="java"}}
826 final List<Object> list = new ArrayList<Object>;
827 list.add("name");
828 list.add("My name is ArrayList");
829 final TestDto2Class dtoFromList = new TestDto2Class();
830 final Assembler assembler =
831 DTOAssembler.newCustomAssembler(TestDto2Class.class, Map.class);
832 // assemble DTO from List
833 assembler.assembleDto(dtoFromList, list, null, null);
834 final List<Object> listCopy = new ArrayList<Object>();
835 // assemble List from Dto
836 assembler.assembleEntity(dtoFromList, listCopy, null, null);
837
838 {{/code}}
839 )))
840
841 Again - pure awesomeness
842
843 But this is just to get you started - how about joggling between assemblers?
844
845 **DTO mayhem**
846
847 (((
848 {{code language="java"}}
849 final TestEntity2Class entity = ...;
850 final TestDto2Class dto = new TestDto2Class();
851 final Assembler entityAsm =
852 DTOAssembler.newCustomAssembler(TestDto2Class.class, TestEntity2Class.class);
853 final Assembler mapAsm =
854 DTOAssembler.newCustomAssembler(TestDto2Class.class, Map.class);
855
856 // assemble DTO from entity to get data
857 entityAsm.assembleDto(dto, entity, null, null);
858
859 // assemble Map from Dto
860 final Map<String, Object> mapObj = new HashMap<String, Object>();
861 mapAsm.assembleEntity(dto, map, null, null);
862
863 ... do something with map like pass to lucene or auto generate web form ...
864
865 // get values from map to DTO
866 mapAsm.assembleDto(dto, map, null, null);
867
868 // put DTO value to original entity
869 entityAsm.assembleEntity(dto, entity, null, null);
870
871 {{/code}}
872 )))
873
874 === Beyond entities DSL ===
875
876 DSL works much in the same way as the Assembler interface is the same for all types of assemblers. However there are subtle differences in how DSL works. Annotations are static so in fact all @Dto annotations are equivalent to registry.dto("someDto").forEntityGeneric(). Meaning that we map to an arbitrary entity and hence with annotations we can only have one mapping per DTO class. This is inflexible when for example we will full read/write access to fields for DTO/Entity mapping but read only access for DTO/Map. So here are some techniques for working with DSL.
877
878 **Simple Alternative Mappings**
879
880 (((
881 {{code language="java"}}
882 // We define SomeDto to SomeEntity mapping with single field "field1"
883 // This mapping work only for this pair
884 registry.dto(SomeDto.class).forEntity(SomeEntity.class).withField("field1");
885
886 // Define SomeDto to HashMap with read only access for field1 only
887 registry.dto(SomeDto.class).forEntity(HashMap.class).withField("field1").readOnly();
888
889 // Finally define SomeDto to generic entity mapping (a sort of catch all) with uses
890 // converter for firld1
891 registry.dto(SomeDto.class).forEntityGeneric().withField("field1").converter("field1Converter");
892
893 {{/code}}
894 )))
895
896 As you can see with DSL we can provide alternative mappings to fine grain how exactly entities are mapped.
897
898 But this looks like duplication? Is there a way to overcome this? Yes, of course!
899
900 Suppose SomeEntity has subclass SomeComplexEntity - how can we reuse mappings from SomeEntity?
901
902 **Reuse mappings**
903
904 (((
905 {{code language="java"}}
906 // we define single field mapping
907 registry.dto(SomeDto.class).forEntity(SomeEntity.class).withField("field1");
908
909 // useContextFor allows to copy all mappings from SomeDto-SomeEntity pair to SomeDto-SomeComplexEntity
910 // and then we add another mapping to it for field2.
911 registry.dto(SomeDto.class)
912 .useContextFor(dtoCtx.forEntity(SomeEntity.class), SomeComplexEntity.class).withField("field2");
913
914 {{/code}}
915 )))
916
917 |(% style="color:red" %){{icon name="times-circle"/}} When using useContextFor we are copying mappings as they are at this point in time. So we get a copy of mappings and apply it to SomeComplexEntity. The original mappings remain unchanged. So two graphs of mapping will exist in isolation.
918
919 = More examples =
920
921 We do appreciate that there are quite a few configurations involved in mapping DTO however it would be impractical to try an explain all use cases for each feature of GeDA API.
922
923 Please download source code and have a look at our comprehensive list of examples. It will be much easier for you to understand how GeDA works by looking at actual working examples.
924
925 The examples are located in two places:
926
927 **examples** module
928
929
930 [[image:examples-module.png||alt=""]]
931 \\And some more advanced and not so easy to read **core** examples used by JUnits
932
933 The example are located in core/src/test/java/com.inspiresoftware.lib.dto.geda.assembler.examples package:
934
935
936 [[image:examples.png||alt=""]]
937
938
939 |(% style="color:green" %){{icon name="check"/}} We strongly recommend to setup GeDA project in an IDE such as Intellij IDEA or Eclipse as you will be able to easily navigate to the JUnit test that use the sample mapping DTO and Entity files
940
941 |(% style="color:green" %){{icon name="check"/}} See this article about [[setting up GeDA project>>doc:GeDA - Generic DTO Assembler.Setting up GeDA project.WebHome]]
942
943 All example DTO and Entities are used in one or more JUnit tests or in a dedicated examples module that clearly show the use cases for each feature of GeDA.
944
945 |(% style="color:red" %){{icon name="times-circle"/}} **examples** and performance testing modules (**core-btest**, **core-ptest** and **osgi-itest**) are not meant to be used as dependencies to your project. Please use them only as sandbox to test out some of GeDA's features by setting up GeDA project
946
947 = Got stuck? =
948
949 Post your question on GeDA [[Google Groups>>https://groups.google.com/d/forum/geda-generic-dto-assembler-discussion-group]]
GeDA - Generic DTO Assembler © Denys Pavlov 2009 - 2018
v.1.0.0