Técnicas de Mapeamento de Entidades e Consultas Dinâmicas Multi-Schema com Spring Boot

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.

Tags: Spring Boot MyBatis java DTO Mapping Dynamic SQL

Publicado em 6-21 23:07