Explorar os arquivos de configuração e mapeamento no MyBatis é fundamental para otimizar a interação com o banco de dados. Este guia detalha aspectos essenciais, desde a gestão de parâmetros até estratégias avançadas de mapeamento e carregamento de dados.
Arquivos de Mapeamento (Mapper XML)
Nos arquivos mapper.xml, cada instrução SQL definida é encapsulada em um objeto MappedStatement. O MyBatis gerencia essas instruções através de uma identificação única, que é uma combinação do namespace definido no arquivo mapper e o id da instrução SQL.
Variáveis em Consultas SQL
MyBatis oferece duas formas principais de incluir valores dinâmicos nas suas consultas:
#{propertyName}: Este é o método preferencial. Ele insere o valor do parâmetro de forma segura, preparando a instrução (preparedStatement) e substituindo o placeholder. Isso previne ataques de injeção de SQL e lida automaticamente com os tipos de dados.${propertyName}: Insere o valor do parâmetro diretamente na string SQL sem qualquer tratamento. Deve ser usado com cautela, principalmente para injeção de nomes de tabelas, colunas ou partes da cláusula ORDER BY, onde o valor é conhecido e confiável.
Estratégias de Geração de Chaves Primárias
A gestão de chaves primárias é um aspecto crucial na inserção de dados. O MyBatis oferece flexibilidade para diversas estratégias:
UUID como Chave Primária (MySQL)
Para gerar um UUID antes da inserção e usá-lo como chave primária:
<insert id="inserirRegistroUsuario" parameterType="com.exemplo.dominio.Usuario">
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
SELECT UUID()
</selectKey>
INSERT INTO Usuarios(id, nome_usuario, data_nasc, sexo, endereco)
VALUES(#{id}, #{nomeUsuario}, #{dataNasc}, #{sexo}, #{endereco})
</insert>
Retorno de Chaves Auto-Incrementadas (MySQL)
Para obter o ID gerado por uma coluna auto-incrementada após a inserção:
<insert id="adicionarNovoUsuario" parameterType="com.exemplo.dominio.Usuario">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO Usuarios(nome_usuario, data_nasc, sexo, endereco)
VALUES(#{nomeUsuario}, #{dataNasc}, #{sexo}, #{endereco})
</insert>
Utilizando Sequências (Oracle)
No Oracle, sequências são usadas para gerar chaves primárias. O valor é obtido antes da inserção:
<insert id="criarNovoUsuarioOracle" parameterType="com.exemplo.dominio.Usuario">
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Integer">
SELECT MINHA_SEQUENCE.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO Usuarios(id, nome_usuario, data_nasc, sexo, endereco)
VALUES(#{id}, #{nomeUsuario}, #{dataNasc}, #{sexo}, #{endereco})
</insert>
Mapeamento de Resultados (resultMap)
O resultMap é essencial quando os nomes das colunas do banco de dados não correspondem diretamente aos nomes das propriedades no seu JavaBean (POJO). Embora o MyBatis possa realizar o mapeamento automático para nomes correspondentes, resultMap oferece controle total sobre como os resultados da consulta são mapeados para seus objetos Java.
Diferenças entre resultType e resultMap
resultType: Utilizado para mapear os resultados de uma consulta diretamente para um tipo específico (POJO, tipo primitivo, HashMap). Funciona bem quando os nomes das colunas no SQL são idênticos aos nomes das propriedades do POJO. Se houver divergência, o mapeamento falha para essas colunas.resultMap: Fornece um mapeamento explícito entre nomes de colunas do banco de dados e nomes de propriedades do POJO. É indispensável para cenários onde há diferença nos nomes, mapeamento de relacionamentos complexos (um para um, um para muitos), ou quando se deseja um controle mais fino sobre o processo de mapeamento.
Uso de resultMap com Relações Complexas: association e collection
Para mapear relacionamentos entre entidades, como "um para um" ou "um para muitos", o resultMap utiliza as tags <association> e <collection>.
<association>: Usado para mapear um relacionamento "um para um", onde o resultado da consulta é mapeado para uma propriedade de objeto único dentro do seu POJO principal.<collection>: Utilizado para mapear um relacionamento "um para muitos", onde o resultado da consulta é mapeado para uma lista (ou outra coleção) de objetos dentro do seu POJO principal.
Exemplo de Mapeamento com Collection: Pedido e Detalhes
Considere uma relação onde um Pedido pode ter múltiplos DetalhePedido.
Classe POJO Pedido
package com.exemplo.dominio;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class Pedido implements Serializable {
private Integer idPedido;
private Integer idCliente;
private String numeroPedido;
private Date dataCriacao;
private String observacao;
// Relação um para um com Cliente
private Cliente cliente;
// Relação um para muitos com DetalhePedido
private List<DetalhePedido> detalhes;
// Getters e Setters
public Integer getIdPedido() { return idPedido; }
public void setIdPedido(Integer idPedido) { this.idPedido = idPedido; }
public Integer getIdCliente() { return idCliente; }
public void setIdCliente(Integer idCliente) { this.idCliente = idCliente; }
public String getNumeroPedido() { return numeroPedido; }
public void setNumeroPedido(String numeroPedido) { this.numeroPedido = numeroPedido; }
public Date getDataCriacao() { return dataCriacao; }
public void setDataCriacao(Date dataCriacao) { this.dataCriacao = dataCriacao; }
public String getObservacao() { return observacao; }
public void setObservacao(String observacao) { this.observacao = observacao; }
public Cliente getCliente() { return cliente; }
public void setCliente(Cliente cliente) { this.cliente = cliente; }
public List<DetalhePedido> getDetalhes() { return detalhes; }
public void setDetalhes(List<DetalhePedido> detalhes) { this.detalhes = detalhes; }
}
Consulta SQL
SELECT
p.id AS id_pedido,
p.id_cliente,
p.numero_pedido,
p.data_criacao,
p.observacao,
c.nome AS nome_cliente,
c.email,
dp.id AS id_detalhe,
dp.quantidade,
dp.id_produto
FROM
pedidos p
JOIN
clientes c ON p.id_cliente = c.id
JOIN
detalhes_pedido dp ON p.id_pedido = dp.id_pedido;
resultMap para Pedido com Detalhes
Para mapear os detalhes do pedido em uma lista, usamos <collection>:
<resultMap type="com.exemplo.dominio.Pedido" id="mapeamentoPedidoComDetalhes">
<id column="id_pedido" property="idPedido"/>
<result column="id_cliente" property="idCliente"/>
<result column="numero_pedido" property="numeroPedido"/>
<result column="data_criacao" property="dataCriacao"/>
<result column="observacao" property="observacao"/>
<!-- Mapeamento da relação um para um com Cliente -->
<association property="cliente" javaType="com.exemplo.dominio.Cliente">
<id column="id_cliente" property="id"/>
<result column="nome_cliente" property="nome"/>
<result column="email" property="email"/>
</association>
<!-- Mapeamento da relação um para muitos com DetalhePedido -->
<collection property="detalhes" ofType="com.exemplo.dominio.DetalhePedido">
<id column="id_detalhe" property="id"/>
<result column="quantidade" property="quantidade"/>
<result column="id_produto" property="idProduto"/>
</collection>
</resultMap>
Manipulação de Caracteres Especiais em Mappers
Quando as instruções SQL contêm caracteres especiais como < (menor que) ou > (maior que), que são reservados em XML, você pode usar entidades XML (<, >) ou blocos CDATA para evitar erros de parser:
<select id="filtrarDados" resultType="java.util.Map">
<![CDATA[
SELECT * FROM minha_tabela WHERE valor > 10 AND data < '2023-01-01'
]]>
</select>
Configurações Globais (mybatis-config.xml)
Definição de Aliases de Tipo (typeAliases)
Aliases simplificam o uso de nomes de classes longos no XML. Você pode definir aliases individualmente ou escanear um pacote para que todas as classes nesse pacote tenham aliases automaticamente (gearlmente o nome da classe, com a primeira letra minúscula).
<configuration>
<typeAliases>
<!-- Alias individual -->
<typeAlias type="com.exemplo.dominio.Usuario" alias="usuarioPojo"/>
<!-- Escaneia um pacote para definir aliases automaticamente -->
<package name="com.exemplo.dominio"/>
</typeAliases>
...
</configuration>
Carregamento de Mappers
Os arquivos de mapeamento SQL (mappers) precisam ser registrados na configuração global do MyBatis. Existem várias formas de fazer isso:
<configuration>
<mappers>
<!-- Carregamento por caminho do recurso (XML) -->
<mapper resource="mappers/UsuarioMapper.xml"/>
<!-- Carregamento pela interface do mapper (requer que o XML e a interface tenham o mesmo nome e estejam no mesmo diretório) -->
<mapper class="com.exemplo.mappers.InterfaceUsuarioMapper"/>
<!-- Escaneamento de um pacote para encontrar interfaces mapper -->
<package name="com.exemplo.mappers"/>
</mappers>
...
</configuration>
Carregamento Lento (Lazy Loading)
O carregamento lento é uma técnica para otimizar o desempenho, onde dados relacionados (como um objeto associado) são carregados do banco de dados apenas quando são acessados pela primeira vez, e não junto com o objeto principal. Isso é particularmente útil para reduzir o número de consultas iniciais.
Configuração Global de Carregamento Lento
Você pode habilitar o carregamento lento globalmente no arquivo mybatis-config.xml:
<configuration>
<settings>
<!-- Habilita o carregamento lento -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- Desabilita o carregamento agressivo (carrega tudo assim que acessa o objeto pai) -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
...
</configuration>
Configuração de Carregamento Lento via resultMap
Para aplicar o carregamento lento a uma associação específica (e.g., carregar um objeto Cliente apenas quando ele é acessado a partir de um objeto Pedido):
Consulta de Pedidos (inicial)
<select id="buscarPedidosComClienteLazy" resultMap="mapaPedidoClienteLazy">
SELECT id AS id_pedido, id_cliente, numero_pedido, data_criacao, observacao
FROM pedidos
</select>
resultMap com <association> para Carregamento Lento
Neste resultMap, a associação com Cliente é configurada para carregamento lento. O atributo select aponta para outro statement que buscará o cliente, e column passa o ID do cliente como parâmetro.
<resultMap type="com.exemplo.dominio.Pedido" id="mapaPedidoClienteLazy">
<id column="id_pedido" property="idPedido"/>
<result column="id_cliente" property="idCliente"/>
<result column="numero_pedido" property="numeroPedido"/>
<result column="data_criacao" property="dataCriacao"/>
<result column="observacao" property="observacao"/>
<!-- Configura o carregamento lento para a propriedade 'cliente' -->
<association property="cliente"
select="com.exemplo.mappers.InterfaceClienteMapper.buscarClientePorId"
column="id_cliente" fetchType="lazy"/>
</resultMap>
Quando a propriedade getCliente() é invocada no objeto Pedido, o MyBatis executará a consulta buscarClientePorId usando o id_cliente como parâmetro para carregar os dados do cliente.