DTO plugin for Grails
Dependency :
compile ":dto:0.2.4"
Summary
Installation
The usual:
grails install-plugin dto
Description
Manually creating and managing DTO objects for your domain classes is labour intensive and error-prone. This plugin aims to simplify DTOs by automatically generating them and providing a mechanism to easily map domain class instances to DTO instances.The basic behaviour of this command is to generate DTO classes in the
the command will create a file
If you don't provide any parameters to the command, it will prompt you for the name of a domain class. Once you provide that, it generates the corresponding DTO. You can also pass the names of multiple domain classes as separate arguments to the command, like so:
Note than when you provide the name of a domain class, you should include the package. If you don't include it, the command assumes the domain class is in the default package. It makes for more typing than you might expect, but that's the way it works for now.So what about those two optional arguments?
Running the command
will result in both a
If you have a lot of DTOs, though, you end up flooding that single package. What if you just want to substitute the root of the package, but keep the sub-packages that help differentiate the domain classes? For example, you may have domain classes in
Voila! Package substitution. This is a particularly important feature for GWT users, because they typically have to put classes used by the client into a "client" sub-package.Of course, typing such a long command line every time you want to generate some DTOs is a pain, so you can also specify your package transformations in your application's
You can specify as many transformations as you like. If the generator comes across a class that doesn't fit one of the "from" packages, it falls back to the default behaviour of putting the DTO in the same package as the domain class. If you would rather have a different fallback package, you can use the wildcard:
That's it for the DTO generation. The next stage involves creating DTO instances from their corresponding domain instances.
The assumption in the above example is that
It's important to realise that, in this case, the argument to
See how we use a "classpath:" prefix? Any valid Spring resource path can be used. Also take care to put the above setting in
Check out what's changed in the plugin's release notes.When you install this plugin, you get two features. The first is a new command "generate-dto".
The generate-dto command
grails generate-dto [--all] [--non-recursive] [domain class ...]
src/java directory that have properties matching the corresponding domain classes. For example, if you have a domain class
package org.exampleclass Post { String content int priority }
src/java/org/example/PostDTO.java that looks like this:
package org.examplepublic class PostDTO implements grails.plugins.dto.DTO { private static final long serialVersionUID = 1L; private String content; private int priority; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } }
grails generate-dto org.example.Post org.example.security.User org.example.Other
-
--all: The command will generate DTOs for all your domain classes. -
--non-recursive: The command will not generate DTOs for domain class relations/associations.
generate-dto to not only create DTOs for the specified domain classes, but also for any related domain classes and any relations of those related domain classes. And so on. For example, let's say you have the following two domain classes:
class Post {
User user
String content
}class User {
String username
String passwordHash static hasMany = [ users: User ]
}grails generate-dto Post
PostDTO.java file and a UserDTO.java one. But if you specify the --non-recursive option, then only PostDTO.java will be created.As of version 0.2 of the plugin, you can control which packages the DTO classes are created in. For example, say you want all DTOs to go into the same package, org.example.dto . Just pass the target package as an argument to --pkg :
grails generate-dto --all --pkg=org.example.dto
org.example.attributes , org.example.posts , and org.example.security , but want the DTOs in org.another.dto.client.attributes , org.another.dto.client.posts , and org.another.dto.client.security . In other words, you want to swap the two root parts of the package with another root. Easily done:
grails generate-dto --all --oldpkg=org.example --newpkg=org.another.dto.client
grails-app/conf/BuildConfig.groovy file:
dto.package.transforms = [ "org.example": "org.another.dto.client" ]
dto.package.transforms = [ "*": "org.example.dto", "org.example": "org.another.dto.client" ]
Converting domain instances to DTOs
Once you have a domain instance, you can create the corresponding DTOs by one of two methods:-
domainObj as DTO -
domainObj.toDTO()
grails.plugins.dto.DTO .These methods work well if the DTO classes have the same package as their corresponding domain classes, but if you have used package substitution during the DTO generation, you'll run into problems. In such cases, you can use a variation of the second method:
import ....UserDTO
…
def dto = user.toDTO(UserDTO)UserDTO is in a different package than the User domain class, hence the import. So the User instance will explicitly be converted to a UserDTO instance.Occasionally you may have a collection of domain classes that you want to convert. For example, when you execute a findAllBy query. You could convert each domain instance in the collection one by one, but the DTO plugin gives you a shortcut:
def users = User.findAllByAgeGreaterThan(20)
def dtos = users.toDTO(UserDTO)toDTO() is the class of objects you want in the resulting list, not the class of the list (or other collection). The method is hard-coded to create a TreeSet if the source collection is a SortedSet , a HashSet for a Set , and an ArrayList for all other collection types.The basic mapping of fields from domain classes to DTOs isn't always appropriate. For example, what if you want to store a relationship's ID in the DTO rather than the relationship itself? In that case, you need to manually modify the generated DTO class and add a Dozer mapping file. The mapping file can be located anywhere and called anything because you specify its (or their) location(s) via this configuration setting:
dto.mapping.files = [ "classpath:dozer-mapping.xml" ]Config.groovy not BuildConfig.groovy .