Ejemplo simple con Ibatis

De ChuWiki

Vamos a hacer aquí un pequeño ejemplo con Ibatis. No voy a poner código completo, pero sí todo lo necesario para hacerlo. Veremos como hacer INSERT, UPDATE, DELETE y SELECT.


La base de datos[editar]

Como base de datos usaré MySQL y en la base de datos "prueba" tengo la siguiente tabla

mysql> use prueba;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> describe COCHE;
+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| ID_COCHE        | int(11)      | NO   | PRI | NULL    | auto_increment | 
| MARCA           | varchar(255) | YES  |     | NULL    |                | 
| MATRICULA       | varchar(255) | YES  |     | NULL    |                | 
| FECHA_MATRICULA | date         | YES  |     | NULL    |                | 
| ID_PROPIETARIO  | int(11)      | NO   |     |         |                | 
+-----------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

El bean java[editar]

La clase java en la que guardaremos los datos de un registro leído de esta tabla o donde guardaremos los datos que queremos insertar o modificar será un Java bean como el siguiente

package com.chuidiang.beans;

import java.util.Date;

public class Coche {
    private Integer id;
    private String marca;
    private String matricula;
    private Date fechaMatricula;
    private Integer idPropietario;

    public Integer getId() {
        return id;
    }

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

    public String getMarca() {
        return marca;
    }

    public void setMarca(String marca) {
        this.marca = marca;
    }

    public String getMatricula() {
        return matricula;
    }

    public void setMatricula(String matricula) {
        this.matricula = matricula;
    }

    public Date getFechaMatricula() {
        return fechaMatricula;
    }

    public void setFechaMatricula(Date fechaMatricula) {
        this.fechaMatricula = fechaMatricula;
    }

    public Integer getIdPropietario() {
        return idPropietario;
    }

    public void setIdPropietario(Integer idPropietario) {
        this.idPropietario = idPropietario;
    }

    /** Método de conveniencia para escribir rápido en pantalla */
    public String toString() {
        return id + "," + marca + "," + matricula + "," + 
        fechaMatricula;
    }
}


Fichero XML con el mapeo[editar]

