Ingeniería Inversa con iBATIS: Abator

De ChuWiki

Abator es una herramienta de iBATIS que lee las tablas de una base de datos y genera todo lo necesario para trabajar con dicha base de datos usando iBATIS:

  • Crea beans Java para nuestro código.
  • Crea los ficheros XML de mapeo SQL de iBATIS para las tablas de nuestra base de datos.
  • Crea las clases Java correspondientes al patrón DAO para acceso a base de datos.

En este artículo instalaremos Abator como plugin de Eclipse y haremos un pequeño ejemplo.


Instalar Abator en Eclipse[editar]

Para instalar Abator en Eclipse seguimos los siguientes pasos:

  1. En Eclipse seleccionamos "Help" --> "Software Updates" --> "Find and Install"
  2. Seleccionamos "Search for new features to install" y pulsamos "Next"
  3. Pulsamos el botón "New Remote Site"
  4. Ponemos un nombre -por ejemplo Abator- y la URL http://ibatis.apache.org/tools/abator
  5. Marcamos "Abator for Eclipse Update Site" y pulsamos "Next"
  6. Seguimos el resto de la instalación hasta terminar. Nos pedirá reiniciar Eclipse.


La base de datos[editar]

Suponemos que tenemos una base de datos MySQL con varias tablas: persona, COCHE y PROPIETARIO, que son sobre las que haremos ingeniería inversa con iBATIS. Las tablas están en una base de datos de nombre "prueba". Solo pondré la tabla PROPIETARIO y veremos que es lo que genera Abator para esta tabla. Las otras serán similares. La tabla PROPIETARIO puede ser como esta

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 PROPIETARIO;
+------------------+--------------+------+-----+---------+----------------+
| Field            | Type         | Null | Key | Default | Extra          |
+------------------+--------------+------+-----+---------+----------------+
| ID               | int(11)      | NO   | PRI | NULL    | auto_increment | 
| FECHA_NACIMIENTO | date         | YES  |     | NULL    |                | 
| NOMBRE           | varchar(255) | YES  |     | NULL    |                | 
+------------------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)


Fichero de configuración de Abator[editar]

Para arrancar Abator necesitamos un fichero de configuración XML donde le diremos a Abator donde está la base de datos, cómo conectarse a ella, qué tablas nos interesan y qué debe generar para cada una de ellas y dónde. Un fichero simple XML de configuración de Abator puede ser como este, de nombre SimpleAbatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE abatorConfiguration
  PUBLIC "-//Apache Software Foundation//DTD Abator for iBATIS Configuration 1.0//EN"
  "http://ibatis.apache.org/dtd/abator-config_1_0.dtd">

<abatorConfiguration>
	<abatorContext id="DB2Tables" generatorSet="Java2">
		<jdbcConnection driverClass="org.gjt.mm.mysql.Driver"
			connectionURL="jdbc:mysql://localhost/prueba" 
			userId="usuarioBD"
			password="passwordBD">
			<classPathEntry
				location="/home/chuidiang/.m2/repository/mysql/mysql-connector-java/5.1.5/mysql-connector-java-5.1.5.jar" />
		</jdbcConnection>

		<javaModelGenerator targetPackage="com.chuidiang.beans"
			targetProject="PRUEBA_IBATIS/src/main/java">
		</javaModelGenerator>

		<sqlMapGenerator targetPackage="com.chuidiang.xml"
			targetProject="PRUEBA_IBATIS/src/main/java">
		</sqlMapGenerator>

		<daoGenerator type="IBATIS" targetPackage="com.chuidiang.dao"
			targetProject="PRUEBA_IBATIS/src/main/java">
		</daoGenerator>

		<table tableName="persona" domainObjectName="MiPersona"></table>

		<table tableName="PROPIETARIO" domainObjectName="Propietario">
		</table>

		<table tableName="COCHE" domainObjectName="Coche"></table>

	</abatorContext>
</abatorConfiguration>

Veamos algunas de las cosas del fichero.

La cabecera debemos copiarla tal cual. Luego hay dos tags <abatorConfiguration> y <abatorContext>. En el segundo le indicamos qué versión de java usamos -java2-.

Luego hay un tag <jdbcConnection> con los datos relativos a la conexión a base de datos. Debemos poner como <classPathEntry> el fichero jar con el driver de la base de datos, en nuestro caso mysql-connector-java-5.1.5.jar

En <javaModelGenerator> indicamos dónde queremos los beans java correspondientes a las tablas. targetPackage es el nombre del paquete que queramos que tengan y targetProject es el nombre del proyecto eclipse donde queremos que se generen y el path dentro de ese proyecto para llegar a un directorio de fuentes.

