Github Project - Version
1.0
Mailing list -
Issues
Created & Maintained by Séven
Le Mesle
Mapping is checked at compilation, it is refactoring proof and debuggable
Uses no reflection, no runtime processing, works as fast as manual mapping
Uses only annotations to describe and configure mapping, no external configuration files
Built with JSR 269, supports any Java version from v6, works anywhere (IDE, Maven, Gradle, ...)
Lightweight API, easy to learn, gives rich compiler message with tips for fix
Supports custom mappers, mapping post-processor, custom field to field mapping
Selma is available in maven central, feel free to add it to your pom dependencies.
Selma uses a mapper interface to describe and configure the needed mapping. Here we want to map Order to OrderDto and reverse.
Here we defined some configuration for fields:
fullName
from the Customer class should be mapped to
the field customerFullName
in OrderDto reference
from any class should be mapped to the
field ref
in any matching classid
should be skipped because it does not exist in
OrderDtoOnce built the generated mapper can be retrieved via Selma class.
Compiler will report any issues like not mappable properties, unused mapping configuration or API
miss-use.
Check out our sample app
project.
Why a 1.0 release now ? Selma now support what I expected in it from the beginning : Custom type to type or field to field and type to type mapping, cyclic mapping, factories, enums, Collections, IoC, Arrays, all kinds of types mapping and so on. The 1.0 release of Selma comes with bug fixes and many new features. Like custom field to field mapping, Maps inheritance, Bean Aggregation and CDI support.
See release notes or Milestone 1.0 or Github Release Tag.
This release fixes bugs in generic handling and factory methods. It also implement a new cyclic mapping support using an instance cache. Many thanks to all bug reports and pull requests. 1.0 release will be next big release with specific field to field custom mappers. This will be the last release before final 1.0
See release notes or Milestone 0.15.
This release introduces factory methods, it is now possible to declare factory beans inside the mapper. Generic type handling has also been improved to support declaring a Mapper generic interface. Many thanks to all bug reports and pull requests. 1.0 release is coming we just cyclic mapping handling and specific field to field custom mappers.
See release notes or Milestone 0.14.
This release introduces abstract class mappers, it is now possible to declare custom mappings inside the mapper and call mapping methods from custom mappings. There are also bug fixes. Many thanks to all bug reports and pull requests.
this
.See release notes or Milestone 0.13.
This release adds support for collection mapping strategy. The mapping strategy allows to choose wether you want to populate collections from getter or not.
See release notes or Milestone 0.12.
This release adds Spring support using the new withIoc=SPRING
. It is also now possible to use
a Selma mapper as the custom mapper of another one.
See release notes or Milestone 0.11.
This release deprecates @IgnoreFields
and
@Fields.
The new @Maps
annotation
should now be used instead.
It supports all the parameters available in @Mapper
at the method
scope.
@IgnoreFields
and @Fields
in favor of a new annotation@IgnoreFields
from warning messagesSee release notes or Milestone 0.10.
Selma match properties using setter and getter names. Selma can map only same field names. By default, properties not existing in both source and destination beans will break compilation.
You can use Selma to build duplicates of your beans or map from model to DTO when they shares same property names.
Selma can reduce a model bean to a DTO containing a sublist of the properties.
Here we used IgnoreMissing.DESTINATION
to indicate we wish to ignore properties (age,
indices, tags) from source bean missing in destination bean. But we could also choose to specify each
ignored fields.
Here we used prefixes Person
and fr.xebia.selma.Person
to show that you can
match ignored fields using simple class name or FQCN prefix.
Selma can use a reduced DTO to build a model bean.
Here we used IgnoreMissing.SOURCE
to indicate we wish to ignore properties (age,
indices, tags) missing in source bean.
A mapper interface can describe multiple mapping methods.
Here we used @Maps
annotation to describe mapping per method. Please notice that
@Mapper
configuration is available for all mapping methods while @Maps
is only
applied to a single method. Both annotations provides the exact same parameters.
By default Selma checks every single missing properties but you can choose to disable these helpfull checks.
Here we used IgnoreMissing.ALL
to disable missing properties checking. So we removed
@Maps
annotation, feel free to combine @Mapper
and @Maps
in the
same mapper.
Selma takes care of generating mapping for the complete bean graph. Each embedded bean will be mapped in it's own method of the generated code. You can customize the mapping
Here we declared the asAddress(Address source)
method in the mapper interface to demonstrate
we can do it. asPersonDTO(Person source)
implementation will call asAddress(Address
source)
. Note that you do not need to declare this method for the mapping to work.
Selma can handle cyclic mapping. This behaviour is disabled by default because it has a small
performance impact. You can enable it using the withCyclicMapping
parameter available in
@Mapper
.
Here we declared the asPersonDTO(Person source)
method in the mapper interface to
map Person bean and adress beans. The person bean will be mapped only one time and propagated to the
person field of the Address
bean.
You can define custom property to property mapping using withCustomFields
parameter available in @Mapper
and @Maps
.
This configuration will map firstName
to name
property and
birthDay
to birthDate
property. The @Field
annotation define a
route that will be applied from PersonDTO
to Person
and reverse.
You can flatten properties from nested bean using withCustomFields
parameter available in @Mapper
and @Maps
.
This configuration will flatten properties from Address
bean to address* properties in
PersonDTO
. It also works to unflatten the properties from PersonDTO
to
Person
and Address
.
@Maps
annotation is powerfull and can be used multiple times in the same mapper interface.
The interface can comes to an end where you define the exact same @Maps
for multiple
method. To avoid this kind of code duplication @InheritMaps
is here to help you. Instead of
duplicating the @Maps
you can simply use @InheritMaps
on the corresponding
methods.
As you can see, the @InheritMaps
inherits its configuration from the @Maps
.
Selma does its best to identify the good @Maps
decorated method to inherit from. By default
Selma choose the method with the same in/out types pair.
Sometimes Selma finds multiple eligible methods to inherit from, in such situation you just need to
provide the method name to choose in the @InheritMaps
parameters.
Instead of creating a new instance of the destination bean, you can choose to map the source bean against a given instance of the destination bean. You only need to declare a second parameter in your mapping methods that will be used as destination instance.
Selma can map collections using a copy clone strategy. Selma will generate a new collection containing the values mapped from source collection. We support almost all kind of collections and the generated code takes care of preserving the order of elements.
By default Selma uses a setter to populate the newly created collection to the destination bean.
When using a Jaxb generated bean you won't have a setter, so you'll need to use the getter to retrieve
a fresh new instance of an empty collection to populate with mapped values.
For this to work you'll need to use the withCollectionStrategy = ALLOW_GETTER
of the
@Mapper
or @Maps
annotation.
The generated code will call getStrings()
from destination bean to
populate the collection. Be aware that default strategy does not allow to use getter for collection mapping.
Selma can map enums using a same value strategy by default. Selma will generate a new method for every single enum to enum mapping required, using a switch block without default.
Selma can map enums using a same value strategy with a default value. The default value will be used for values not existing in both enums.
Here we demonstrated 2 ways of declaring a default value for your enum mapping:
@Maps
with enum, so we need to declare both from and to enum
with a default value.@EnumMapper
directly on a method mapping two enums types,
with a default value.
It is also possible to declare the enumMapper
inside @Mapper
.
With this configuration SIMPLE_CUSTOMER
and BUSINESS_CUSTOMER
will be mapped
to CUSTOMER
.
Sometimes, you need to aggregate multiple source beans in the same destination bean. Selma can take care
of this, if you define the mapping method with multiple source beans.
This does not change anything for updates method, just add as the last parameter the bean to be updated.
The AggregationMapper generated class maps FirstBean
and
SecondBean
properties to the AggregatedBean
properties.
You can notice the mapFromAggregateInUpdate
method which will update
AggregatedBean out
and return the result.
As you can see in mapFromAggregateWithInterceptor
method, interceptors ares also supported.
For them to work, just define the intercepting method with all ordered source beans prior to the out bean
parameters.
Sometimes, Selma will not do what you need. Let say, you want to convert from String to date or from
Integer to Float. In Selma, we do not want magic conversion, so converting from boxed Integer to native
int and reverse is supported but, we do not support auto-magically convert from int to float because
there can be data loss. So you can define your own custom mapper.
A custom mapper is a class that
contains one or more methods taking an input parameter (the source bean / value) and return the
destination value after hand coded mapping. You only need to add the class to the
withCustom
attribute of the @Mapper
or @Maps
annotation.
The PersonMapper generated class will use the AddressCustomMapper
addressAsString
method to map the Address
to String
.
For now we've seen that it's possible to use custom mappers to apply on specific type to type conversion. If you need to apply a specific type to type conversion just for one propertie its also possible.
As you can see, the @Field
annotation now provides a parameter withCustom
to
handle custom mappers to apply to specific fields. They can be applied on Field to Field or on single
field name. This does work for both interceptors and custom mappers.
You can use custom mappers to update the destination bean as described in updating destination bean. For this kind of use the custom mapping method should declare a second parameter giving the destination bean.
All custom mapper class should define a public default constructor so the generated class can
instantiate it. Just remember, for selma, method name is nothing, just ensure to define the good In/Out
type pair.
Unused custom mapper will be reported as a compilation warning.
Not convinced by custom mapper, you need to do special things on the source or target bean after the mapping occurs. Selma, allows you to define a hook in the custom mapper which will be executed after the mapping itself. You just need to define a method returning void and taking two parameters the source bean and the destination bean. See the example below:
interceptAddressToDto
will be called by the generated mapping code at the end of the
mapping process.
The last way to declare custom mappers is to use an abstract class as the mapper de scription instead of an interface. Selma parse non abstract methods and load every custom mapper method it contains. This can simplify the mapping declaration and allows to call a generated mapping method from a custom mapper.
The generated code will call mapAddress()
method for each AddressIn
it should map.
To handle the mapping of CityIn
to CityOut
the custom mapping method calls
asCityOut()
implementation of the generated code.
Selma allows to declare a Mapper interface as the custom mapper of another one. Doing this way you can compose a complex mapping strategy made of several mappers calling each other. All mappers will be generated in their private scope.
AddressMapper
will be called by the generated mapping code to handle the mapping
of AddressIn
to AddressOut
.
By default Selma tries to map every bean. This means that given the same source and destination
propertie types, Selma will generate code to build a fresh new instance for the destination field.
If you want to directly use the source field in destination passing source reference to the destination
field, you should tell Selma that this is an immutable type.
This mapper will pass by reference any mapping from Address
to Address
.
You can specify as many immutables type as you want. It is also possible to declare immutables inside
@Maps
. The withImmutablesPackages
, will make Selma consider any bean inside
org.project.immutables
as immutable class.
Some beans can not be built with a default empty constructor. For some reason the application code use a
factory to provide new instances of these beans. Default Selma behavior is to call a default constructor
without parameters.
To solve this issue without using reflection at runtime, Selma supports sourced beans, the idea
is to pass one or more parameters to the constructor call in the generated code.
This mapper will pass the given DataSource
instance to the Person
constructor.
To see how to inject the DataSource
instance refer to
Building the mapper.
You can also provide one or more Factory using the withFactory
Mapper annotation parameter.
The Factory instance can be injected using Selma builder see
Building the mapper.
This mapper will call the given MyFactory
to build new Person
by calling the
generic newSourceBeansInstance
method. You can also specify methods like
newCity
to statically provide new instances of the City
bean.
Selma will always first choose matching static typed factory methods. If none are found, Selma will
choose the generic method that matches with the closest type boundaries.
Now that you've define the mapping contract you need, you probably want to retrieve the generated mapper
instance. All you need to know for this is the Selma
class. It provides a builder API to
build and configure your mapper instance.
Here we build a PersonMapper
, providing to it custom mappers and sources.
PersonMapper
mapper interface.withCustom(...)
to provide our custom mappers.
withSource(...)
to provide sources to our sourced
beans.withFactories(...)
to provide Factories to our Mapper.disableCache()
to bypass mapper instance cache and force Selma to build a
new mapper instance .
If you are using Spring IoC framework, you can configure your mapper interface to be configured as a Spring service.
Selma will use a @Service
to annotate the generated mapper class and it will use @Autowired
for all claimed custom mappers and factories. The only thing you have to do is configure Spring to scan for annotations in the Mapper
package.
Here we declared a AddressMapper
, which will be annotated using Spring annotations. You can
inject the generated mapper inside your Java beans or services.
The withIoC
parameter of the mapper has three options to handle CDI annotations.
The CDI
value generates a @Named
bean.
The CDI_SINGLETON
value generates a @Named
and @Singleton
bean.
The CDI_APPLICATION_SCOPED
value generates a @ApplicationScoped
bean.
Here we declared three different mappers, to demonstrate the use of the three possible CDI values.
Selma can be used from Java 6 to 8.
Old releases of the maven compiler plugin tend to badly support annotation processor messages. We use successfully version 3.2.
You can also use the annotation processor maven plugin if you can not move to maven compiler 3.2.
You can find help about configuring eclipse here.
You can retrieve snapshots builds from 0.16-SNAPSHOT in the sonatype snapshot repository. For this to work, you should add Sonatype snaphot repository in your maven settings.xml or pom.xml