While repositories are primarily intended to work with Entities, it
might be preferable in some cases to have an additional mapping layer on
top of them, for example because the Entities are quite complex but the service
layer needs only a limited view on it, or because the Entities are
exposed over a remote interface and there should not be a 1:1 view on
the domain model.
DeltaSpike Data allows to directly plugin in such a mapping mechanism
without the need to specify additional mapping methods:
@Repository(forEntity = Person.class)
@MappingConfig(PersonDtoMapper.class)
public interface PersonRepository
{
PersonDto findBySsn(String ssn);
List<PersonDto> findByLastName(String lastName);
}
The PersonDtoMapper
class has to implement the QueryInOutMapper
interface:
public class PersonDtoMapper implements QueryInOutMapper<Person>
{
@Override
public Object mapResult(Person result)
{
...
}
...
@Override
public Object mapResultList(List<Person> result)
{
...
}
@Override
public boolean mapsParameter(Object parameter)
{
return parameter != null && (
parameter instanceof PersonDto || parameter instanceof PersonId);
}
@Override
public Object mapParameter(Object parameter)
{
...
}
}
The mapper can also be used to transform query parameters. Parameters
are converted before executing queries and calling repository
extensions.
Note that those mapper classes are treated as CDI Beans, so it is
possible to use injection in those beans (e.g. you might inject an
EntityManager
or other mappers). As the @MappingConfig
refers to the
mapper class directly, the mapper must be uniquely identifiable by its
class.
It is also possible to combine mappings with the base Repository classes:
@Repository(forEntity = Person.class)
@MappingConfig(PersonDtoMapper.class)
public interface PersonRepository extends EntityRepository<PersonDto, PersonId>
{
...
}
In this case, the forEntity
attribute in the @Repository
annotation
is mandatory. Also it is up to the mapper to convert parameters
correctly (in this example, a conversion from a PersonDto
parameter to
Person
entity and from PersonId
to Long
is necessary).
Simple Mappings
In many cases it is just required to map a DTO object back and forth. For
this case, the SimpleQueryInOutMapperBase
class can be subclassed,
which only requires to override three methods:
public class PersonMapper extends SimpleQueryInOutMapperBase<Person, PersonDto>
{
@Override
protected Object getPrimaryKey(PersonDto dto)
{
return dto.getId();
}
@Override
protected PersonDto toDto(Person entity)
{
...
}
@Override
protected Person toEntity(Person entity, PersonDto dto) {
...
return entity;
}
}
The first method, getPrimaryKey
, identifies the primary key of an
incoming DTO (this might need mapping too). If there is a primary key in
the DTO, Data tries to retrieve the Entity and feed it to the toEntity
method, so the entity to be mapped is attached to the persistence
context. If there is no primary key, a new instance of the Entity is
created. In any case, there is no need to map the primary key to the
entity (it either does not exist or is already populated for an existing
entity).