Manual Básico de Apache iBatis

3
60397

Manual Básico de
Apache iBatis

Introducción

¿Qué es iBatis?

Apache iBatis
está constituido por dos frameworks independientes que
generalmente se usan juntos: DAO y sqlMaps. El primero simplifica la
implementación el patrón de diseño Direct Access
Objects (DAO) y el segundo simplifica la persistencia de objetos en
bases de datos relacionales.

iBatis NO es un
ORM (Object Relational Mapper), por lo que se pueden utilizar modelos
de datos existentes o poco normalizados y, finalmente, no es
completamente transparente (el programador programa el SQL)

Ambos frameworks
de iBatis han sido portados a .NET con notable éxito y
popularidad. Ambas versiones ya son releases estables y maduras.

¿Para qué sirve?

El patrón
DAO nos ayuda a organizar las tareas de persistencia (guardado,
búsqueda, recuperación, borrado, etc.) de los objetos y
nos permite definir múltiples implementaciones para un mismo
objeto mediante la definición de una interfaz. Por ejemplo, la
interfaz UsuarioDao tiene los metodos guardarUsuario(Usuario
usuario), obtenerUsuario(String loginId), etc y dos implementaciones:
UsuarioMySqlDao y UsuarioLdapDao donde implementamos las operaciones
para MySql y Ldap respectivamente. Con iBatis DAO podremos configurar
cuándo usar una u otra implementación sin necesidad de
modificar código. Además, agregar un UsuarioOracleDao
será muy fácil y no implicará modificar código
del resto de la aplicación.

Cuando hacemos
un análisis y diseño orientado a objetos obtenemos
nuestro modelo de clases y también diseñamos el modelo
de datos donde se almacena la información. Y siempre nos queda
la tarea de conectarnos con una base de datos, crear statements,
construir selects/updates/etc, recorrer resultsets y setear atributos
de objetos, etc. para guardar, buscar, recuperar, etc. los valores de
los atributos de un objeto. iBatis sqlMap simplifica esta tarea
resumiéndola a la configuración de ficheros XML, con
SQL ANSI o propietario y funciona con prácticamente cualquier
base de datos con driver JDBC.

¿Cuándo utilizar iBatis?

Como toda herramienta,
no vale para todo. iBatis sqlMap es muy válido cuando:

  • Se requiere una
    curva de aprendizaje rápida y pocos conocimientos previos, y
    no requiere aprender un lenguaje de consultas como en Hibernate o
    EJB CMP.

  • Se necesita
    manipular el SQL (para utilizar SQL propietario, optimizarlo, etc.)
    o se necesita llamar a procedimientos almacenados.

  • El modelo de datos
    existe previamente y no está muy normalizado (aunque
    lógicamente se puede utilizar con modelos nuevos y
    normalizados)

  • Alto rendimiento y
    optimización

…pero no para
cuando:

  • Se requiere una
    automatización total y transparente de la persistencia

  • Se requiere
    soporte multi-RDBMS (motor de base de datos) transparente y
    automática

Hay una comparación
constructiva con Hibernate en
http://raibledesigns.com/page/rd?entry=hibernate_vs_ibatis

iBatis DAO es válido
cuando:

  • Se sabe que el
    RDBMS (motor de base de datos) puede cambiar en el futuro

  • La implementación
    del acceso a datos puede requerir cambios sustanciales en el futuro

  • La implementación
    del acceso a datos puede variar entre un entorno de producción
    y otro (software comercial en distintos clientes)

iBatis DAO soporta
sqlMap, Hibernate, etc.

¿Cómo funciona esto internamente?

Básicamente,
programación declarativa y extensión del framework. Es
decir, se configuran ficheros XML y, en iBatis DAO, se extienden
clases donde se implementa la interfaz y el comportamiento
específico.

Ejemplo:

  1. iBatis DAO
    (opcional)

    1. Crear un
      DAO.XML

<!DOCTYPE
daoConfig PUBLIC «-//iBATIS.com//DTD DAO Configuration 2.0//EN»
«http://www.ibatis.com/dtd/dao-2.dtd»>

<daoConfig>

<context>

<transactionManager
type=»SQLMAP»>

<property
name=»SqlMapConfigResource»

value=»es/dxd/km/dao/sqlMap/sqlMapConfig.xml»
/>

</transactionManager>

<dao
interface=»es.dxd.km.dao.UsuarioDao»

implementation=»es.dxd.km.dao.sqlMap.UsuarioSqlMapDao»
/>

</context>

</daoConfig>

    1. Crear una
      utilidad de configuración tipo DaoConfig

public
class DaoConfig {

private
static final String DAO_XML = «es/dxd/km/dao/dao.xml»;

private
static final DaoManager daoManager;

static
{

try
{

daoManager
= newDaoManager();

}
catch (Exception e) {

throw
new RuntimeException(«Description. Cause: » + e, e);

}

}

public
static DaoManager getDaoManager() {

return
daoManager;

}

public
static DaoManager newDaoManager() {

try
{

Reader
reader = Resources.getResourceAsReader(DAO_XML);

return
DaoManagerBuilder.buildDaoManager(reader, null);

}
catch (Exception e) {

throw
new RuntimeException(

«Could
not initialize DaoConfig. Cause: » + e, e);

}

}

}

    1. Crear la
      interfaz del Dao para Usuarios, UsuariosDao:

