Skip to content
Site Tools
Narrow screen resolution Wide screen resolution Auto adjust screen size Increase font size Decrease font size Default font size
Esta aquí : Home arrow Enlaces arrow Artículos de miembros arrow Spring + DAO Pattern (usando JDBC/iBATIS/JPA)
Spring + DAO Pattern (usando JDBC/iBATIS/JPA) PDF Imprimir E-Mail
Calificación del usuario: / 8
MaloBueno 
escrito por diego.silva   

Spring es un poderoso framework para Java donde su principal objetivo (y muy útil) es la instanciación de objetos. Desde nuestros programas  - en vez de hacer new() y con cientos de inicializaciones de valores - simplemente obtenemos el objeto ya armadito desde el Spring.

Lo que veremos en este micro tutorial es cómo usar Spring bajo el patrón Dao y patrón Factory. Veremos una aplicación web (usando JSF) que accede a la base de datos "sample" que viene incluido en el NetBeans. Nuestra aplicación se ejecutará en Glassfish, y debe contar con un JDBC Resource que accede a la base de datos mencionada.  

  • Versión Google Sites: https://sites.google.com/site/apuntesdejava/Home/dao-y-spring
  • Versión Google Knol: http://knol.google.com/k/diego-enrique-silva-lmaco/spring-dao-pattern/2rok88lo9lpfm/2

En los ejemplos publicados, veremos que existe el archivo sun-resources.xml que contiene la configuración del JDBC Resource y JDBC Pool para crear el DataSource en nuestro Glassfish:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd">
<resources>

<jdbc-resource enabled="true" jndi-name="jdbc/sample" object-type="user" pool-name="samplePool">
<description/>
</jdbc-resource>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.ClientDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="samplePool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="URL" value="jdbc:derby://localhost:1527/sample"/>
<property name="serverName" value="localhost"/>
<property name="PortNumber" value="1527"/>
<property name="DatabaseName" value="sample"/>
<property name="User" value="app"/>
<property name="Password" value="app"/>
</jdbc-connection-pool>
</resources>

En resumen, para acceder a nuestra base de datos lo haremos por el dataSource, usando el JDNI: jdbc/sample.

Usando JDBC

RowMapper

Este es quizás la manera de acceder a la base de datos más simple y básica.

Recordemos que cuando usábamos un JDBC normal, después de hacer un executeQuery(), haciamos un while (rs.next()) y dentro del bucle haciamos un new a cada objeto por  cada fila y lo agregábamos a una lista. Pues con el Spring es lo mismo, pero nos da una ayudadita.


public List getProducts() {

RowMapper mapper = new RowMapper() {

public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
//se lee cada campo como en el while de un ResultSet comun de JDBC
Product p = new Product();
p.setProductId(rs.getInt("PRODUCT_ID"));
p.setManufacturerId(rs.getInt("MANUFACTURER_ID"));
p.setProductCode(rs.getString("PRODUCT_CODE"));
p.setPurchaseCost(rs.getDouble("PURCHASE_COST"));
p.setQuantityOnHand(rs.getInt("QUANTITY_ON_HAND"));
p.setMarkup(rs.getFloat("MARKUP"));
p.setAvailable(rs.getString("AVAILABLE"));
p.setDescription(rs.getString("DESCRIPTION"));
return p;
}
};

List list = jdbcTemplate.query("SELECT * FROM Product", mapper);

return list;
}

El Spring es el que hace el executeQuery() y le decimos cómo lo va a tratar por cada fila del resultado. Esto se hace implementado la interfaz org.springframework.jdbc.core.RowMapper.

Para mayor información como hacer queries desde Spring, visitar: http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html

¿Y el jdbcTemplate?

Pues este objeto es una propiedad que fue recibida desde el Spring:

    private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource ds) {
jdbcTemplate = new JdbcTemplate(ds);
}

¿Y cómo el Spring pone el valor del DataSource?

Así:

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/sample" />
</bean>
<bean id="ProductDao" class="dao.derby.DerbyProductsDao">
<property name="dataSource" ref="dataSource" />
</bean>

