Durante i miei primi anni di programmazione (praticamente i più recenti della mia vita) ho imparato, durante il corso di Basi di Dati, come accedere ad un DataBase mediante codice Java.

In quel corso appresi prima quei 3-4 comandi SQL che mi permettevano di creare tabelle, campi e poco più. Ero ancora alle primissime armi col Java, sicché miscelare Java ed SQL in un’unica cosa era piuttosto stimolante.

A quel tempo usammo le banali connessioni JDBC a mano, specificando quindi le varie Connection, Statement, ResultSet, ecc…

Il codice, se pur pesante, ridondante e macchinoso, risultava assai carino da vedere… purtroppo però, col crescere della complessità dell’applicazione da sviluppare, il codice iniziava a diventare veramente ingestibile :)

Oggi come oggi ci sono varie alternative al passato… ovvero, Hibernate, JPA e MyBatis.

Questi framework si pongono ad un livello di astrazione più alto, e quindi consentono di scrivere del codice molto snello e riusabile, potendo anche permettere in maniera indolore la separazione dei compiti tra programmatore Java e programmatore SQL.

Cercando in rete ho visto un esempio minimale di mybatis… l’ho riadattato, esteso e pacchettizzato per voi. Per poterlo aprire, vi è sufficiente avere:

  • eclipse (meglio l’ultima versione… attualmente la 3.6)
  • mysql (come sopra)

A questo punto, vediamo il codice.


Questo è il prospetto dei file del progetto all’interno di eclipse:

Come potete notare, ci sono sorgenti del codice, sorgenti per qualche TestUnit, librerie mybatis e driver mysql, nonché del codice sql per creare database e tabella.

Il primo passo da fare è la creazione della tabella “testmybatis” a mano. Una volta creata, abbiamo fatto metà dell’opera ;)

Successivamente, andremo a creare la tabella “contatto” specificando qualche campo ed aggiungerne uno:

DROP TABLE IF EXISTS `contatto`;