public
interface UsuarioDao extends Dao {

public
int updateUsuario(Usuario usuario);

public
int insertUsuario(Usuario usuario);

public
int deleteUsuario(String idUsuario);

public
Usuario getUsuario(String idUsuario);

public
Usuario getUsuarioValidado(String idUsuario, String password);

public
List getUsuariosByExample(Usuario usuario);

}

    1. Crear su
      implementación:

public
class UsuarioSqlMapDao extends SqlMapDaoTemplate implements

es.dxd.km.dao.UsuarioDao
{

public
UsuarioSqlMapDao(DaoManager arg0) {

super(arg0);

}

public
int updateUsuario(Usuario usuario) {

try
{

return
getSqlMapExecutor().update(«updateUsuario», usuario);

}
catch (SQLException e) {

throw
new DaoException(«Error actualizando usuario. Cause: » + e,
e);

}

}

public
int insertUsuario(Usuario usuario) {

try
{

getSqlMapExecutor().insert(«insertUsuario»,
usuario);

}
catch (SQLException e) {

throw
new DaoException(«Error insertando usuario. Cause: » + e,
e);

}

return
1;

}

public
int deleteUsuario(String idUsuario) {

try
{

return
getSqlMapExecutor().delete(«deleteUsuario», idUsuario);

}
catch (SQLException e) {

throw
new DaoException(«Error actualizando usuario. Cause: » + e,
e);

}

}

public
Usuario getUsuario(String idUsuario) {

try
{

Usuario
usuario = (Usuario) getSqlMapExecutor().queryForObject(

«getUsuarioById»,
idUsuario);

usuario.setRoles((List)
getSqlMapExecutor().queryForList(

«getRolesUsuario»,
usuario));

return
usuario;

}
catch (SQLException e) {

throw
new DaoException(«Error recuperando usuario. Cause: » + e,
e);

}

}

public
List getUsuariosByExample(Usuario usuario) {

try
{

Usuario
usuarioExample = new Usuario();

if
(usuario.getApellidos() != null) {

usuarioExample.setApellidos(«%»

+
usuario.getApellidos().toLowerCase() + «%»);

}

if
(usuario.getNombre() != null) {

usuarioExample.setNombre(«%»

+
usuario.getNombre().toLowerCase() + «%»);

}

usuarioExample.setIdUsuario(usuario.getIdUsuario());

List
usuarios = (List) getSqlMapExecutor().queryForList(

«getUsuariosByExample»,
usuarioExample);

//
Asignar los roles

for
(Iterator iter = usuarios.iterator(); iter.hasNext();) {

Usuario
usuario2 = (Usuario) iter.next();

usuario2.setRoles((List)
getSqlMapExecutor().queryForList(

«getRolesUsuario»,
usuario2));

}

return
usuarios;

}
catch (SQLException e) {

throw
new DaoException(«Error recuperando usuarios. Cause: » + e,
e);

}

}

public
Usuario getUsuarioValidado(String idUsuario, String password) {

Usuario
usuario = getUsuario(idUsuario);

return
usuario.getPassword().equals(password) ? usuario : null;

}

}

  1. iBatis sqlMap

    1. Crear la
      configuración sqlMapConfig.xml

<?xml
version=»1.0″ encoding=»UTF-8″ ?>

<!DOCTYPE
sqlMapConfig

PUBLIC
«-//iBATIS.com//DTD SQL Map Config 2.0//EN»

«http://www.ibatis.com/dtd/sql-map-config-2.dtd»>

<sqlMapConfig>

<properties

resource=»properties/SqlMapConfig.properties»
/>

<settings
cacheModelsEnabled=»true» enhancementEnabled=»true»

lazyLoadingEnabled=»true»
maxRequests=»32″ maxSessions=»10″

maxTransactions=»5″
useStatementNamespaces=»false» />

<transactionManager
type=»JDBC»>

<dataSource
type=»SIMPLE»>

<property
name=»JDBC.Driver» value=»${driver}» />

<property
name=»JDBC.ConnectionURL» value=»${url}» />

<property
name=»JDBC.Username» value=»${username}» />

<property
name=»JDBC.Password» value=»${password}» />

</dataSource>

</transactionManager>

<sqlMap

resource=»es/dxd/km/dao/sqlMap/Usuario.xml»
/>

</sqlMapConfig>

    1. La
      configuración de contexto es preferible sacarla afuera del
      xml para, por ejemplo, cambiar fácilmente de entornos, en
      el sqlMapConfig.xml se indicó que estuviera en
      properties/SqlMapConfig.properties:

driver=org.apache.derby.jdbc.ClientDriver

