Começando com iBatis (MyBatis): Annotations

21 Feb 2011
8 mins read

Este tutorial tem como objetivo mostrar como fazer o setup do iBatis (MyBatis) em uma aplicação Java e mostrar exemplos simples de como fazer insert, update, select e delete usando annotations.

Este é o terceiro tutorial da série sobre iBatis/MyBatis.  Você pode ler os dois primeiros nos links:

  1. Introdução ao iBatis (MyBatis), uma alternativa ao JDBC e Hibernate
  2. Começando com iBatis (MyBatis): Configuração em XML

iBatis/ MyBatis oferecem a partir da versão 3 uma nova feature: annotations. Mas o MyBatis simplesmente NÃO tem nenhuma documentação ou exemplos com annotations. Comecei a procurar em listas de discussão algo sobre o assunto e consegui pouca informação, que usei para escrever esse tutorial.

Outra coisa que você vai notar é a limitação em relação às annotations. Vou demonstrar algumas funcionalidades aqui. Nos próximos tutoriais, você vai perceber que pode fazer muitas coisas com este poderoso framework, mas terá que usar configuração em XML se quiser mais flexibilidade. Todo o poder do iBatis está no XML.

Vamos então começar a brincar com iBatis e annotations. É bem mais simples, e você pode usar para queries simples e projetos pequenos. Como já mencionei, se desejar fazer algo mais completo, terá que recorrer à configuração em XML.

Uma última observação antes de começar: este tutorial é o mesmo do tutorial anterior, a única diferença é que vamos usar annotations, e não XML.

Pre-Requisitos

Para este tutorial usei:

IDE: Eclipse (você pode usar a sua IDE favorita)
DataBase: MySQL
Libs/jars: MybatisMySQL conector e JUnit (para testes)

No Eclipse, a estrutura do seu projeto vai ficar assim:

Dados de Exemplo

Execute o script que está dentro da pasta sql antes de começar o projeto. Este arquivo contém os dados de exemplo usados neste tutorial.

Não vou colocar o sample do banco de dados aqui de novo, ok? Você pode pegar o do exemplo passado ou dentro do projeto completo, disponível para download no fim da página.

1 - Contact POJO

Vamos criar um POJO para representar o contato – modelo de dados usado neste projeto de exemplo – que contém id, nome, telefone e endereço de email: - é o mesmo do post anterior.

2 - ContactMapper

Neste arquivo, vamos colocar todas as queries e também vamos usar as annotations. É a interface do MyBatis com o SQLSessionFactory.

Você pode criar algumas strings/constantes para não ficar passando uma string enorme para a annotation. Lembre-se que o código SQL é o mesmo do arquivo XML.

[code lang="java" firstline="1" toolbar="true" collapse="false" wraplines="false"]
package com.loiane.data;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.loiane.model.Contact;

public interface ContactMapper {

final String SELECT_ALL = "SELECT * FROM CONTACT";
final String SELECT_BY_ID = "SELECT * FROM CONTACT WHERE CONTACT_ID = #{id}";
final String UPDATE = "UPDATE CONTACT SET CONTACT_EMAIL = #{email}, CONTACT_NAME = #{name}, CONTACT_PHONE = #{phone} WHERE CONTACT_ID = #{id}";
final String UPDATE_NAME = "UPDATE CONTACT SET CONTACT_NAME = #{name} WHERE CONTACT_ID = #{id}";
final String DELETE = "DELETE FROM CONTACT WHERE CONTACT_ID = #{id}";
final String INSERT = "INSERT INTO CONTACT (CONTACT_EMAIL, CONTACT_NAME, CONTACT_PHONE) VALUES (#{name}, #{phone}, #{email})";

/**
* Returns the list of all Contact instances from the database.
* @return the list of all Contact instances from the database.
*/
@Select(SELECT_ALL)
@Results(value = {
@Result(property="id", column="CONTACT_ID"),
@Result(property="name", column="CONTACT_NAME"),
@Result(property="phone", column="CONTACT_PHONE"),
@Result(property="email", column="CONTACT_EMAIL")
})
List<Contact> selectAll();

/**
* Returns a Contact instance from the database.
* @param id primary key value used for lookup.
* @return A Contact instance with a primary key value equals to pk. null if there is no matching row.
*/
@Select(SELECT_BY_ID)
@Results(value = {
@Result(property="id"),
@Result(property="name", column="CONTACT_NAME"),
@Result(property="phone", column="CONTACT_PHONE"),
@Result(property="email", column="CONTACT_EMAIL")
})
Contact selectById(int id);

/**
* Updates an instance of Contact in the database.
* @param contact the instance to be updated.
*/
@Update(UPDATE)
void update(Contact contact);

/**
* Updates an instance of Contact in the database.
* @param name name value to be updated.
* @param id primary key value used for lookup.
*/
void updateName(@Param("name") String name, @Param("id") int id);

/**
* Delete an instance of Contact from the database.
* @param id primary key value of the instance to be deleted.
*/
@Delete(DELETE)
void delete(int id);

/**
* Insert an instance of Contact into the database.
* @param contact the instance to be persisted.
*/
@Insert(INSERT)
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(Contact contact);
}
[/code]

