Utilitário para Conversão de Modelos e DTOs
No desenvolvimento de APIs robustas, a conversão frequente entre entidades de banco de dados e objetos de transferência de dados (DTOs) é uma tarefa comum. Para otimizar esse processo, podemos implementar uma classe utilitária baseada no BeanUtils do Spring e na API de Streams do Java.
package com.projeto.infra.utils;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* Utilitário para transformação de instâncias entre diferentes classes de modelo.
*/
public final class ObjectTransformer {
private ObjectTransformer() {
// Construtor privado para evitar instanciação
}
/**
* Instancia uma nova classe de destino e copia as propriedades do objeto de origem.
*/
public static <T> T transform(Object source, Class<T> targetClazz) {
if (source == null) {
return null;
}
try {
T targetInstance = targetClazz.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(source, targetInstance);
return targetInstance;
} catch (Exception e) {
throw new RuntimeException("Erro ao converter objeto: " + targetClazz.getName(), e);
}
}
/**
* Converte uma coleção de objetos para uma lista do tipo especificado.
*/
public static <T> List<T> transformList(Collection<?> sourceList, Class<T> targetClazz) {
if (sourceList == null || sourceList.isEmpty()) {
return new ArrayList<>();
}
return sourceList.stream()
.map(item -> transform(item, targetClazz))
.collect(Collectors.toList());
}
}
Implementação de Consultas Dinâmicas Multi-Schema
Em cenários onde os dados estão distribuídos em múltiplos esquemas (databases) dentro da mesma instância, é possível realizar consultas unificadas utilizando configurações externas e o recurso de foreach do MyBatis.
1. Configuração Externa
Defina as informações de conexão ou esquemas em um arquivo de propriedades, como external-config.properties:
app.node-mapping=schema_vendas,101;schema_estoque,102;schema_logistica,103
2. Mapeamento da Configuração no Spring
Crie uma classe de configuração para ler esses parâmetros dinamicamente:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@Data
@PropertySource("classpath:/external-config.properties")
@ConfigurationProperties(prefix = "app")
public class MultiSchemaProperties {
private String nodeMapping;
}
3. Lógica de Serviço e Processamento de Dados
O serviço processa a string de configuração e delega a execução ao Mapper:
@Service
@Slf4j
public class DashboardServiceImpl implements DashboardService {
@Resource
private InventoryMapper inventoryMapper;
@Resource
private MultiSchemaProperties schemaProperties;
@Override
public ResponseDTO getGlobalMetrics() {
try {
List<SchemaContext> activeSchemas = new ArrayList<>();
String mappingStr = schemaProperties.getNodeMapping();
if (mappingStr != null) {
for (String entry : mappingStr.split(";")) {
String[] parts = entry.split(",");
activeSchemas.add(new SchemaContext(parts[0], parts[1]));
}
}
Map<String, Object> params = new HashMap<>();
params.put("currentDate", LocalDate.now());
params.put("activeFlag", 1);
List<MetricResult> results = inventoryMapper.fetchUnifiedMetrics(activeSchemas, params);
// Agregação lógica dos resultados
int totalStock = results.stream().mapToInt(MetricResult::getQuantity).sum();
return ResponseDTO.success(totalStock);
} catch (Exception e) {
log.error("Falha ao processar métricas globais", e);
return ResponseDTO.fail("Erro interno no processamento.");
}
}
}
4. Integração com MyBatis (XML Mapper)
Abaixo, a estrutura do XML para realizar a operação de UNION entre os diferentes esquemas passados dinamicamente:
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.projeto.repository.InventoryMapper">
<select id="fetchUnifiedMetrics" resultType="com.projeto.model.MetricResult">
<foreach collection="schemas" item="schema" separator=" UNION ">
SELECT
#{schema.nodeId} as nodeId,
COUNT(1) as totalItems,
SUM(stock_level) as quantity
FROM `${schema.schemaName}`.tb_inventory_items
WHERE status = #{params.activeFlag}
AND last_update >= #{params.currentDate}
</foreach>
</select>
</mapper>
Note que o uso de ${schema.schemaName} é necessário para injetar o nome da base de dados diretamente no SQL, pois o nome do esquema não pode ser passado como um parâmetro preparado (#{}) tradicional.