url=jdbc:derby://localhost:1527/kmdb

username=derby

password=derby

    1. Finalmente, se
      configuran las operaciones y su SQL en Usuario.xml:

<?xml
version=»1.0″ encoding=»UTF-8″ ?>

<!DOCTYPE
sqlMap PUBLIC «-//iBATIS.com//DTD SQL Map 2.0//EN»
«http://www.ibatis.com/dtd/sql-map-2.dtd»>

<sqlMap
namespace=»Usuario»>

<select
id=»getUsuarioById» resultClass=»es.dxd.km.model.Usuario»>

SELECT
rtrim(ID_USUARIO) as idUsuario, rtrim(PASSWORD) as

password,
rtrim(NOMBRE) as nombre, rtrim(APELLIDOS) as apellidos

FROM
APP.USUARIOS WHERE id_usuario = #value#

</select>

<select
id=»getRolesUsuario»

parameterClass=»es.dxd.km.model.Usuario»

resultClass=»es.dxd.km.model.Rol»>

SELECT
rtrim(R.ID_ROL) as idRol, rtrim(R.NOMBRE) as nombre,

rtrim(R.DESCRIPCION)
as descripcion FROM APP.ROLES R,

APP.USUARIOS_ROLES
U WHERE R.ID_ROL = U.ID_ROL AND U.ID_USUARIO

=
#idUsuario#

</select>

<select
id=»getUsuariosByExample»

parameterClass=»es.dxd.km.model.Usuario»

resultClass=»es.dxd.km.model.Usuario»>

SELECT
rtrim(ID_USUARIO) as idUsuario, rtrim(PASSWORD) as

password,
rtrim(NOMBRE) as nombre, rtrim(APELLIDOS) as apellidos

FROM
APP.USUARIOS

<dynamic
prepend=»WHERE»>

<isNotNull
prepend=»AND» property=»nombre»>

lower(NOMBRE)
like #nombre#

</isNotNull>

<isNotNull
prepend=»AND» property=»apellidos»>

lower(APELLIDOS)
like #apellidos#

</isNotNull>

<isNotNull
prepend=»AND» property=»idUsuario»>

ID_USUARIO
= #idUsuario#

</isNotNull>

</dynamic>

</select>

<insert
id=»insertUsuario»

parameterClass=»es.dxd.km.model.Usuario»>

INSERT
INTO APP.USUARIOS (ID_USUARIO, PASSWORD, NOMBRE,

APELLIDOS)
VALUES (#idUsuario#, #password#, #nombre#,

#apellidos#)

</insert>

<update
id=»updateUsuario»

parameterClass=»es.dxd.km.model.Usuario»>

UPDATE
APP.USUARIOS SET PASSWORD = #password#, NOMBRE =

#nombre#,APELLIDOS
= #apellidos# WHERE ID_USUARIO = #idUsuario#

</update>

<delete
id=»deleteUsuario» parameterClass=»string»>

DELETE
FROM APP.USUARIOS WHERE ID_USUARIO = #value#

</delete>

</sqlMap>

¿Licencia?

iBatis
es un proyecto que pertenece y utilza la licencia de Apache Software
Foundation, por lo que es Open Source disponible, modificable y
comercializable libremente.
(todo asesoramiento en este tema será
bienvenido)

¿Y para qué este manual básico?

Para simplificar el
«first touch» y para explicar iBatis a alto nivel.

No pretende ser un
tutorial, una guía del usuario ni un manual de referencia.
Ellos ya se han escrito y están disponibles en las direcciones
que se mencionan en la sección «¿Dónde
encuentro más info?»

¿Dónde encuentro más info?

En la Página
Oficial http://ibatis.apache.org
encontrarán en la sección Documentación unos
tutoriales muy buenos aunque extensos, una guía de referencia
y el JavaDoc de la API. También tienen una sección de
preguntas frecuentes muy completa.

Desde esta página
también se puede descargar (recomiendo) el ejemplo oficial de
utilización de iBatis: el JPetStore. Viene a ser como el
PetStore de J2EE reconvertido a Struts+iBatis.

¿Cómo pongo en funcionamiento
iBatis?

Básicamente hay
que incluir las librerías en el path (en las aplicaciones web,
en /WEB-INF/lib)

Lo ideal es deplegar el
war del JPetStore, descargable desde http://ibatis.apache.org,
y ver cómo funciona. Hacerle alguna modificación.
Comenzar un prototipo desde cero y seguir los tutoriales.

Consejo: ser muy
meticuloso con los XML porque la info de debugging es escasa y
confusa.

3 Comentarios

  1. Hola, para el caso de Ibatis como puedo obtener un rango de registros, por ejemplo de 100 registros quiero obtenter del 41 al registro 60. algo como =60

    Gracias.

  2. Este tutorial me va a servir para conocer IBATIS, pues aca en Chile lo piden mucho en los perfiles analista programador Java.

    Gracias Javier !!!

Dejar respuesta

Please enter your comment!
Please enter your name here