domingo, 15 de fevereiro de 2026

Gerando Relatórios PDF no Java EE com iReport 5.6 e DTOs (JavaBeans)

        Olá, nesse artigo vou falar sobre geração de relatórios com IReport a partir de dados que já estão na memória (objetos DTO) em vez de fazer uma consulta SQL direta no Jasper. Para isso, utilizamos o JRBeanCollectionDataSource.

        Vamos ver primeiro as dependencias do POM:

<!-- Core do Jasper -->
    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>5.6.0</version>
    </dependency>
    <!-- Necessário para ler os campos do DTO -->
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.4</version>
    </dependency>
    <!-- Motor de exportação PDF (Jasper 5.6 usa essa versão) -->
    <dependency>
        <groupId>com.lowagie</groupId>
        <artifactId>itext</artifactId>
        <version>2.1.7</version>
    </dependency>

        O IReport gera dois arquivos:

.jrxml: É o seu arquivo de edição (código-fonte XML). É onde você desenha no iReport.

.jasper: É o arquivo compilado. Dica: Sempre coloque o .jasper na pasta de recursos (src/main/resources) do seu projeto Java para que a aplicação o encontre facilmente.

        Configurando o iReport para reconhecer o DTO:

Para que os campos apareçam no designer:

  1. Vá em Tools -> Options -> iReport -> Classpath e adicione a pasta target/classes do seu projeto.
  2. No Report Inspector, clique com o botão direito em Fields -> Add Field.
  3. O nome do campo deve ser idêntico ao atributo da classe Java (ex: se no Java é private String nomeCliente, no iReport o campo deve se chamar nomeCliente).
        Como lidar com Logos e Backgrounds

        No ambiente Java EE, o relatório não sabe "onde ele está" a menos que você diga. Para usar caminhos como images/logo.png direto no .jrxml, faça o seguinte:

        No iReport (Designer) crie um parâmetro chamado IMAGENS_DIR (tipo String). No campo da imagem, use a expressão:

$P{IMAGENS_DIR} + "logo.png"

        No Java você extrai o caminho da pasta de relatórios dinamicamente:

// Obtém o caminho físico da pasta de relatórios dentro do seu WAR
String pathRelatorios = getServletContext().getRealPath("/WEB-INF/imagens/");

Map<String, Object> parametros = new HashMap<>();
// Passa esse caminho base para o Jasper
parametros.put("IMAGENS_DIR", pathRelatorios + File.separator);

        Outra opção é usando o InputStream, passá-la como um InputStream via parâmetro. 
        
        No iReport:
  1. Vá em Parameters -> Add Parameter. Nomeie como LOGO_IMG.
  2. Mude a Parameter Class para java.io.InputStream.
  3. Arraste um elemento de Image para o relatório. No campo Image Expression, digite: $P{LOGO_IMG}.
        No Java carregue a imagem da sua pasta de recursos (src/main/resources/images/logo.png) e envie para o Jasper:

InputStream logo = this.getClass().getResourceAsStream("/images/logo.png");
parametros.put("LOGO_IMG", logo);


        O Código Java de geração do Relatório

        Aqui está o restante do código Java para a geração do relatório:

// Centralizando a lógica de criação do JasperPrint
private JasperPrint prepararRelatorio(List<MeuDTO> listaDados, Map<String, Object> parametros) throws JRException {

    // Carrega o arquivo compilado dos resources
    InputStream relatorioStream = this.getClass().getResourceAsStream("/relatorios/meu_arquivo.jasper");
    
    // Transforma a lista de objetos no DataSource do Jasper
    JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(listaDados);

    // Gere os parametros necessários
    Aqui você pega o caminho relativo da imagem por exemplo
    
    // Une o layout (.jasper) + Parâmetros + Dados (DTOs)
    return JasperFillManager.fillReport(relatorioStream, parametros, ds);
}