En <sqlMapGenerator> indicamos dónde queremos los ficheros XML de mapeo que luego usará iBATIS. Nuevamente hay que definir un targetPackage y un targetProject. Los ficheros se ubicarán en el path que se obtiene de concatenar lo que pongamos en targetProject más targetPackage.

En <daoGenerator> indicamos dónde queremos las clases java correspondientes a la implementación del patrón DAO. Nuevamente hay que poner targetPackage y targetProject.

Finalmente, en varios tags <table> ponemos de las que queremos que se generen los beans java, la implementación del patrón DAO y los ficheros de mapeo. En cada tag simplemente indicamos en nombre de la tabla con tableName y el nombre del bean java que queremos asociado con domainObjectName. Es posible poner comodines propios de base de datos en tableName para obtener varias tablas simultáneamente.


Ejecutamos Abator[editar]

Una vez creado el fichero SimpleAbatorConfig.xml, desde eclipse, en el árbol de "Package Explorer" pulsamos sobre él con el botón derecho del ratón para obtener el menú y seleccionamos "Generate iBATIS Artifacts". Si todo va como debe, Abator se conectará a la base de datos y nos generará dentro de nuestro proyecto eclipse los beans java, las clases java que implementan el patrón DAO y los ficheros XML de mapeo de iBATIS.

Vamos a ir viendo un ejemplo de lo que genera Abator para la tabla PROPIETARIO.

El java bean de la tabla PROPIETARIO[editar]

El bean Propietario.java no tiene nada especial. Símplmente atributos correspondientes a las columnas de la tabla PROPIETARIO y los métodos set() y get() correspondientes. No le añade ninguna herencia ni dependencia extraña o propia de iBATIS. No pondré aquí el código para no extenderme mucho.


La implementación del patrón DAO[editar]

Por cada tabla, Abator genera una interface y una clase. La interface no tiene ninguna dependencia extraña con nada de iBATIS y simplemente tiene métodos para obtener, salvar, modificar o borrar de base de datos el java bean Propietario.java. Muestro aquí el código de la interface PropietarioDAO.java y comentaré después algunos de los métodos

package com.chuidiang.dao;

import com.chuidiang.beans.Propietario;
import com.chuidiang.beans.PropietarioExample;
import java.util.List;

public interface PropietarioDAO {

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    void insert(Propietario record);

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    int updateByPrimaryKey(Propietario record);

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    int updateByPrimaryKeySelective(Propietario record);

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    List selectByExample(PropietarioExample example);

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    Propietario selectByPrimaryKey(Integer id);

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    int deleteByExample(PropietarioExample example);

    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    int deleteByPrimaryKey(Integer id);
}

Vemos métodos para select, update, insert y delete.

Vemos que algunos llevan el apellido "ByPrimaryKey". En esos métodos se hará la SQL correspondiente teniendo en cuenta la clave primaria de la tabla, por lo que sólo obtendremos un registro en el select, o sólo modificaremos uno de los registros con el update, o sólo borraremos un registro con el delete.

Vemos que algunos llevan el apellido "Selective". Estos métodos ignoran los atributos null del bean que se le pasen. Por ejemplo, si hacemos un updateByPrimaryKeySelective(unBeanPropietario), este método NO hará update de aquellos atributos que sean null. Sólo modificará en base de datos los que sean NO null.

Y vemos que algunos llevan el apellido "ByExample". A estos métodos se les pasa además una clase PropietarioExample. Esta clase también la genera Abator y es una especie de "filtro". En PropietarioExample y usando sus métodos podemos decir cosas como "id mayor que 4", "fechaNacimiento menor que 10 Dic 2005", etc. Puedes ver más detalles de cómo configurar este filtro en Uso de las clases Example

La otra clase del patrón DAO que genera Abator para la tabla PROPIETARIO es simplemente la implementación de esta interface PropietarioDAOImpl. Esta clase realiza las consultas e inserciones de SQL usando iBATIS, por lo que esta clase sí depende de iBATIS totalmente. La implemtación de cada método es cortita y aquí tienes un ejemplo de uno de ellos