@Select

A annotation @Select é bem simples. Vamos dar uma olhada no primeiro método desta classe: selectAll. Basta usar a annotation @Select e passar a query como parâmetro. Bem simples né? Note que também estamos usando a annotation @Result. Essa annotation só serve para mapear coluna do banco com o atributo da classe, isso se os nomes não forem idênticos. Se tiverem o mesmo nome, não precisa usar essa annotation.

Vamos agora dar uma olhada no segundo método: selectById. Note que estamos usando um parâmetro. É um parâmetro simples, fácil de usar.

@Update

Suponha que queira fazer update de todas as colunas. Você pode passar o objeto como parâmetro e o iBatis faz toda a mágica para você. Lembre-se de que o nome do parâmetro da query tem que ter o mesmo nome do atributo da classe, senão o iBatis pode ficar confuso.

Digamos agora que você quer usar apenas 2 ou 3 parâmetros na sua query de update, e esses parâmetros estão soltos. Se voltar no post anterior, vai notar que usando a configuração XML você pode setar apenas 1 tipo de parâmetro para a query (pode usar um map para vários parâmetros) através da opção parameterType. Se você usar annotations, você pode usar mais de um parâmetro através da annotation @Param.

@Delete

A annotation @Delete também é bem simples. Segue as mesmas regras em relação aos parâmetros da query do tópico anterior.

@Insert

A annotation @Insert também segue as mesmas regras em relação aos parâmetros.

E em relação à geração de chaves? Se o banco de dados que você está usando suporta auto generation key, você pode obter a key gerada através da annotation @Options. Você precisa espeficificar as opções useGeneratedKeys e keyProperty, como no exemplo. Se o banco que você está usando não tem suporte a essa feature, desculpe, mas ainda não descobri uma maneira de fazer isso via annotations. O que você pode fazer é gerar manualmente (escrever uma método que retorna a key e passar o parâmetro para o insert).

3 - MyBatisConnectionFactory

Toda aplicação iBatis (MyBatis) é centralizada numa instância do SqlSessionFactory. Uma instância do SqlSessionFactory pode ser adquirida usando o SqlSessionFactoryBuilder. O SqlSessionFactoryBuilder pode criar uma instância do SqlSessionFactory a partir de um XML ou a partir de uma instância customizada da classe Configuration.

Com anotações, precisamos apenas configurar o nosso mapper na configuração do IBatis:

[code lang="java" firstline="1" toolbar="true" collapse="false" wraplines="false" highlight="27"]
package com.loiane.dao;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.loiane.data.ContactMapper;

public class MyBatisConnectionFactory {

private static SqlSessionFactory sqlSessionFactory;

static {

try {

String resource = "SqlMapConfig.xml";
Reader reader = Resources.getResourceAsReader(resource);

if (sqlSessionFactory == null) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

sqlSessionFactory.getConfiguration().addMapper(ContactMapper.class);
}
}

catch (FileNotFoundException fileNotFoundException) {
fileNotFoundException.printStackTrace();
}
catch (IOException iOException) {
iOException.printStackTrace();
}
}

