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:
-
iBatis DAO
(opcional)
-
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>
-
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);
}
}
}
-
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);
}
-
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;
}
}
-
iBatis sqlMap
-
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>
-
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
-
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.
Es una introducción muy buena al framework.
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.
Este tutorial me va a servir para conocer IBATIS, pues aca en Chile lo piden mucho en los perfiles analista programador Java.
Gracias Javier !!!