...
public class PropietarioDAOImpl extends SqlMapDaoTemplate implements PropietarioDAO {
...
    /**
     * This method was generated by Abator for iBATIS. This method corresponds to the database table PROPIETARIO
     * @abatorgenerated  Tue Dec 18 19:58:56 GMT 2007
     */
    public int updateByPrimaryKeySelective(Propietario record) {
	int rows = update(
		"PROPIETARIO.abatorgenerated_updateByPrimaryKeySelective",
		record);
	return rows;
    }
...

Ficheros de mapeo de iBATIS[editar]

Los ficheros XML de mapeo generados son bastante complejos, para dar todas las posibilidades de la interface DAO. Afortunadamente, no tenemos que usarlos para nada nosotros directamente, ya que iBATIS se encarga de manejarlos. Simplemente, a modo de curiosidad, te pongo el fichero PROPIETARIO_SqlMap.xml generado por Abator.

<?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 namespace="PROPIETARIO" >
  <resultMap id="abatorgenerated_PropietarioResult" class="com.chuidiang.beans.Propietario" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    <result column="ID" property="id" jdbcType="INTEGER" />
    <result column="FECHA_NACIMIENTO" property="fechaNacimiento" jdbcType="DATE" />
    <result column="NOMBRE" property="nombre" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="abatorgenerated_Example_Where_Clause" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    <iterate property="oredCriteria" conjunction="or" prepend="where" removeFirstPrepend="iterate" >
      (
      <iterate prepend="and" property="oredCriteria[].criteriaWithoutValue" conjunction="and" >
        $oredCriteria[].criteriaWithoutValue[]$
      </iterate>
      <iterate prepend="and" property="oredCriteria[].criteriaWithSingleValue" conjunction="and" >
        $oredCriteria[].criteriaWithSingleValue[].condition$
          #oredCriteria[].criteriaWithSingleValue[].value#
      </iterate>
      <iterate prepend="and" property="oredCriteria[].criteriaWithListValue" conjunction="and" >
        $oredCriteria[].criteriaWithListValue[].condition$
        <iterate property="oredCriteria[].criteriaWithListValue[].values" open="(" close=")" conjunction="," >
          #oredCriteria[].criteriaWithListValue[].values[]#
        </iterate>
      </iterate>
      <iterate prepend="and" property="oredCriteria[].criteriaWithBetweenValue" conjunction="and" >
        $oredCriteria[].criteriaWithBetweenValue[].condition$
        #oredCriteria[].criteriaWithBetweenValue[].values[0]# and
        #oredCriteria[].criteriaWithBetweenValue[].values[1]#
      </iterate>
      )
    </iterate>
  </sql>
  <select id="abatorgenerated_selectByPrimaryKey" resultMap="abatorgenerated_PropietarioResult" parameterClass="com.chuidiang.beans.Propietario" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    select ID, FECHA_NACIMIENTO, NOMBRE
    from PROPIETARIO
    where ID = #id:INTEGER#
  </select>
  <select id="abatorgenerated_selectByExample" resultMap="abatorgenerated_PropietarioResult" parameterClass="com.chuidiang.beans.PropietarioExample" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    select ID, FECHA_NACIMIENTO, NOMBRE
    from PROPIETARIO
    <isParameterPresent >
      <include refid="PROPIETARIO.abatorgenerated_Example_Where_Clause" />
      <isNotNull property="orderByClause" >
        order by $orderByClause$
      </isNotNull>
    </isParameterPresent>
  </select>
  <delete id="abatorgenerated_deleteByPrimaryKey" parameterClass="com.chuidiang.beans.Propietario" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    delete from PROPIETARIO
    where ID = #id:INTEGER#
  </delete>
  <delete id="abatorgenerated_deleteByExample" parameterClass="com.chuidiang.beans.PropietarioExample" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    delete from PROPIETARIO
    <include refid="PROPIETARIO.abatorgenerated_Example_Where_Clause" />
  </delete>
  <insert id="abatorgenerated_insert" parameterClass="com.chuidiang.beans.Propietario" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    insert into PROPIETARIO (ID, FECHA_NACIMIENTO, NOMBRE)
    values (#id:INTEGER#, #fechaNacimiento:DATE#, #nombre:VARCHAR#)
  </insert>
  <update id="abatorgenerated_updateByPrimaryKey" parameterClass="com.chuidiang.beans.Propietario" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    update PROPIETARIO
    set FECHA_NACIMIENTO = #fechaNacimiento:DATE#,
      NOMBRE = #nombre:VARCHAR#
    where ID = #id:INTEGER#
  </update>
  <update id="abatorgenerated_updateByPrimaryKeySelective" parameterClass="com.chuidiang.beans.Propietario" >
    <!--
      WARNING - This element is automatically generated by Abator for iBATIS, do not modify.
      This element was generated on Tue Dec 18 19:58:56 GMT 2007.
    -->
    update PROPIETARIO
    <dynamic prepend="set" >
      <isNotNull prepend="," property="fechaNacimiento" >
        FECHA_NACIMIENTO = #fechaNacimiento:DATE#
      </isNotNull>
      <isNotNull prepend="," property="nombre" >
        NOMBRE = #nombre:VARCHAR#
      </isNotNull>
    </dynamic>
    where ID = #id#
  </update>
</sqlMap>