public static SqlSessionFactory getSqlSessionFactory() {

return sqlSessionFactory;
}

}
[/code]

4 - ContactDAO

Agora que configuramos basicamente tudo que precisamos, vamos criar o DAO.

Para chamar as queries, precisamos fazer mais uma configuração, que é setar e fazer o getMapper na Session. Com o mapper em mãos, chamamos o método que desejamos.

[code lang="java" firstline="1" toolbar="true" collapse="false" wraplines="false" highlight="29,49,68,88"]
package com.loiane.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.loiane.data.ContactMapper;
import com.loiane.model.Contact;

public class ContactDAO {

private SqlSessionFactory sqlSessionFactory;

public ContactDAO(){
sqlSessionFactory = MyBatisConnectionFactory.getSqlSessionFactory();
}

/**
* Returns the list of all Contact instances from the database.
* @return the list of all Contact instances from the database.
*/
public List<Contact> selectAll(){

SqlSession session = sqlSessionFactory.openSession();

try {

ContactMapper mapper = session.getMapper(ContactMapper.class);
List<Contact> list = mapper.selectAll();

return list;
} finally {
session.close();
}
}

/**
* Returns a Contact instance from the database.
* @param id primary key value used for lookup.
* @return A Contact instance with a primary key value equals to pk. null if there is no matching row.
*/
public Contact selectById(int id){

SqlSession session = sqlSessionFactory.openSession();

try {

ContactMapper mapper = session.getMapper(ContactMapper.class);
Contact list = mapper.selectById(id);

return list;
} finally {
session.close();
}
}

/**
* Updates an instance of Contact in the database.
* @param contact the instance to be updated.
*/
public void update(Contact contact){

SqlSession session = sqlSessionFactory.openSession();

try {

ContactMapper mapper = session.getMapper(ContactMapper.class);
mapper.update(contact);

session.commit();
} finally {
session.close();
}
}

/**
* Updates an instance of Contact in the database.
* @param name name value to be updated.
* @param id primary key value used for lookup.
*/
public void updateName(String name, int id){

SqlSession session = sqlSessionFactory.openSession();

try {

ContactMapper mapper = session.getMapper(ContactMapper.class);
mapper.updateName(name, id);

session.commit();
} finally {
session.close();
}
}

/**
* Insert an instance of Contact into the database.
* @param contact the instance to be persisted.
*/
public void insert(Contact contact){

SqlSession session = sqlSessionFactory.openSession();

try {

ContactMapper mapper = session.getMapper(ContactMapper.class);
mapper.insert(contact);

session.commit();
} finally {
session.close();
}
}

/**
* Delete an instance of Contact from the database.
* @param id primary key value of the instance to be deleted.
*/
public void delete(int id){

SqlSession session = sqlSessionFactory.openSession();

try {

ContactMapper mapper = session.getMapper(ContactMapper.class);
mapper.delete(id);

session.commit();
} finally {
session.close();
}
}
}
[/code]

5 - Mapper Configuration File

O arquivo de configuração do iBatis contém propriedades que irão dizer como é que o iBatis irá se comportar.

Não precisamos configurar alias ou xml mapper neste exemplo, pois já fizemos isso no SqlSessionFactory.

[code lang="xml" firstline="1" toolbar="true" collapse="false" wraplines="false"]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/blog"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>

</configuration>
[/code]

Download

Infelizmente, não existe uma boa documentação ou exemplos que ensinem a usar annotações no site oficial do MyBatis. Nos próximos exemplos, vou usar o XML como configuração padrão, e quando possível, irei também demonstrar o uso das annotations para cada caso.

Se desejar fazer o download completo do projeto com todos os fontes e jars necessários para executar o projeto, você pode fazer o download a partir da minha conta no GitHub:https://github.com/loiane/ibatis-annotations-helloworld

Se desejar fazer o download do aquivo zip, basta clicar no botão de download - depois é só importar o arquivo para o Eclipse (ou outra IDE):

O próximo tutorial também será sobre iBatis. A estimativa é que essa série tenha cerca de 10 tutoriais (este é o terceiro). Vamos explorar bem o iBatis!

Bons códigos! :)