Ibatis necesita uno o más ficheros con las sentencias SQL que usaremos en nuestro programa. El fichero XML con el mapeo, al que llamaré COCHE.xml, puede ser como este

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap 
   PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" 
   "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap >
	<select id="getCoche" resultClass="com.chuidiang.beans.Coche">
      SELECT 
          ID_COCHE as id,
          MARCA as marca,
          MATRICULA as matricula,
          FECHA_MATRICULA as fechaMatricula,
          ID_PROPIETARIO as idPropietario
       FROM COCHE
	   WHERE ID_COCHE = #valor#
	</select>

	<select id="getCoches" resultClass="com.chuidiang.beans.Coche">
      SELECT 
          ID_COCHE as id,
          MARCA as marca,
          MATRICULA as matricula,
          FECHA_MATRICULA as fechaMatricula,
          ID_PROPIETARIO as idPropietario
       FROM COCHE
	</select>
	
	<select id="getHashCoche" resultClass="java.util.Hashtable">
      SELECT 
          ID_COCHE as id,
          MARCA as marca,
          MATRICULA as matricula,
          FECHA_MATRICULA as fechaMatricula,
          ID_PROPIETARIO as idPropietario
       FROM COCHE
	   WHERE ID_COCHE = #valor#
	</select>
	
	<insert id="insertCoche" >
	   INSERT INTO COCHE VALUES (
	   null,
	   #marca#,
	   #matricula#,
	   #fechaMatricula#,
	   #idPropietario#)
	</insert>
	
	<update id="updateCoche" parameterClass="com.chuidiang.beans.Coche">
	   UPDATE COCHE SET
          MARCA = #marca#,
          MATRICULA = #matricula#,
          FECHA_MATRICULA = #fechaMatricula#,
          ID_PROPIETARIO = #idPropietario#
	   WHERE
	      ID_COCHE=#id#   
	</update>
	
	<delete id="removeCoche">
	   DELETE FROM COCHE WHERE ID_COCHE=#valor#
	</delete>
</sqlMap>

Vamos a comentar algunos detalles.

La parte del xml version y del DOCTYPE en la cabecera del fichero conviene ponerla para que Ibatis pueda verificar que es correcto el resto del fichero.

El tag principal es <sqlMap>

Luego hay varios tag <select>, <update>, <insert> o <delete> en función del tipo de sentencia SQL que queramos poner. Hay que fijarse que en las sentencias SQL aparecen "variables" entre signos #. Ibatis, en tiempo de ejecución, reemplazará estas variables con valores que le pasemos desde código.

Cada uno de estos tags lleva obligatoriamente un atributo id="nombre" en el que debemos ponerle un nombre que distinga unas sentencias SQL de otras. Esos nombres son los que usaremos en el código.

Cada uno de estos tags puede llevar además dos atributos parameterClass y resultClass. Cuando desde código Java intenemos usar una de estas SQL, usaremos unos métodos que nos proporciona Ibatis. Estos métodos admiten como primer parámetro un String que es el nombre que identifica la sentencia SQL, y como segundo parámetro un Object. parameterClass es el nombre de la clase que pasaremos como parámetro Object en el método Ibatis. De ese parameterObject Ibatis tratará de obtener los valores de las variables entre #.

  • Si parameterClass="int" o un tipo simple, en el código deberemos pasar un Integer o un tipo simple, e Ibatis pondrá directamente ese Integer reemplazando a #id#.
  • Si parameterClass="unaClaseJavaBean", en el código pasaremos esa clase como parámetro e Ibatis tratará de obtener el id llamando al método getId().
  • Si parameterClass="unHashTable" en el código pasaremos un Hashtable como parámetro e Ibatis tratará de obtener el id llamando a get("id")

parameterClass no es obligatorio, ya que Ibatis tratará de arreglarse con lo que le pasemos. De todas formas, por eficiencia, es mejor ponerlo.

El atributo resultClass indica qué clase devolverán los métodos Ibatis en el return, y es obligatorio si la sentencia SQL tiene que devolver algo -en concreto, para los SELECT-. Ibatis, igual que hacía con parameterClass, instanciará una o más clases del tipo indicado en resultClass y tratará de rellenar dentro los datos.

  • Si resultClass="unaClaseJavaBean", Ibatis hará new de unaClaseJavaBean y tratará de rellenar los valores llamando a los métodos setId(), setMarca(), setMatricula(), etc.
  • Si resultClass="unHashTable", Ibatis hará new de HashTable y tratará de rellenar los valores llamando a put("id", ...), put("marca", ...), etc.

Fichero XML de configuración de Ibatis[editar]

Ibatis necesita un fichero de configuración en XML donde se pongan los parámetros de conexión a la base de datos y donde se indiquen todos los ficheros de mapeo XML -como el anterior- que queremos usar. El fichero, al que llamaré ConfiguracionIbatis.xml, puede ser como este

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
   PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
   "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
 <transactionManager type="JDBC" >
   <dataSource type="SIMPLE">
    <property name="JDBC.Driver" value="org.gjt.mm.mysql.Driver"/>
    <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/prueba"/>
    <property name="JDBC.Username" value="usuario"/>
    <property name="JDBC.Password" value="password"/>
   </dataSource>
 </transactionManager>
 <sqlMap resource="com/chuidiang/xml/COCHE.xml" />
</sqlMapConfig>

Vemos la parte de la cabecera, que deberíamos poner tal cual está. También los datos de conexión a la base de datos. Y finalmente, como <sqlMap resource="... /> uno de los ficheros de mapeo XML. Pondríamos tantos de estos tags <sqlMap> como ficheros tuvieramos.

Tanto este fichero de configuración como los de mapeo en principio deben estar en el classpath, es decir, o bien dentro del jar, o bien en los directorios de búsqueda de clases.


El código java[editar]

Vamos a ver ahora el código java para empezar a usar todo esto. Por supuesto, debemos añadir a nuestro proyecto todos los jar de Ibatis más el driver de la base de datos que estemos usando -MySQL en este caso-.


Configuramos Ibatis[editar]

Lo primero que hacemos en el código es instanciar una clase de Ibatis que lea los ficheros de configuración y que es a la que pediremos que nos realice las consultas, inserciones, modificaciones y borrados. El código para instanciar esta clase de Ibatis puede ser como este

String resource = "com/chuidiang/xml/ConfiguracionIBatis.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);

Este código simplemente obtiene un Reader de nuestro fichero de configuración de Ibatis y le pide a SqlMapClientBuilder un SqlMapClient. El código puede lanzar excecpciones, así que deberíamos meterlo en un try-catch o bien relanzar la excepción en nuestro método que tenga este código.


Hacemos consultas con Ibatis[editar]

Vamos ahora a usar los distintos SELECT que hemos puesto en el fichero de configuración. El primero es un SELECT por id, al que hemos puesto el nombre de "getCoche". Para la cláusula WHERE hemos usado #value# y no hemos indicado el parameterClass, así que aquí deberemos pasar directamente un entero. Si pasamos una clase que no sea un tipo primitivo, Ibatis tratará de llamar al método getId() o get("id"). El código para esta consulta puede ser así

Integer claveCoche = new Integer(1);
Coche coche = (Coche) sqlMap.queryForObject("getCoche", claveCoche);

En queryForObject() estamos indicando que nos devuelva un solo objeto Coche. Como parámetro pasamos el nombre que le dimos a esta consulta en el fichero COCHE.xml al SELECT y el entero que hace de clave. Ibatis se encarga de poner el entero en #value#, hacer la consulta, hacer un new de la clase Coche y de rellenar todos sus parámetros llamando a los distintos métodos set() de la clase Coche. El nombre de los métodos set() será el de los AS que hemos puesto en la consulta.

Otro SELECT que tenemos es al que hemos llamado "getCoches", que pretendemos que nos devuelva una lista de coches. Este SELECT no tiene ningún parámetro de entrada, ya que consulta todos los coches y no hay cláusula WHERE. El resultClass indica con qué clase se hará cada uno de los registros leídos de base de datos. El código para usar este SELECT puede ser como el siguiente

List<Coche> coches = sqlMap.queryForList("getCoches", null);

Hemos llamado a queryForList() para indicar que queremos una lista. Como parámetro hemos pasado el nombre del SELECT que es "getCoches" y un null, puesto que no hemos puest cláusula WHERE. Ibatis se encarga de hacer la consulta, construir tantas clases Coche como registros obtenga y devolvernos una List de Coche.

Finalmente, otro SELECT que hemos puesto es para obtener un Hashtable en vez de una clase Coche. Al SELECT lo hemos llamado "getHashCoche". No hemos indicado un parameterClass, pero como hemos puesto en la cláusula WHERE un ID_COCHE=#valor#, tendremos que pasar un entero como parámetro de entrada. Como resultClass hemos puesto un java.util.Hashtable, que será donde Ibatis nos devuelva el resultado. El código para usar esto puede ser

Map hashCoche = (Map) sqlMap.queryForObject("getHashCoche", 3);

Ibatis se encarga de hacer la consulta, reemplazando el #valor# del WHERE por un 3, luego instancia un Hashtable, lo rellena con el método put("id",...), put("marca", ...) y lo devuelve. Los valores que usa como claves para el Hashtable son los AS que hemos puesto en el SELECT.


Insertamos registros con Ibatis[editar]

Para insertar usaremos la SQL que hemos llamado "insertCoche". No lleva resultClass puesto que esta SQL no devuelve ningún registro. No le hemos puesto parameterClass, así que en el método java correspondiente podremos usar indistintamente una clase Coche o un Hashtable. Este podría ser el código de ejemplo

// Insertar un coche nuevo. No ponemos id porque se genera solo en la inserción.
Coche coche = new Coche();
coche.setMarca("una marca");
coche.setMatricula("una matricula");
coche.setFechaMatricula(new Date());
sqlMap.insert("insertCoche", coche);

// Ahora insertamos con un Hashtable.
Hashtable hashCoche = new Hashtable();
hashCoche.put("marca", "la marca");
hashCoche.put("matricula", "x-2222-z");
hashCoche.put("fechaMatricula", new Date());
sqlMap.insert("insertCoche", hashCoche);

En el método insert() pasamos como parámetro el nombre de la SQL "insertCoche" y la clase que contiene los datos. En el caso del bean, Ibatis llamará a los métodos get() para obtener los valores, en el caso del Hashtable llamará al método get(clave) para obtener los valores.


Modificamos registros en base de datos con Ibatis[editar]

Para modificar datos usaremos la SQL con el nombre "updateCoche". No lleva resultClass puesto que no devuelve ningún registro y le hemos puesto como parameterClass un bean Coche, por lo que sólo podremos usar este UPDATE pasando una instancia de Coche. El código de ejemplo puede ser

// Obtenemos un coche.
Integer claveCoche = new Integer(1);
Coche coche = (Coche) sqlMap.queryForObject("getCoche", claveCoche);

// modificar un coche
coche.setMarca("cambiada");
sqlMap.update("updateCoche", coche);

En este caso, Ibatis obtendrá el #id# para el WHERE y los campos que hay que modificar todos de la clase Coche que le pasemos.


Borramos un registro con Ibatis[editar]

Para el borrado de registros, usaremos la SQL de nombre "removeCoche" a la que se le debe pasar un entero con el #valor# del id del coche a borrar. El código de ejemplo puede ser

sqlMap.delete("removeCoche", 11);

que borrará de BD el coche cuya clave es 11.


Algo más[editar]

Ibatis tiene muchas más cosas además de las simples que hemos visto aquí. Podemos poner, por ejemplo, una especie de condiciones entremezcladas con las sentencias SQL del fichero XML, de forma que, por ejemplo, sólo se haga UPDATE de aquellos campos que no son null en el bean que se pase. También, con esos condicionales, se podrían construir cláusulas WHERE más complejas.