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>