CREATE TABLE `contatto` (
  `id` int(15) NOT NULL,
  `nome` varchar(50) DEFAULT NULL,
  `cognome` varchar(50) DEFAULT NULL,
  `telefono` varchar(20) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `contatto`(`id`,`nome`,`cognome`,`telefono`,`email`) VALUES ( '1','mario','rossi','3330000000','mario.rossi@gmail.com');

Una volta che il DB è stato popolato, tutto sarà pronto per poter sfruttare le potenzialità di mybatis.

Il file più importante è quello di configurazione di mybatis… in questo caso l’abbiamo chiamato “configuration.xml”, ossia:

<?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>
	<properties resource="com/toastedtech/mybatis/db.properties" />
	<typeAliases>
		<typeAlias type="com.toastedtech.mybatis.Contatto" alias="contatto" />
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="com/toastedtech/mybatis/ContattoMapper.xml" />
	</mappers>
</configuration>

In cui, per semplicità, si fa riferimento ad un file chiamato “db.properties” che dovrete andare a modificare secondo le vostre impostazioni personali… nel mio caso:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testmybatis
jdbc.username=root
jdbc.password=

Come si può constatare, si richiama un altro file, cioè “ContattoMapper.xml”, che serve sostanzialmente per le query vere e proprie:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
		"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.toastedtech.mybatis.ContattoMapper">
	<insert id="inserisci" useGeneratedKeys="true" keyProperty="id">
		insert
		into contatto (id, cognome, nome, telefono, email)
		values (#{id},
		#{cognome}, #{nome}, #{telefono}, #{email})
	</insert>

	<update id="aggiorna">
		update contatto set
		cognome = #{cognome},
		nome = #{nome},
		telefono = #{telefono},
		email = #{email}
		where id = #{id}
	</update>

	<delete id="cancella">
		delete from contatto
		where id = #{value}
	</delete>

	<select id="selezionaTutti" resultType="contatto">
		select
		id, cognome, nome,
		telefono, email
		from contatto
	</select>

	<select id="seleziona" resultType="contatto">
		select
		id, cognome, nome,
		telefono, email
		from contatto
		where id = #{value}
	</select>

</mapper>

I più scaltri avranno fatto caso che, all’interno di “configuration.xml”, è presente un riferimento ad un alias che punta ad una classe “Contatto”, e che questo alias si chiama “contatto”, e che “contatto” è anche presente in “ContattoMapper.xml”.

La classe “Contatto” non è altro che un “bean“, ovvero una semplice classe Java contenente dei campi privati e dei getter & setter pubblici per ciascuno di essi.

Il file “Contatto.java” sarà invece così formato:

package com.toastedtech.mybatis;

public class Contatto {
    Integer id;
    String cognome;
    String nome;
    String telefono;
    String email;

    @Override
    public String toString() {
        return "Contatto [id=" + id + ", cognome=" + cognome + ", nome=" + nome + ", telefono=" + telefono + ", email=" + email + "]";
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getCognome() {
        return cognome;
    }

    public void setCognome(String cognome) {
        this.cognome = cognome;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getTelefono() {
        return telefono;
    }

    public void setTelefono(String telefono) {
        this.telefono = telefono;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Come si può evincere dal codice, sia i campi del bean, che i campi della tabella del DB hanno lo stesso nome… questo per semplicità, ma ovviamente si può optare anche per campi diversi.
In una differente versione di questo esempio, mostreremo come mappare adeguatamente i campi colonna della tabella con i campi privati del bean.

Successivamente, mostriamo l’interfaccia “ContattoMapper.java” nella quale sono inserite le operazioni definite all’interno di “ContattoMapper.xml”:

package com.toastedtech.mybatis;

import java.util.List;

public interface ContattoMapper {

    Integer inserisci(Contatto contatto);

    List<Contatto> selezionaTutti();

    Contatto seleziona(Integer id);

    Integer aggiorna(Contatto contact);

    Integer cancella(Integer id);

}

Il nome dei metodi corrisponde all’id del mapper.

Vediamo infine il “Main.java”:

package com.toastedtech.mybatis;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

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

public class Main {
    private static SqlSessionFactory sqlMapper = null;

    public static void main(String[] args) {
        String resource = "com/toastedtech/mybatis/configuration.xml";
        Reader reader = null;

        try {
            reader = Resources.getResourceAsReader(resource);
            sqlMapper = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }

        SqlSession session = sqlMapper.openSession();
        try {
            ContattoMapper mapper = session.getMapper(ContattoMapper.class);

            List<Contatto> contacts = mapper.selezionaTutti();
            for (Contatto contact : contacts) {
                System.out.println(contact);
            }
        } finally {
            session.close();
        }
    }
}

Sostanzialmente viene letto il file di configurazione, inizialittata la sessione sql ed il mapper. Tutto qua!
Da questo momento, sarà possibile effettuare query al DB in maniera trasparente.

L’esecuzione del main avrà come output:

Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]

Vediamo ora un test più complesso… ma sempre sul banale! Per questo usiamo un Junit Test.

Specifichiamo quindi una classe “MyBatisTest” così:

package com.toastedtech.mybatis.test;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.toastedtech.mybatis.Contatto;
import com.toastedtech.mybatis.ContattoMapper;

public class MyBatisTest {

    private static SqlSessionFactory sqlMapper = null;
    private static SqlSession session;
    private static ContattoMapper mapper;

    /*
     * Questo metodo viene eseguito solamente una volta, all'inizializzazione del test
     */
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        String resource = "com/toastedtech/mybatis/configuration.xml";
        Reader reader = null;

        try {
            reader = Resources.getResourceAsReader(resource);
            sqlMapper = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }

        session = sqlMapper.openSession();
        mapper = session.getMapper(ContattoMapper.class);
    }

    /*
     * Questo metodo viene eseguito sempre appena prima il test
     */
    @Before
    public void setUp() throws Exception {
        List<Contatto> contacts = mapper.selezionaTutti();
        System.out.println("PRIMA +---------------------------------+ => TOT elementi: " + contacts.size());
        for (Contatto contact : contacts) {
            System.out.println(contact);
        }
    }

    /*
     * Questo metodo viene eseguito sempre finito prima il test
     */
    @After
    public void setDown() throws Exception {
        List<Contatto> contacts = mapper.selezionaTutti();
        System.out.println("DOPO  +---------------------------------+ => TOT elementi: " + contacts.size());
        for (Contatto contact : contacts) {
            System.out.println(contact);
        }
        System.out.println();
    }

    @Test
    public void inserisci() {
        Contatto contatto = new Contatto();
        contatto.setId(5);
        contatto.setNome("massimo");
        contatto.setCognome("tassi");
        contatto.setTelefono("3338888888");
        contatto.setEmail("massimo.tassi@gmail.com");

        mapper.inserisci(contatto);

        Contatto c = mapper.seleziona(new Integer(5));
        assertEquals("massimo", c.getNome());
        assertEquals("3338888888", c.getTelefono());
    }

    @Test
    public void aggiorna() {
        Contatto contatto = new Contatto();
        contatto.setId(5);
        contatto.setNome("carla");
        contatto.setCognome("nannini");
        contatto.setTelefono("3339999999");
        contatto.setEmail("carla.nannini@gmail.com");

        mapper.aggiorna(contatto);

        Contatto c = mapper.seleziona(new Integer(5));
        assertEquals("carla", c.getNome());
        assertEquals("3339999999", c.getTelefono());
    }

    @Test
    public void cancella() {
        mapper.cancella(new Integer(5));
    }

    /*
     * Questo metodo viene eseguito solamente una volta, alla fine del test
     */
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        session.close();
    }
}

Eseguendo il Test Unit, si ottiene come output:

PRIMA +---------------------------------+ => TOT elementi: 1
Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]
DOPO  +---------------------------------+ => TOT elementi: 2
Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]
Contatto [id=5, cognome=tassi, nome=massimo, telefono=3338888888, email=massimo.tassi@gmail.com]

PRIMA +---------------------------------+ => TOT elementi: 2
Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]
Contatto [id=5, cognome=tassi, nome=massimo, telefono=3338888888, email=massimo.tassi@gmail.com]
DOPO  +---------------------------------+ => TOT elementi: 2
Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]
Contatto [id=5, cognome=nannini, nome=carla, telefono=3339999999, email=carla.nannini@gmail.com]

PRIMA +---------------------------------+ => TOT elementi: 2
Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]
Contatto [id=5, cognome=nannini, nome=carla, telefono=3339999999, email=carla.nannini@gmail.com]
DOPO  +---------------------------------+ => TOT elementi: 1
Contatto [id=1, cognome=rossi, nome=mario, telefono=3330000000, email=mario.rossi@gmail.com]

e graficamente:

A questo punto, buon divertimento… scaricate l’intero progetto (mybatis-helloworld-eclipse) e fatemi sapere :)