Proyecto ejemplo

http://diesil-java.googlecode.com/files/SpringDaoJdbc.tar.gz

Usando IBatis

iBATIS es un framework de Apache que consiste en mapear las consultas y los objetos que son resultado de las consultas o que son usados como parámetros de ellas. Solo hace eso: mapea consultas. No confundir ni comparar con JPA.

Con iBATIS, ya no se tiene que poner los comandos SQL dentro de los programas Java, sino se usa un archivo .xml. 

Archivos .XML para iBATIS

Este es el contenido del archivo Product.xml que contiene las consultas que refiere a la tabla PRODUCT:

<?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="sample">
<resultMap id="productResult" class="beans.Product">
<result property="productId" column="PRODUCT_ID" />
<result property="manufacturerId" column="MANUFACTURER_ID" />
<result property="productCode" column="PRODUCT_CODE" />
<result property="purchaseCost" column="PURCHASE_COST" />
<result property="quantityOnHand" column="QUANTITY_ON_HAND" />
<result property="markup" column="MARKUP" />
<result property="available" column="AVAILABLE" />
<result property="description" column="DESCRIPTION" />

</resultMap>
<select id="productSelect" resultMap="productResult">
SELECT * FROM PRODUCT
</select>
</sqlMap>

Como se puede ver, el mapeo de cada columna con cada propiedad del objeto beans.Product está dado en el tag <resultMap> y es utilizado en el <select>. iBATIS ya sabe que cada resultado de ese query será mapeado por el resultMap="productResult".

El archivo de configuración de iBATIS requiere cierta configuración para acceder a la base de datos; pero usando Spring, el archivo quedará reducido a lo siguiente:(Archivo SqlmapConfig.xml)


<sqlMapConfig>
<sqlMap resource="Products.xml" />
</sqlMapConfig>

beans.xml para iBATIS

Ahora, necesitamos configurar el beans.xml para que la conexión a la base de datos  lo configure a nuestro iBATIS.

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/sample" />
</bean>
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:SqlmapConfig.xml"/>
<property name="dataSource" ref="dataSource"/>
</bean>

<bean id="ProductDao" class="dao.ibatis.IBatisProductsDao">
<property name="sqlMapClient" ref="sqlMapClient" />
</bean>

La implementación del acceso a IBatis.

Ahora, nuestra clase para manejar el IBatis es el siguiente:

public class IBatisProductsDao extends SqlMapClientDaoSupport implements ProductDao {

public List getProducts() {
List list = getSqlMapClientTemplate().queryForList("productSelect");
// solo llama a la consulta
return list;
}
}

Por la herencia de la clase org.springframework.orm.ibatis.support.SqlMapClientDaoSupport, se tiene el método setSqlMapClient(), que es lo que se está usando en el archivo beans.xml

Para mayor información de cómo usar IBatis con Spring, ver: http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-ibatis

Proyecto ejemplo

http://diesil-java.googlecode.com/files/SpringDaoIBatis.tar.gz

Usando JPA

¿Será necesario usar Spring con JPA? Si vamos usar el patrón DAO + Factory, definitivamente que sí. Ya que el Factory se encargará de devolver una interfaz implementada según sea  haya decidido usar. La aplicación misma no cambia, solo cambiará la implementación del acceso a la base de datos.

La clase entidad

Para usar JPA, es necesario hacer unos ajustes a nuestros Objetos de Transferencia (Transfer Objects, es decir, los objetos que usamos para mostrar en el lista y obtener datos de la base de datos). Es necesario que sean declarados como Entidades.

@Entity
@Table(name = "PRODUCT")


public class Product implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "PRODUCT_ID")

private Integer productId;
@Column(name = "PURCHASE_COST")
private BigDecimal purchaseCost;
@Column(name = "QUANTITY_ON_HAND")
private Integer quantityOnHand;
@Column(name = "MARKUP")
private BigDecimal markup;
@Column(name = "AVAILABLE")
private String available;
@Column(name = "DESCRIPTION")
private String description;
@Column(name="MANUFACTURER_ID")
private int manufacturerId;
@Column(name="PRODUCT_CODE")
private String productCode;
//...

