Export to GitHub

toman - GettingStarted.wiki


Introduction

The goal of this library is to relieve the business (application) tier from a burden of transfer object (TO) classes designed for the sake of the web (presentation) tier. The following picture depicts a common situation that takes place as long as the web tier's TOs are maintained and delivered by the business tier. These TO classes are very often single-purposed for the use in the web tier and their presence in the business tier leads to tight coupling between the two tiers and introduces an unnecessary knowledge of the web tier's details into the business tier.

https://lh6.googleusercontent.com/_NBdZbm2Vu7g/TbaR2gRBgCI/AAAAAAAAAB0/thkNm9dxiss/s400/to_in_business_tier.png

The following picture illustrates an improved design, in which the business tier is agnostic of the web tier's transfer objects.

https://lh5.googleusercontent.com/_NBdZbm2Vu7g/TbaR20fnlmI/AAAAAAAAACA/3e9GP87K25Q/s400/to_in_web_tier.png

The question arises: How to achieve this design in practice?

The core idea is to let the web tier developer to design its transfer objects that suit his needs and to maintain them in the web tier module. Each TO class is annotated with a special annotation that provides information or formula for constructing and populating instances of the TO class. This formula is read by a special component residing in the business tier, which is able to construct instances of annotated TO classes. This component (TOMan) is generic and does not directly depend on any instantiated TO classes (these classes are passed as an argument of its methods). The formula is written in an object query language like JPQL or HQL. It is assumed that the web developer knows the domain model of the application and relationships between domain entities (the knowledge of the underlying SQL structures is not necessary) and so he should be able to define his needs with the help of some object query language.

The following picture shows the schema of an imaginary PhotoAlbum application. There are two entities in the application Photo and Album (not shown on the picture) having N:1 relationship established between them. The picture depicts the BasicPhotoAlbumTO transfer object class whose goal is to select and combine some data originating in both entities: photo id, photo name, photo description and album name.

https://lh5.googleusercontent.com/_NBdZbm2Vu7g/TbaR2yQfMJI/AAAAAAAAAB4/p8wJnTZhIUg/s640/find_to.png

The class is annotated with annotation @TO having three attributes: * id - a QL WHERE expression fragment allowing identification of the TO. It is concatenated with the expressions in the remaining attributes either to find a TO for a given ID or to update the database with the TO's data. * read - a QL SELECT expression fragment whose purpose is to enumerate the attributes that constitute the TO. Each element appearing right after the SELECT keyword must correspond to a field in the TO class. Each such a field must be annotated with annotation @Index that binds the field to the corresponding element in the SELECT clause using its index in the list. This expression is concatenated with the value in the id attribute when finding the TO for a given unique (primary) key. It also used when querying the database for a list of TOs. In such a case this expression is concatenated with the WHERE clause fragment provided by the client code (the second argument of the createQuery method). * write - a QL UPDATE expression fragment used for updating the database with the state in the TO. Before updating this expression is concatenated with value in the id attribute.

The following picture illustrates updating database with the TO's state:

https://lh3.googleusercontent.com/_NBdZbm2Vu7g/TbaR3JrQ_8I/AAAAAAAAACE/jI4nEyy9obY/s640/update_to.png

The following picture shows how querying the database for a list of TO's is being carried out:

https://lh4.googleusercontent.com/_NBdZbm2Vu7g/TbaR201WPCI/AAAAAAAAAB8/XzEeoV6vSXY/s640/query_to.png

More features

  • See CheatSheet for a dense overview of features
  • Referencing other TOs from within a TO. Use the Ref annotation. See ReferencingTransferObjects.
  • Using setters as initializers - simply annotate setters instead of fields. See UsingSettersInsteadFields.
  • Getting values for a TO from a query that returns entities instead vectors. Use the Prop(propName) annotation to reference the property in the entity. See ReferencingEntityProperties.
  • TO inheritance. See TransferObjectsInheritance.

Source code

Transfer object class example

``` @TO( id = "p.id=:photoId", read = "select p.id, p.name, p.description, p.album.name from Photo p", write = "update Photo p set p.name=:photoName, p.description=:photoDescription") public class BasicPhotoAlbumTO {

@Index(0)
private int photoId;
@Index(1)
private String photoName;
@Index(2)
private String photoDescription;
@Index(3)
private String albumName;

public int getPhotoId() {
    return photoId;
}

public void setPhotoId(int photoId) {
    this.photoId = photoId;
}

public String getPhotoName() {
    return photoName;
}

public void setPhotoName(String photoName) {
    this.photoName = photoName;
}

public String getPhotoDescription() {
    return photoDescription;
}

public void setPhotoDescription(String photoDescription) {
    this.photoDescription = photoDescription;
}

public String getAlbumName() {
    return albumName;
}

}

```

JPA client

``` public class JPAMain {

public static void main(String[] args) throws Exception {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("album");
    EntityManager entityManager = entityManagerFactory.createEntityManager();

    JPATOMan toMan = new JPATOMan(entityManager);

    // List all photos
    TOMan.TOQuery<BasicPhotoAlbumTO> query = toMan.createQuery(BasicPhotoAlbumTO.class, null);
    List<BasicPhotoAlbumTO> results = query.getResults();
    for (BasicPhotoAlbumTO to : results) {
        System.out.println(to.getPhotoId() + " " + to.getPhotoDescription() + " " + to.getAlbumName());
    }

    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();

    // finding the photo for a given ID
    BasicPhotoAlbumTO to = toMan.findTO(BasicPhotoAlbumTO.class, 351);
    to.setPhotoDescription(to.getPhotoDescription() + "!");
    // updating the photo's description
    toMan.updateTO(to);

    tx.commit();

}

}

```

Hibernate client

``` public class HMain { public static void main(String[] args) throws Exception { SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.openSession();

    HTOMan toMan = new HTOMan(session);

    // List all photos
    TOMan.TOQuery<BasicPhotoAlbumTO> query = toMan.createQuery(BasicPhotoAlbumTO.class, null);
    List<BasicPhotoAlbumTO> results = query.getResults();
    for (BasicPhotoAlbumTO to : results) {
        System.out.println(to.getPhotoId() + " " + to.getPhotoDescription() + " " + to.getAlbumName());
    }

    Transaction tx = session.beginTransaction();

    // finding the photo for a given ID
    BasicPhotoAlbumTO to = toMan.findTO(BasicPhotoAlbumTO.class, 351);
    to.setPhotoDescription(to.getPhotoDescription() + "!");
    // update the photo's description
    toMan.updateTO(to);

    tx.commit();

}

} ```