GeDA documentation has moved to http://www.inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler now. This page may be outdated


GeDA - Generic DTO Assembler

GeDA logoGeDA was inspired by developer's laziness (as always). The rationale for its existence was overhead of creating DTO Assemblers in an n-tier application to pass information within domain object to UI through DTO's (Data Transfer Objects). The basic principle of the above application design is to extract the necessary information from the domain objects in the form of DTO's. In reality this involves a tedious get/set method calls that look very much the same for most of the objects. GeDA uses Java5's annotations in order to map DTO's onto paths of the Domain object properties (the reflection method). Thus the annotated DTO's can be examined by a Generic DTO Assembler that will create specific instance of the assembler for the DTO is respect to a Domain object. The assmbler is basically a placeholder for so called "data pipes" that allow transferring data from/to domain object's field. But do not take my word for it - try it out yourself.

NOTE: Unfortunately I do not always have time to update the documentation, so please look into JUnit tests in the test source - these will be the best guides. 

web: https://sourceforge.net/projects/geda-genericdto/

latest stable links are here:

http://www.inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler

svn: svn co https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto geda-genericdto

To setup mvn dependency please read this page.

NOTE: If you have some questions please consult FAQ's section prior sending a request

contact us using our feedback form

Recent enhancements may be found here

 

Learn by Example:

Say we have a web-application that in some of its part manages the user details. So we have a domain object Person in which we have a domain object Address. On the other hand we have a web-page called "edit user info" where we wish to input the name and address details and submit it to the server.

So here is what we have:

Person:

package dp.lib.dto.geda.example;
public class Person {
    private Long personId;
    private String name;
    private Address address;
    public Long getPersonId() {
        return personId;
    }
    public void setPersonId(Long personId) {
        this.personId = personId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }

}

Address:

package dp.lib.dto.geda.example;

public class Address {

    private String street;
    private String city;

    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }

}


 

The DTO (Form data):

package dp.lib.dto.geda.example;

import dp.lib.dto.geda.annotations.Dto;
import dp.lib.dto.geda.annotations.DtoField;

@Dto
public class PersonDTO {


    @DtoField("personId")
    private Long personId;
    @DtoField("name")
    private String name;
    @DtoField("address.street")
    private String street;
    @DtoField("address.city")
    private String city;

    public Long getPersonId() {
        return personId;
    }
    public void setPersonId(Long personId) {
        this.personId = personId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }

}

So you can see that we have mapped our DTO fields to out Person+Address domain objects.

"What now?" I hear you saying. Well we need say PersonService that will provide our DTO wheneven our web-app needs it. Here is what it may look like:

PersonService:

package dp.lib.dto.geda.example;

public class PersonService {


    public PersonDTO getPersonInfo(final Long personId) {
        final Person person = getPersonFromDb(personId);
        final PersonDTO dto = newBean();

        final DTOAssembler assembler = DTOAssembler.newAssembler(PersonDTO.class, Person.class);
        assembler.assembleDto(dto, person, null);

        return dto;
    }

    public PersonDTO newBean() {
        return new PersonDTO();
    }

    public Person getPersonFromDb(final Long personId) {
        // some code to return a domain object
    }

}

 

Neat eh? So step by step:

1. We retrieved the Person domain object from DB

2. We created new DTO bean (This is better to be done through bean factory)

3. We created the assembler instance

4. we assembled the DTO

And what was done behind the sceenes (step 3):

1. The assembler cache was checked for existance of the assembler (the cache is used to optimise performance, but point to note is that there will be only one instance of assembler for a DTO - Domain pair)

2. A new instance of assembler is created and the DTO is examined with Domain object class to create "data pipes". In reality all the assembler is - is a collection of data pipes that transfer data from/to domain object. Each pipe is designated to one field of domain object.

3. The assembler instance is created and cached.

 

Ok, so while we were examining the code our user has altered the infor in web-form and posted it, so how will our PersonService will handle the post:

 

public class PersonService {

    ...

    public void savePersonInfo(final PersonDTO dto) {

        final Person person = getPersonFromDb(dto.getPersonId());

        final DTOAssembler assembler = DTOAssembler.newAssembler(PersonDTO.class, Person.class);
        assembler.assembleEntity(dto, person, null);

        saveEntity(person);

    }

    private void saveEntity(final Person person) {
        // do the db saving stuff
    }

    ...

}

Lets have a close look:

1. We retrieve a fresh domain object

2. We create the assemble (No we are not, we are using the cached one, since we provide the same DTO-Domain pair)

3. do the assembly

4. persist the changes

 

Well there is all that is to it... ehh - nope. As you can see each of the assemly methods has a third parameter which is a map of value converters. GeDA declares a ValueConverter - that is used to define the converters. In our simple example above all data in DTO and Domain match their type, so all is nice and simple, but if we have some complex data in the domain? That is were the converters come into play.

To declare a converter you need to specify it in the field annotation:

For example we have an enum that needs to be a boolean in our DTO

    /**
     * Test enum for conversion testing.
     */
    public enum Decision { 
        /** true. */
        Decided, 
        /** false. */
        Undecided 
    };

    @Field(value = "decision", converter = "boolToEnum")
    private Decision myEnum;

And here is the Converter:

class TestConverter3 implements ValueConverter {

    public Object convertToDto(final Object object) {
        final Boolean value = (Boolean) object;
        if (value != null && value) {
            return Decision.Decided;
        }
        return Decision.Undecided;
    }

    public Object convertToEntity(final Object object) {
        final Decision value = (Decision) object;
        return (value != null && Decision.Decided.equals(value));
    }

}

 

And here is how we can use it:

       final Map converters = new HashMap();
        converters.put("boolToEnum", conv3toDto);

        final DTOAssembler assembler = 
            DTOAssembler.newAssembler(TestDto3Class.class, TestEntity3Class.class);

        assembler.assembleDto(dto, entity, converters);

        assertEquals(Decision.Decided, dto.getMyEnum());

So we need to assign the same key so that assembler know which converter in our map of converters to use. This gives great flexibility since you can reuse the same converter for many fields BUT the point to note the converters, since they are reused MUST be stateless, this is one of the reasons why they were designed to be a parameter on the conversion method - to enforce this idea.

 

The last feature that I want to talk about is the read only fields - sometimes the DTO is used for read only information or some of its fields. To let the assembler know this you can use the Field annotation in the following way:

	@DtoField(value = "number", readOnly = true)
    private Double myDouble;

What this will do is create a one way pipe for the field, meaning that when you call assebleDto all data from domain object will be copied to it but when you call assembleEntity only readOnly = false fields will be copied from DTO back to Entity. This is a nice feature to give you that extra notch of control over things.

 

Hope you enjoyed reading this as much as I have writting it. But I thing the best way to learn it is to download the source and examin the JUnit test - this will show various ways of how to use GeDA.

All the best to you and hope GeDA will save you some time in your projects.

 

If you have any questions / suggestions / bug reports please send them to us using the feedback form.

 

 

This page was last updated on: 01/03/2013 08:01