beans.xml para JPA

El archivo persistence.xml ya tiene la conexión a la base de datos por el JNDI del datasource. Pero ahora lo que nos tiene que preocupar es acceder desde nuestra implementación de ProductoDao usando JPA.

NetBeans tiene un asistente para acceder al JPA usando el JNDI, pero como nuestra implementación de ProductoDao no es un componente de JEE, entonces nos va a generar bastante código con referente a Context y demas cosas de JNDI. Lo ideal es que se use anotaciones. Para ello, debemos considerar lo siguiente en nuestro beans.xml.


    <bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

<property name="persistenceUnitName" value="SpringDaoJpaPU"/>
</bean>


<context:annotation-config />

<bean id="ProductDao" class="dao.jpa.JpaProductDao"/>

El tag <context:annotacion-config/> es el que se encargará de permitir las anotaciones en nuestra implementación de ProductDao.

Implementación de la interfaz ProductDao

Ahora bien, nuestra implementación de la interfaz ProductDao es como sigue:

@Repository
public class JpaProductDao implements ProductDao {

@PersistenceUnit(unitName = "SpringDaoJpaPU")
private EntityManagerFactory emf;

@Transactional(readOnly = true)
public List getProducts() {
EntityManager em = emf.createEntityManager();
try {
Query q = em.createQuery("SELECT p FROM Product p");
return q.getResultList();
} finally {
if (em != null) {
em.close();
}
}
}
}

Y todo lo demás, sigue igual.

Proyecto ejemplo

http://diesil-java.googlecode.com/files/SpringDaoJpa.tar.gz

El resto del proyecto

Como hemos visto, nos hemos centrado más en el acceso a la base de datos que en la aplicación en sí.

El DAOFactory

Esa es la idea del DAO + Factory: solo cambiar el acceso a la base de datos, el resto de la aplicación no debe sufrir modificación alguna. De tal manera que si se necesita cambiar la manera de acceder a la base de datos, solo se necesita cambiar en el beans.xml la implementación que necesitemos:

<!-- Usando JDBC -->
<bean id="ProductDao" class="dao.derby.DerbyProductsDao">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- usando Ibatis -->

<bean id="ProductDao" class="dao.ibatis.IBatisProductsDao">
<property name="sqlMapClient" ref="sqlMapClient" />
</bean>

<!-- usando JPA -->
<bean id="ProductDao" class="dao.jpa.JpaProductDao"/>

Los tres casos son accedidos por el nombre "ProductDao". Esto quiere decir que nuestra clase DAOFactory, deberá tener el siguiente código:

public class DaoFactory {

private static DaoFactory instance;

private synchronized static DaoFactory newInstance() {
return new DaoFactory();
}
private BeanFactory factory;

private DaoFactory() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/beans.xml");
factory = (BeanFactory) ctx;
}

public static DaoFactory getInstance() {
if (instance == null) {
instance = newInstance();
}
return instance;
}

public ProductDao getProductDao() {
return (ProductDao) factory.getBean("ProductDao");
}
}

La interfaz Dao: ProductoDao

ProductDao es una interfaz que solo tiene los métodos a implementar. No tiene nada más. Así que nuestro DaoFactory no sabrá qué devolverá el método factory.getBean(). Solo sabe que lo que devuelva será una interfaz ProductDao.

public interface ProductDao {

List getProducts();
}

El cliente: ProductManagedBean

El objeto que usará el DAOFactory para acceder a la base de datos, queda reducido a lo siguiente:

    private ProductDao productDao;
private List list;

public ProductManagedBean() {
productDao = DaoFactory.getInstance().getProductDao();
}

public List getProducts() {
if (list == null) {
list = productDao.getProducts();
}
return list;
}

En nuestra aplicación es el ManagedBean encargado de mostrar el listado en el JSP.

Proyectos utilizados

 
< Anterior   Siguiente >