MyBatis: Configuração de Mapeadores com Anotações e SQL Dinâmico

Ao utilizar MyBatis, é possível configurar mapeadores SQL diretamente no código Java através de anotações. Uma funcionalidade particularmente útil é a capacidade de gerar SQL dinamicamente, permitindo consultas mais flexíveis sem a necessidade de arquivos XML separados. Abaixo, exploramos como implementar SQL dinâmico usando as anotações do MyBatis.

Entidade do Modelo

Primeiro, definimos a classe de entidade que representa os dados do estudante:


package com.example.model;

public class Student {
   private Integer id;
   private String name;
   private Integer age;

   // Construtor padrão
   public Student() {
   }

   // Construtor para criação de novos estudantes
   public Student(String name, Integer age) {
       this.name = name;
       this.age = age;
   }

   // Getters e Setters
   public Integer getId() {
       return id;
   }
   public void setId(Integer id) {
       this.id = id;
   }
   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
   public Integer getAge() {
       return age;
   }
   public void setAge(Integer age) {
       this.age = age;
   }

   @Override
   public String toString() {
       return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
   }
}
   

Provedor de SQL Dinâmico

Criamos uma classe provedora de SQL para encapsular a lógica de geração dinâmica de instruções SQL. Esta classe utiliza a classe org.apache.ibatis.jdbc.SQL para construir as consultas programaticamente.


package com.example.mapper;

import java.util.Map;
import org.apache.ibatis.jdbc.SQL;
import com.example.model.Student;

public class StudentDynaSqlProvider {

   // Método para inserção condicional de estudante
   public String insertConditional(final Student student) {
       return new SQL() {{
           INSERT_INTO("t_student");
           if (student.getName() != null) {
               VALUES("name", "#{name}");
           }
           if (student.getAge() != null) {
               VALUES("age", "#{age}");
           }
       }}.toString();
   }

   // Método para atualização condicional de estudante
   public String updateConditional(final Student student) {
       return new SQL() {{
           UPDATE("t_student");
           if (student.getName() != null) {
               SET("name = #{name}");
           }
           if (student.getAge() != null) {
               SET("age = #{age}");
           }
           WHERE("id = #{id}");
       }}.toString();
   }

   // Método para exclusão de estudante por ID
   public String deleteById() {
       return new SQL() {{
           DELETE_FROM("t_student");
           WHERE("id = #{id}");
       }}.toString();
   }

   // Método para buscar estudante por ID
   public String selectById() {
       return new SQL() {{
           SELECT("*");
           FROM("t_student");
           WHERE("id = #{id}");
       }}.toString();
   }

   // Método para busca de estudantes com múltiplos critérios
   public String findByCriteria(final Map<string object=""> criteria) {
       return new SQL() {{
           SELECT("*");
           FROM("t_student");
           if (criteria.containsKey("name") && criteria.get("name") != null) {
               WHERE("name LIKE #{name}"); // Note: '%' deve ser passado no valor do map
           }
           if (criteria.containsKey("age") && criteria.get("age") != null) {
               WHERE("age = #{age}");
           }
       }}.toString();
   }
}
   </string>

Interface do Mapeador com Anotações

A interface do mapeador utiliza as anotações @InsertProvider, @UpdateProvider, @DeleteProvider e @SelectProvider para vincular os métodos aos métodos correspondentes na classe provedora de SQL.


package com.example.mapper;

import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import com.example.model.Student;

public interface StudentMapper {

   @InsertProvider(type = StudentDynaSqlProvider.class, method = "insertConditional")
   int insertStudent(Student student);

   @UpdateProvider(type = StudentDynaSqlProvider.class, method = "updateConditional")
   int updateStudent(Student student);

   @DeleteProvider(type = StudentDynaSqlProvider.class, method = "deleteById")
   int deleteStudent(int id);

   @SelectProvider(type = StudentDynaSqlProvider.class, method = "selectById")
   Student getStudentById(Integer id);

   @SelectProvider(type = StudentDynaSqlProvider.class, method = "findByCriteria")
   List<student> findStudents(Map<string object=""> criteria);
}
   </string></student>

Exemplo de Teste

O código de teste demonstra como utilizar os métodos do mapeador para innteragir com o banco de dados, incluindo inserção, atualização, exclusão e consultas com critérios dinâmicos.


package com.example.test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.example.mapper.StudentMapper;
import com.example.model.Student;
import com.example.util.SqlSessionFactoryUtil; // Assumindo que esta classe gerencia a SqlSessionFactory

public class StudentTest {
   private static Logger logger = Logger.getLogger(StudentTest.class);

   private SqlSession sqlSession = null;
   private StudentMapper studentMapper = null;

   @Before
   public void setUp() throws Exception {
       // Abre uma nova sessão usando a fábrica de sessões
       sqlSession = SqlSessionFactoryUtil.openSession();
       studentMapper = sqlSession.getMapper(StudentMapper.class);
   }

   @After
   public void tearDown() throws Exception {
       // Fecha a sessão
       if (sqlSession != null) {
           sqlSession.close();
       }
   }

   @Test
   public void testInsertStudent() {
       logger.info("Testando inserção de estudante...");
       Student newStudent = new Student("Alice", 22);
       int rowsAffected = studentMapper.insertStudent(newStudent);
       sqlSession.commit(); // Confirma a transação
       logger.info("Registros afetados na inserção: " + rowsAffected);
   }

   @Test
   public void testUpdateStudent() {
       logger.info("Testando atualização de estudante...");
       Student studentToUpdate = new Student("Alice Wonderland", 23);
       studentToUpdate.setId(1); // ID do estudante a ser atualizado
       int rowsAffected = studentMapper.updateStudent(studentToUpdate);
       sqlSession.commit();
       logger.info("Registros afetados na atualização: " + rowsAffected);
   }

   @Test
   public void testDeleteStudent() {
       logger.info("Testando exclusão de estudante...");
       int studentIdToDelete = 2; // ID do estudante a ser excluído
       int rowsAffected = studentMapper.deleteStudent(studentIdToDelete);
       sqlSession.commit();
       logger.info("Registros afetados na exclusão: " + rowsAffected);
   }

   @Test
   public void testGetStudentById() {
       logger.info("Testando busca de estudante por ID...");
       Integer studentId = 1;
       Student foundStudent = studentMapper.getStudentById(studentId);
       logger.info("Estudante encontrado: " + foundStudent);
   }

   @Test
   public void testFindStudents() {
       logger.info("Testando busca de estudantes por critérios...");
       Map<string object=""> searchCriteria = new HashMap<>();
       // Exemplo 1: Buscar por nome (precisa incluir '%' no valor do map)
       searchCriteria.put("name", "%Alice%");
       List<student> studentsByName = studentMapper.findStudents(searchCriteria);
       logger.info("Estudantes encontrados com nome '%Alice%':");
       studentsByName.forEach(System.out::println);

       // Limpa e adiciona novo critério
       searchCriteria.clear();
       searchCriteria.put("age", 23); // Buscar por idade
       List<student> studentsByAge = studentMapper.findStudents(searchCriteria);
       logger.info("Estudantes encontrados com idade 23:");
       studentsByAge.forEach(System.out::println);

       // Exemplo com múltiplos critérios (neste caso, findByCriteria só suporta um WHERE por vez com SQLBuilder)
       // Para múltiplos WHERE, a lógica no provedor de SQL precisaria ser mais sofisticada ou usar XML.
   }
}
   </student></student></string>

Tags: MyBatis java SQL Dinâmico Anotações Persistência de Dados

Publicado em 7-21 17:51