// Método para enviar ao navegador
public void emitirRelatorio(HttpServletResponse response, List<MeuDTO> lista, boolean baixar) {

    try {
        JasperPrint jp = prepararRelatorio(lista, new HashMap<>());
        
        // Define o tipo de saída (PDF)
        response.setContentType("application/pdf");
        
        // Define se abre no navegador ou faz download
        String modo = baixar ? "attachment" : "inline";
        response.setHeader("Content-Disposition", modo + "; filename=\"relatorio.pdf\"");
        
        // Escreve o PDF na resposta HTTP
        JasperExportManager.exportReportToPdfStream(jp, response.getOutputStream());
        
        response.getOutputStream().flush();
        response.getOutputStream().close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}



Dica: Se usarem uma versão do Java muito nova (Java 17+), o Jasper 5.6 pode precisar de flags extras no servidor (--add-opens), pois ele é uma ferramenta antiga que acessa classes internas do Java.




segunda-feira, 5 de janeiro de 2026

Trace Id - O que é e como implementar

        Nesse artigo não vou trazer uma implementação via código do Trace Id, mas o como implementar vai estar ligado a ideia de o que você precisa saber para implementar. 

        Um Trace Id é um Id único que percorre todas as etapas de uma requisição em microsserviços, devendo passar por todos os microsserviços compondo seu log. O Objetivo: Se der um erro, precisamos saber o fluxo completo da requisição para ajudar a identificar a causa raiz, os impactos e se os fluxos de rollback relacionados ao erro deram certo (no caso de um SAGA por exemplo, embora o SAGA tenha o SAGA ID), entre outras coisas. Qual a mensagem origem? Como eram seus dados? Sempre que perguntarmos por algo o Trace Id vai estar relacionado com essa pergunta.

        Para manter o fluxo do Trace Id você vai ter que:

  • Usar filtros para pegá-lo assim que receber a requisição web em todas as APIs;
  • Usar Interceptors para colocá-lo antes de enviar chamadas de uma API para outra;
  • Usar interceptors para o Kafka para colocá-lo no header do producer (ou alguma forma manual) andes de enviar
  • Pegar no @Listener do Kafka para setar no projeto
  • Salvar em bancos que guardam estado (bancos que salvam dados em seus estágios intermediários entre o processamento(Outbox Pattern ou outros)
  • Salvar em tabelas de auditoria (dependendo do caso)
  • Usar em logs
        É algo que vai interferir em todo o projeto, se esquecer em uma etapa fica um buraco de difícil solução. Então implementar requer cuidado, atenção, paciência e teste.

        Fica mais complicado se trabalhar com:
  • Chamada Assíncronas
  • Startar outras threads
  • WebFlux
  • Kafka Consumer
        É preciso estudar o cenário e opções corretas para se ter uma boa implementação pois caso contrário vai falhar. É o tipo de coisa que o conceito a simples, a implementação nem tanto.


sábado, 3 de janeiro de 2026

Padrão Brasileiro para Cálculos Financeiros - Bancos, Fintechs e afins em Java

        Em sistemas financeiros, precisão é uma exigência legal, contábil e auditável. Os tipos primitivos como double e float utilizam representação binária, o que gera erros de precisão. Então o padrão para se trabalhar com valores monetários é o BigDecimal, pois ele traz: precisão decimal exata, controle explícito de escala e controle explícito de arredondamento.

        O padrão para lidar com valores é:

  • Uso de BigDecimal
  • Escala fixa de 4 casas decimais (mesmo que só exiba duas usa 4 nos cálculos)
  • Arredondamento HALF_EVEN (arredondamento bancário)
  • Usar numeric no Banco de Dados (Numeric(19,4))

        Porém para se trabalhar corretamente com BigDecimal é preciso obedecer algumas regras:

  • Nunca iniciar o construtor a partir de um double ou float:

new BigDecimal(10.25);  // errado, faz o valor entrar nos erros de cálculo de repres. binária

  • Existem duas formas para inicializar:

new BigDecimal("10.25");     // A partir de String

BigDecimal.valueOf(10.25);  // Usando valueOf

// criando valores corretamente

BigDecimal valor = new BigDecimal("123.456789")

        .setScale(4, RoundingMode.HALF_EVEN);


  • O arredondamento é o RoundingMode.HALF_EVEN

Exemplo:

new BigDecimal("2.345").setScale(2, RoundingMode.HALF_EVEN); // 2.34 new BigDecimal("2.355").setScale(2, RoundingMode.HALF_EVEN); // 2.36

  • Não usar o equals para fazer comparações entre os objetos. O equals vai comparar as instancias.
    Use o "compareTo".
new BigDecimal("10.0").equals(new BigDecimal("10.00")); // false

new BigDecimal("10.0").compareTo(new BigDecimal("10.0"))

Retorna:         -1 → menor que         0 → igual         1 → maior que

        E a partir daí é usar o que a classe traz de métodos:

Soma:

BigDecimal resultado = valor1.add(valor2).setScale(4, RoundingMode.HALF_EVEN);

Subtração:

BigDecimal resultado = valor1.subtract(valor2).setScale(4, RoundingMode.HALF_EVEN);

Multiplicação:

BigDecimal resultado = valor1.multiply(valor2).setScale(4, RoundingMode.HALF_EVEN);

Divisão:

BigDecimal parcela = valor1.divide( valor2, 4, // escala RoundingMode.HALF_EVEN );


        Um exemplo de uma classe para trabalhar com dinheiro:


public class Money { private final BigDecimal value; public Money(BigDecimal value) { this.value = value.setScale(4, RoundingMode.HALF_EVEN); } public static Money of(String value) { return new Money(new BigDecimal(value)); } public Money add(Money other) { return new Money(this.value.add(other.value)); } public Money subtract(Money other) { return new Money(this.value.subtract(other.value)); } public Money multiply(BigDecimal factor) { return new Money(this.value.multiply(factor)); } public BigDecimal getValue() { return value; } }