Pular para o conteúdo principal

archbase-mapper

Módulo para mapeamento objeto-objeto entre entidades e DTOs.

Instalação

<dependency>
<groupId>br.com.archbase</groupId>
<artifactId>archbase-mapper</artifactId>
<version>${archbase.version}</version>
</dependency>

Configuração

archbase:
mapper:
enabled: true
strict-mapping: true # Lança exceção se campo não mapeado

ArchbaseMapper

Interface base para mappers customizados:

import br.com.archbase.mapper.ArchbaseMapper;

@Mapper
public interface PedidoMapper extends ArchbaseMapper<Pedido, PedidoDTO> {

@Override
Pedido toEntity(PedidoDTO dto);

@Override
PedidoDTO toDto(Pedido entity);

@Mapping(target = "itens", ignore = true)
Pedido toEntitySemItens(PedidoDTO dto);

default List<ItemPedidoDTO> toItemList(List<ItemPedido> itens) {
return itens.stream()
.map(this::toItemDto)
.collect(Collectors.toList());
}

@Mapping(target = "produto", source = "produtoId")
ItemPedidoDTO toItemDto(ItemPedido item);
}

Usando o Mapper

@Service
public class PedidoService {

private final PedidoRepository repository;
private final PedidoMapper mapper;

public PedidoDTO criar(CriarPedidoRequest request) {
Pedido pedido = mapper.toEntity(request.toDto());
pedido.validar();
Pedido salvo = repository.save(pedido);
return mapper.toDto(salvo);
}

public Page<PedidoDTO> listar(Pageable pageable) {
return repository.findAll(pageable)
.map(mapper::toDto);
}
}

Mapeamento com ModelMapper

Se o ModelMapper estiver no classpath, ele será usado automaticamente:

@Configuration
public class MapperConfig {

@Bean
public ArchbaseMapper<Object, Object> modelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(PRIVATE);

return new ModelMapperArchbaseMapper(modelMapper);
}
}

Mapeamento com MapStruct

Adicione a dependência:

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>

Crie o mapper:

@Mapper(componentModel = "spring")
public interface ClienteMapper extends ArchbaseMapper<Cliente, ClienteDTO> {
// Implementação gerada automaticamente
}

Mapeamento Condicional

@Mapper
public interface ProdutoMapper extends ArchbaseMapper<Produto, ProdutoDTO> {

@Mapping(target = "precoComDesconto",
expression = "java(entity.getPreco() * (1 - entity.getDesconto()))")
ProdutoDTO toDto(Produto entity);

@Mapping(target = "estoque",
condition = "java(entity.isExibirEstoque())")
ProdutoDTO toDtoComEstoque(Produto entity);
}

Collections

@Mapper
public interface ClienteMapper extends ArchbaseMapper<Cliente, ClienteDTO> {

default List<ClienteDTO> toDtoList(List<Cliente> entidades) {
return entidades.stream()
.map(this::toDto)
.collect(Collectors.toList());
}

default Set<ClienteDTO> toDtoSet(Set<Cliente> entidades) {
return entidades.stream()
.map(this::toDto)
.collect(Collectors.toSet());
}
}

Testes de Mapper

@SpringBootTest
class ClienteMapperTest {

@Autowired
private ClienteMapper mapper;

@Test
void deveMapearParaDto() {
Cliente cliente = new Cliente("João Silva", "joao@email.com");

ClienteDTO dto = mapper.toDto(cliente);

assertThat(dto.getNome()).isEqualTo("João Silva");
assertThat(dto.getEmail()).isEqualTo("joao@email.com");
}

@Test
void deveMapearParaEntidade() {
ClienteDTO dto = new ClienteDTO("João Silva", "joao@email.com");

Cliente cliente = mapper.toEntity(dto);

assertThat(cliente.getNome()).isEqualTo("João Silva");
assertThat(cliente.getEmail()).isEqualTo("joao@email.com");
}
}

Exceção MappingException

Exceção lançada quando ocorre erro no mapeamento:

try {
ClienteDTO dto = mapper.toDto(cliente);
} catch (MappingException e) {
// Trata erro de mapeamento
logger.error("Erro ao mapear cliente: {}", e.getMessage());
throw new BusinessException("Erro ao processar dados do cliente");
}

Causas comuns:

  • Tipos incompatíveis entre origem e destino
  • Campos não mapeados com strict-mapping: true
  • Erro de conversão de tipos complexos
  • Null pointer em cadeias de mapeamento

Auto-configuração

O módulo archbase-mapper oferece auto-configuração via Spring Boot:

ArchbaseMapperAutoConfiguration

Configuração automática é ativada quando o módulo está no classpath:

archbase:
mapper:
enabled: true
strict-mapping: true # Lança exceção se campo não mapeado

Configuração Programática

@Configuration
public class MapperConfiguration {

@Bean
public ArchbaseMapper<Object, Object> modelMapper() {
ModelMapper modelMapper = new ModelMapper();

// Configurações de mapeamento
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(PRIVATE)
.setSkipNullEnabled(true)
.setDeepCopyEnabled(false);

return new ModelMapperArchbaseMapper(modelMapper);
}
}

Integração com MapStruct

@Mapper(
componentModel = "spring",
uses = { /* outros mappers */ }
)
public interface ClienteMapper extends ArchbaseMapper<Cliente, ClienteDTO> {
// Implementação gerada automaticamente em tempo de compilação
}

Próximos Passos