Apartado 3.4: Implementación de los Casos de Uso con Spring
Apartado 3.4: Implementación de los Casos de Uso con Spring
Apartado 3.4: Implementación de los Casos de Uso con Spring
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>3.4</strong> Implementación <strong>de</strong> <strong>los</strong><br />
<strong>Casos</strong> <strong>de</strong> <strong>Uso</strong> <strong>con</strong> <strong>Spring</strong>
Índice<br />
• Introducción a <strong>Spring</strong><br />
• Declaración y Configuración <strong>de</strong> “beans”<br />
• Excepciones <strong>de</strong> Persistencia<br />
• Declaración <strong>de</strong> DataSources<br />
• Integración <strong>con</strong> Hibernate 3<br />
• Gestión <strong>de</strong> Transacciones
¿Qué es <strong>Spring</strong>? (1)<br />
• Framework <strong>de</strong> código abierto creado por Rod<br />
Johnson<br />
• http://www.springframework.org<br />
• Motivación: Facilitar el <strong>de</strong>sarrollo <strong>de</strong> aplicaciones<br />
Java EE, promoviendo buenas prácticas <strong>de</strong> diseño y<br />
programación<br />
• Simplifica el uso <strong>de</strong> muchas <strong>de</strong> las APIs <strong>de</strong> Java EE<br />
• Dispone <strong>de</strong> alternativas a algunas <strong>de</strong> las APIs <strong>de</strong> Java EE<br />
• Internamente se apoyan en APIs <strong>de</strong> Java EE <strong>de</strong> más bajo nivel<br />
• Facilita el uso <strong>de</strong> patrones <strong>de</strong> diseño ampliamente<br />
re<strong>con</strong>ocidos <strong>de</strong>ntro <strong>de</strong> la industria <strong>de</strong>l <strong>de</strong>sarrollo <strong>de</strong> software<br />
(Factory, Abstract Factory, Buil<strong>de</strong>r, Decorator, Service<br />
Locator, etc.)<br />
• Soporte para capa mo<strong>de</strong>lo e interfaz Web<br />
• Es modular: es posible usar algunos <strong>de</strong> <strong>los</strong> módu<strong>los</strong> sin<br />
comprometerse <strong>con</strong> el uso <strong>de</strong>l resto
¿Qué es <strong>Spring</strong>? (y 2)<br />
• Nosotros utilizaremos el soporte <strong>de</strong> <strong>Spring</strong> para<br />
implementar casos <strong>de</strong> uso a nivel <strong>de</strong> capa mo<strong>de</strong>lo<br />
• Alternativa al uso <strong>de</strong> <strong>los</strong> Session Bean <strong>de</strong> EJB<br />
• Pue<strong>de</strong> actuar como una capa <strong>de</strong> integración entre<br />
diferentes APIs (JDBC, JNDI, etc.) y frameworks<br />
• En nuestro caso entre Tapestry e Hibernate
Conceptos Básicos en <strong>Spring</strong><br />
• Inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias (Depen<strong>de</strong>ncy Injection,<br />
DI)<br />
• Programación orientada a aspectos (Aspect-Oriented<br />
Programming, AOP)
Inyección <strong>de</strong> Depen<strong>de</strong>ncias<br />
• Tradicionalmente cada objeto es responsable <strong>de</strong><br />
obtener sus propias referencias a <strong>los</strong> objetos <strong>con</strong> <strong>los</strong><br />
que colabora<br />
• Cuando se aplica la Inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias (DI),<br />
alguna entidad externa es la responsable <strong>de</strong><br />
proporcionar a un objeto sus <strong>de</strong>pen<strong>de</strong>ncias cuando<br />
se crea el objeto (las <strong>de</strong>pen<strong>de</strong>ncias se inyectan en el<br />
objeto)<br />
• Ventajas<br />
• Pérdida <strong>de</strong> acoplamiento entre <strong>los</strong> objetos<br />
• Si un objeto <strong>con</strong>oce sus <strong>de</strong>pen<strong>de</strong>ncias a través <strong>de</strong> interfaces,<br />
es posible cambiar la implementación <strong>de</strong> esas <strong>de</strong>pen<strong>de</strong>ncias<br />
transparentemente para el objeto que las <strong>con</strong>tiene
Programación Orientada a Aspectos (AOP)<br />
• Técnica <strong>de</strong> programación que promueve la separación <strong>de</strong><br />
funcionalida<strong>de</strong>s<br />
• AOP: http://en.wikipedia.org/wiki/Aspect-oriented_programming<br />
• Algunos servicios son utilizados repetidas veces en diferentes<br />
componentes <strong>de</strong> un sistema, cuya responsabilidad principal es<br />
otra<br />
• E.g. servicios <strong>de</strong> logging o gestión <strong>de</strong> transacciones y seguridad<br />
• Se <strong>con</strong>ocen como aspectos transversales ("cross cutting <strong>con</strong>cerns”)<br />
• Un framework <strong>de</strong> AOP hace posible modularizar estos aspectos<br />
o servicios y aplicar<strong>los</strong> <strong>de</strong>clarativamente a <strong>los</strong> componentes<br />
que <strong>los</strong> precisen<br />
• Cada aspecto se implementa en un único punto<br />
• Declarativamente se especifican <strong>los</strong> métodos que el framework<br />
tiene que interceptar para aplicarles el o <strong>los</strong> aspectos que el<br />
<strong>de</strong>sarrollador <strong>de</strong>sea<br />
• Cada componente <strong>de</strong>be preocuparse únicamente <strong>de</strong> su<br />
funcionalidad principal sin preocuparse <strong>de</strong> <strong>los</strong> servicios <strong>de</strong>l sistema<br />
que precise
Módu<strong>los</strong>/Paquetes (1)<br />
DAO<br />
<strong>Spring</strong> JDBC<br />
Transaction<br />
management<br />
ORM<br />
Hibernate<br />
JPA<br />
JDO<br />
TopLink<br />
OJB<br />
iBatis<br />
AOP<br />
<strong>Spring</strong> AOP<br />
Integración AspectJ<br />
JEE<br />
JMX<br />
JMS<br />
JCA<br />
Remotig<br />
EJBs<br />
Email<br />
Web<br />
<strong>Spring</strong> Web MVC<br />
Framework Integration<br />
Struts<br />
WebWork<br />
Tapestry<br />
JSF<br />
Rich View Support<br />
JSPs<br />
Velocity<br />
FreeMarker<br />
PDF<br />
Jasper Reports<br />
Excel<br />
<strong>Spring</strong> Portlet MVC<br />
Core<br />
IoC Container
Módu<strong>los</strong>/Paquetes (2)<br />
• Core<br />
• Constituye la parte fundamental <strong>de</strong>l framework y<br />
proporciona la característica <strong>de</strong> Inyección <strong>de</strong> Depen<strong>de</strong>ncias<br />
(DI) / Inversión <strong>de</strong> Control (IoC)<br />
• Proporciona una implementación sofisticada <strong>de</strong>l patrón<br />
Factoría que permite <strong>de</strong>sacoplar la <strong>con</strong>figuración y<br />
especificación <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias <strong>de</strong> la lógica <strong>de</strong> la aplicación<br />
• DAO<br />
• Proporciona una capa <strong>de</strong> abstracción sobre JDBC que<br />
elimina la necesidad <strong>de</strong> codificar y analizar <strong>los</strong> códigos <strong>de</strong><br />
error específicos <strong>de</strong> cada BBDD<br />
• También proporciona una manera <strong>de</strong> gestionar<br />
transacciones tanto programática como <strong>de</strong>clarativamente,<br />
no sólo para clases que implementen ciertas interfaces, sino<br />
para cualquier objeto Java (POJO)
Módu<strong>los</strong>/Paquetes (y 3)<br />
• ORM<br />
• Proporciona capas <strong>de</strong> integración para las APIs <strong>de</strong><br />
mapeadores objeto-relacionales más populares: Hibernate,<br />
JPA, JDO, iBatis<br />
• Utilizando este paquete es posible utilizar cualquiera <strong>de</strong><br />
estos mapeadores objeto-relacionales en combinación <strong>con</strong><br />
las <strong>de</strong>más características que ofrece <strong>Spring</strong> (como por<br />
ejemplo <strong>con</strong> la gestión <strong>de</strong>clarativa <strong>de</strong> transacciones)<br />
• AOP<br />
• Proporciona una implementación <strong>de</strong>l paradigma <strong>de</strong> la<br />
programación orientada a aspectos (<strong>con</strong>forme a la AOP<br />
Alliance), que es utilizada, transparentemente para el<br />
programador, por parte otros paquetes <strong>de</strong> <strong>Spring</strong>, pero que<br />
también pue<strong>de</strong> ser usada directamente
El Contenedor (1)<br />
• El <strong>con</strong>tenedor <strong>de</strong> IoC es el núcleo <strong>de</strong>l sistema<br />
• Responsable <strong>de</strong> la creación y <strong>con</strong>figuración <strong>de</strong> <strong>los</strong> Beans<br />
• Nota: Un bean, en el <strong>con</strong>texto <strong>de</strong> <strong>Spring</strong>, es un POJO que es<br />
creado y manejado por el <strong>con</strong>tenedor <strong>de</strong> IoC<br />
• La interfaz BeanFactory o su hija<br />
ApplicationContext representan la interfaz <strong>de</strong>l<br />
<strong>con</strong>tenedor<br />
• <strong>Spring</strong> proporciona varias implementaciones
El Contenedor (2)<br />
• Instanciación<br />
try {<br />
ApplicationContext ctx =<br />
new ClassPathXmlApplicationContext(<br />
GlobalNames.SPRING_CONFIG_FILE_LOCATION);<br />
AccountService accountService =<br />
(AccountService) ctx.getBean("accountService");<br />
...<br />
} catch (Exception e) {<br />
}<br />
e.printStackTrace();
El Contenedor (y 3)<br />
• ClassPathXmlApplicationContext<br />
• Permite <strong>de</strong>clarar <strong>los</strong> objetos que componen la aplicación, y<br />
las <strong>de</strong>pen<strong>de</strong>ncias entre el<strong>los</strong> en XML<br />
• A partir <strong>de</strong> <strong>los</strong> metadatos <strong>de</strong> <strong>con</strong>figuración en XML es capaz<br />
<strong>de</strong> crear y <strong>con</strong>figurar <strong>los</strong> objetos que componen la aplicación<br />
• A través <strong>de</strong>l método getBean es posible obtener una<br />
referencia a <strong>los</strong> objetos <strong>de</strong>clarados, a partir <strong>de</strong> su nombre
• Es equivalente a<br />
Declaración <strong>de</strong> Beans (1)<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Declaración <strong>de</strong> Beans (2)<br />
• Se <strong>de</strong>claran <strong>con</strong> la etiqueta bean<br />
• Parámetros básicos<br />
• id: Nombre o i<strong>de</strong>ntificador <strong>de</strong>l bean<br />
• class: Clase <strong>de</strong> implementación <strong>de</strong>l bean<br />
• Inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias basada en “setters”<br />
• Permite inyectar valores u otros beans (a través <strong>de</strong><br />
referencias), invocando al método set correspondiente <strong>de</strong>l<br />
bean sobre el que se está realizando la inyección<br />
• Se indica el nombre <strong>de</strong> la propiedad que se <strong>de</strong>sea inyectar y el<br />
valor que se le <strong>de</strong>sea proporcionar
Declaración <strong>de</strong> Beans (y 3)<br />
• Inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias basada en “setters”<br />
(<strong>con</strong>t)<br />
• Es posible especificarlas<br />
• A través <strong>de</strong> un elemento anidado property, que acepta <strong>los</strong><br />
siguientes atributos<br />
• name: Nombre <strong>de</strong> la propiedad don<strong>de</strong> se <strong>de</strong>sea inyectar el valor<br />
• value: Para inyectar un valor <strong>con</strong>stante<br />
• ref: Para inyectar otro bean a partir <strong>de</strong> su nombre<br />
• Con sintaxis abreviada (utilizando el espacio <strong>de</strong> nombres p) a<br />
través <strong>de</strong> <strong>los</strong> atributos<br />
• p:nombrePropiedad: Para inyectar un valor <strong>con</strong>stante en la<br />
propiedad indicada<br />
• p:nombrePropiedad-ref: Para inyectar otro bean a partir <strong>de</strong><br />
su nombre en la propiedad indicada<br />
• El bean se crea a partir <strong>de</strong> su <strong>con</strong>structor vacío y a<br />
<strong>con</strong>tinuación se invocan <strong>los</strong> métodos set <strong>con</strong> <strong>los</strong> valores<br />
a<strong>de</strong>cuados
Beans <strong>de</strong> la capa mo<strong>de</strong>lo <strong>de</strong> MiniBank (spring-<strong>con</strong>fig.xml)<br />
<br />
Beans <strong>de</strong> la capa mo<strong>de</strong>lo <strong>de</strong> MiniBank<br />
• Declaramos <strong>los</strong> siguientes beans<br />
• Un bean para cada DAO Hibernate<br />
• Un bean para la implementación <strong>de</strong> la fachada<br />
• Un bean para la SesionFactory (que usan <strong>los</strong> DAOs <strong>de</strong><br />
Hibernate)<br />
• Depen<strong>de</strong>ncias entre beans<br />
• La fachada usa <strong>los</strong> DAOs<br />
• Los DAOs usan la SesionFactory<br />
• Utilizamos la DI <strong>de</strong> <strong>Spring</strong> basada en métodos set<br />
para resolver las <strong>de</strong>pen<strong>de</strong>ncias entre beans
AccountServiceImpl.java<br />
public class AccountServiceImpl implements AccountService {<br />
private AccountDao accountDao;<br />
private AccountOperationDao accountOperationDao;<br />
public void setAccountDao(AccountDao accountDao) {<br />
this.accountDao = accountDao;<br />
}<br />
public void setAccountOperationDao(<br />
AccountOperationDao accountOperationDao) {<br />
this.accountOperationDao = accountOperationDao;<br />
}<br />
}<br />
...
AccountDaoHibernate.java y<br />
AccountDaoOperationHibernate.java<br />
public class AccountDaoHibernate extends<br />
GenericDaoHibernate<br />
implements AccountDao {<br />
}<br />
...<br />
public class AccountOperationDaoHibernate<br />
extends GenericDaoHibernate<br />
implements AccountOperationDao {<br />
}<br />
...
GenericAccountDAO.java<br />
public class GenericDaoHibernate<br />
implements GenericDao {<br />
}<br />
private SessionFactory sessionFactory;<br />
...<br />
public void setSessionFactory(SessionFactory sessionFactory) {<br />
this.sessionFactory = sessionFactory;<br />
}<br />
...
Beans: Inyección <strong>de</strong> Depen<strong>de</strong>ncias<br />
accountDao<br />
AccountDaoHibernate<br />
- sessionFactory : SessionFactory<br />
accountService<br />
+ métodos set<br />
AccountServiceImpl<br />
- accountDao : AccountDao<br />
- accountOperationDao :<br />
AccountOperationDao<br />
sessionFactory<br />
+ métodos set<br />
accountOperationDao<br />
AccountOperationDaoHibernate<br />
- sessionFactory : SessionFactory<br />
+ métodos set
Ámbito <strong>de</strong> <strong>los</strong> Beans<br />
• El ámbito <strong>de</strong> un bean se especifica a través <strong>de</strong>l<br />
atributo scope <strong>de</strong> la etiqueta bean<br />
• singleton (valor por <strong>de</strong>fecto)<br />
• El <strong>con</strong>tenedor usa siempre la misma instancia (ya sea cuando<br />
se le pi<strong>de</strong> a través <strong>de</strong> la API o cuando necesita inyectarlo)<br />
• prototype<br />
• Indica que el <strong>con</strong>tenedor <strong>de</strong>be crear una nueva instancia <strong>de</strong>l<br />
bean cada vez que se precise una<br />
• Pue<strong>de</strong> ser necesario, por ejemplo, si el bean tiene estado<br />
• OJO <strong>con</strong> <strong>los</strong> beans <strong>de</strong> tipo singleton <strong>con</strong><br />
<strong>de</strong>pen<strong>de</strong>ncias <strong>con</strong> beans <strong>de</strong> tipo prototype
Más sobre Declaración <strong>de</strong> Beans<br />
• Otras funcionalida<strong>de</strong>s que ofrece <strong>Spring</strong><br />
• Instanciación <strong>de</strong> beans a partir <strong>de</strong> factorías<br />
• Inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias a través <strong>de</strong> <strong>con</strong>structores<br />
• Los valores o referencias a otros beans se inyectan a través <strong>de</strong><br />
<strong>los</strong> argumentos <strong>de</strong> un <strong>con</strong>structor<br />
• Inyección <strong>de</strong> propieda<strong>de</strong>s multivaluadas (listas, <strong>con</strong>juntos,<br />
mapas)<br />
• Autoinyección (autowiring)<br />
• Por nombre: Busca un bean <strong>con</strong> el mismo id que la propiedad<br />
• Por tipo: Busca un bean <strong>con</strong> el mismo tipo que la propiedad<br />
• Por <strong>con</strong>structor: Busca uno o más beans cuyos tipos coincidan<br />
<strong>con</strong> <strong>los</strong> parámetros <strong>de</strong> uno <strong>de</strong> <strong>los</strong> <strong>con</strong>structores <strong>de</strong> ese bean<br />
• Inicialización y liberación <strong>de</strong> recursos <strong>de</strong> un bean a través <strong>de</strong><br />
métodos que <strong>de</strong>ben ser invocados justo <strong>de</strong>spués <strong>de</strong> haberse<br />
creado y justo antes <strong>de</strong> ser <strong>de</strong>struido respectivamente<br />
• Etc.
Excepciones <strong>de</strong> Persistencia (1)<br />
• En JDBC se lanza la excepción<br />
java.sql.SQLException cuando se produce<br />
cualquier tipo <strong>de</strong> error en el acceso a <strong>los</strong> datos<br />
• Problema: Hay que capturarla siempre y analizarla para<br />
saber <strong>de</strong> qué tipo <strong>de</strong> error se trata<br />
• Algunos frameworks (e.g. Hibernate) ofrecen una<br />
jerarquía <strong>de</strong> excepciones más <strong>de</strong>scriptiva (una<br />
excepción diferente para cada tipo <strong>de</strong> error)<br />
• Ventaja: Permite diferenciar entre qué tipos <strong>de</strong> errores<br />
capturar<br />
• Problema: Son específicas <strong>de</strong>l framework utilizado para<br />
realizar la persistencia <strong>de</strong> <strong>los</strong> datos
Excepciones <strong>de</strong> Persistencia (y 2)<br />
• <strong>Spring</strong> proporciona una jerarquía <strong>de</strong> excepciones <strong>de</strong><br />
acceso a datos (heredan <strong>de</strong><br />
DataAccessException) que resuelve ambos<br />
problemas:<br />
• Cada excepción representa un error <strong>con</strong>creto<br />
• No son específicas <strong>de</strong>l framework <strong>de</strong> persistencia <strong>de</strong> datos<br />
utilizado, por tanto se oculta a las capas superiores<br />
• Son excepciones unchecked<br />
• Para que <strong>Spring</strong> realice la <strong>con</strong>versión entre las<br />
excepciones nativas y la jerarquía propia es necesario<br />
<strong>de</strong>clarar el siguiente bean:<br />
DataSources<br />
• In<strong>de</strong>pendientemente <strong>de</strong>l framework <strong>de</strong> persistencia<br />
utilizado probablemente se necesitará <strong>con</strong>figurar una<br />
referencia a un DataSource<br />
• <strong>Spring</strong> proporciona, entre otras, las siguientes<br />
opciones para <strong>con</strong>figurar un bean <strong>de</strong> tipo<br />
DataSource<br />
• DataSources <strong>de</strong>finidos directamente sobre un driver JDBC<br />
• DataSources que son localizados vía JNDI<br />
• Cualquier <strong>con</strong>tenedor Java EE pue<strong>de</strong> poner accesible vía JNDI<br />
un DataSource (que normalmente implementará pool <strong>de</strong><br />
<strong>con</strong>exiones)
DataSources JDBC<br />
<br />
• DriverManagerDataSource: Devuelve una nueva<br />
<strong>con</strong>exión a la BD cada vez que se le pi<strong>de</strong> una<br />
• Es <strong>de</strong>cir, no implementa la estrategia pool <strong>de</strong> <strong>con</strong>exiones<br />
(<strong>con</strong>ceptualmente similar al SimpleDataSource estudiado<br />
en el apartado 3.1)<br />
• Debe indicársele como propieda<strong>de</strong>s<br />
• El nombre <strong>de</strong> la clase <strong>de</strong>l driver JDBC<br />
• La URL <strong>de</strong> <strong>con</strong>exión a la BD<br />
• El usuario para <strong>con</strong>ectarse a la BD<br />
• La <strong>con</strong>traseña <strong>de</strong>l usuario indicado<br />
• Útil para tests <strong>de</strong> integración
DataSources JNDI (1)<br />
• JNDI (Java Naming and Directory Interface)<br />
• Familia <strong>de</strong> paquetes javax.naming (Java SE)<br />
• Es una API que abstrae el acceso a un servicio <strong>de</strong> nombres y<br />
directorios (e.g. LDAP)<br />
• Es posible registrar objetos mediante un nombre jerárquico<br />
• Los servidores <strong>de</strong> aplicaciones Java EE exponen diversos objetos<br />
mediante JDNI<br />
• Los servidores <strong>de</strong> aplicaciones Java EE proporcionan<br />
implementaciones <strong>de</strong> DataSource<br />
• Cada objeto DataSource es accesible a partir <strong>de</strong> un nombre JNDI<br />
<strong>de</strong> la forma java:comp/env/XXX/ZZZ, don<strong>de</strong> XXX suele<br />
(recomendado) ser "jdbc" e YYY el nombre <strong>de</strong> un DataSource<br />
<strong>con</strong>creto<br />
• Habitualmente estos objetos DataSource implementan la estrategia<br />
<strong>de</strong> pool <strong>de</strong> <strong>con</strong>exiones<br />
• Requiere <strong>con</strong>figuración en el servidor (driver, URL, usuario,<br />
<strong>con</strong>traseña, parámetros específicos al pool, etc.)<br />
• Ej.: En Tomcat se <strong>de</strong>finen en <strong>con</strong>f/server.xml
DataSources JNDI (y 2)<br />
<br />
• El atributo jndiName se utiliza para indicar el nombre <strong>de</strong>l<br />
recurso accesible vía JNDI<br />
• Si la aplicación está ejecutándose <strong>de</strong>ntro <strong>de</strong> un servidor <strong>de</strong><br />
aplicaciones Java EE<br />
• El valor <strong>de</strong>l atributo resourceRef <strong>de</strong>be ser true<br />
• El nombre indicado en jndiName es relativo al <strong>con</strong>texto<br />
java:comp/env/<br />
• JndiObjectFactoryBean implementa<br />
org.springframework.beans.factory.FactoryBean<br />
• Los beans que implementan esta interfaz se <strong>de</strong>finen igual que el<br />
resto <strong>de</strong> beans, pero cuando se referencian <strong>de</strong>s<strong>de</strong> otros beans no<br />
se inyecta una instancia <strong>de</strong> ese tipo sino el objeto que <strong>de</strong>vuelve su<br />
método getObject<br />
• El método getObject <strong>de</strong> JndiObjectFactoryBean <strong>de</strong>vuelve el<br />
objeto asociado al nombre JNDI especificado en la <strong>con</strong>figuración
Integración <strong>con</strong> Hibernate 3 (1)<br />
• Los DAOs implementados <strong>con</strong> Hibernate necesitan un objeto <strong>de</strong><br />
tipo org.hibernate.SesionFactory <strong>de</strong>l que obtener la<br />
sesión actual<br />
• Como veremos más a<strong>de</strong>lante el gestor <strong>de</strong> transacciones <strong>de</strong><br />
Hibernate también precisa un objeto <strong>de</strong> ese tipo<br />
• La siguiente <strong>de</strong>claración permite <strong>de</strong>finir un bean para obtener<br />
un SessionFactory que utiliza las anotaciones <strong>de</strong> Hibernate<br />
en las entida<strong>de</strong>s<br />
Integración <strong>con</strong> Hibernate 3 (y 2)<br />
• Como ya hemos visto <strong>con</strong> anterioridad el bean<br />
sesionFactory se inyecta en <strong>los</strong> DAOs<br />
<br />
Transacciones<br />
• <strong>Spring</strong> no gestiona directamente las transacciones, sino que lo<br />
hace a través <strong>de</strong> una abstracción (patrón Strategy)<br />
• Interfaz<br />
org.springframework.transaction.PlatformTransactionManager<br />
• Se pue<strong>de</strong> trabajar <strong>con</strong> las transacciones in<strong>de</strong>pendientemente <strong>de</strong> la<br />
implementación <strong>de</strong>l gestor <strong>de</strong> transacciones <strong>con</strong>creto que se esté<br />
utilizando<br />
• <strong>Spring</strong> proporciona una serie <strong>de</strong> gestores <strong>de</strong> transacciones que<br />
<strong>de</strong>legan la responsabilidad <strong>de</strong> la gestión <strong>de</strong> las transacciones a<br />
implementaciones específicas <strong>de</strong> la plataforma utilizada<br />
• Se pue<strong>de</strong> trabajar <strong>con</strong> transacciones a través <strong>de</strong> la API Java <strong>de</strong><br />
<strong>Spring</strong>, pero también se pue<strong>de</strong>n <strong>de</strong>finir <strong>de</strong> forma <strong>de</strong>clarativa<br />
haciendo uso <strong>de</strong>l framework AOP <strong>de</strong> <strong>Spring</strong>, que incluye una<br />
implementación <strong>de</strong>l aspecto <strong>de</strong> transaccionalidad<br />
• Es posible hacerlo mediante XML o mediante anotaciones Java<br />
• Utilizaremos la opción <strong>de</strong> las anotaciones porque que simplifica la<br />
<strong>de</strong>claración <strong>de</strong> las transacciones
Transacciones <strong>con</strong> Hibernate 3<br />
• Si la capa <strong>de</strong> persistencia <strong>de</strong>l mo<strong>de</strong>lo está implementada <strong>con</strong><br />
Hibernate, entonces el gestor <strong>de</strong> transacciones a utilizar es el<br />
siguiente<br />
API Transacciones (1)<br />
public interface PlatformTransactionManager {<br />
TransactionStatus getTransaction(TransactionDefinition <strong>de</strong>finition)<br />
throws TransactionException;<br />
void commit(TransactionStatus status)<br />
throws TransactionException;<br />
}<br />
void rollback(TransactionStatus status)<br />
throws TransactionException;<br />
public interface TransactionStatus {<br />
boolean isNewTransaction();<br />
void setRollbackOnly();<br />
}<br />
boolean isRollbackOnly();
API Transacciones (y 2)<br />
• Un gestor <strong>de</strong> transacciones implementa la interfaz<br />
PlatformTransactionManager<br />
• TransactionException es una excepción unchecked<br />
• El método getTransaction <strong>de</strong>vuelve un objeto <strong>de</strong> tipo<br />
TransactionStatus en función <strong>de</strong> un parámetro <strong>de</strong> tipo<br />
TransactionDefinition<br />
• TransactionStatus representa una transacción<br />
• Pue<strong>de</strong> ser la transacción actual o una nueva<br />
• TransactionDefinition es una interfaz a través <strong>de</strong> la cual<br />
se pue<strong>de</strong>n especificar las características <strong>de</strong> la transacción que<br />
se quiere obtener (propagación, nivel <strong>de</strong> aislamiento, timeout,<br />
propiedad read-only)<br />
• La subinterfaz TransactionAttribute permite, a<strong>de</strong>más,<br />
especificar la lista <strong>de</strong> excepciones que provocan o no un rollback<br />
• Para utilizar <strong>los</strong> valores por <strong>de</strong>fecto pue<strong>de</strong> ser “null”<br />
• Los métodos commit y rollback se utilizan para hacer un<br />
commit o un rollback <strong>de</strong> la transacción que se les pasa
AOP: Gestión <strong>de</strong> Transacciones (1)<br />
public class AccountServiceImpl implements AccountService {<br />
static {<br />
ApplicationContext <strong>con</strong>text = ...;<br />
transactionManager = (PlatformTransactionManager) <strong>con</strong>text<br />
.getBean("transactionManager");<br />
}<br />
private static PlatformTransactionManager transactionManager;<br />
private AccountDao accountDao;<br />
private AccountOperationDao accountOperationDao;<br />
public void setAccountDao(AccountDao accountDao) {<br />
this.accountDao = accountDao;<br />
}<br />
public void setAccountOperationDao(<br />
AccountOperationDao accountOperationDao) {<br />
this.accountOperationDao = accountOperationDao;<br />
}
AOP: Gestión <strong>de</strong> Transacciones (2)<br />
public Account createAccount(Account account) {<br />
TransactionStatus transactionStatus =<br />
transactionManager.getTransaction(null);<br />
Iniciar<br />
transacción<br />
try {<br />
accountDao.create(account);<br />
} catch (RuntimeException e) {<br />
transactionManager.rollback(transactionStatus);<br />
}<br />
throw e;<br />
transactionManager.commit(transactionStatus);<br />
Finalizar<br />
transacción<br />
return account;<br />
}
AOP: Gestión <strong>de</strong> Transacciones (3)<br />
public void removeAccount(Long accountId)<br />
throws InstanceNotFoundException {<br />
TransactionStatus transactionStatus =<br />
transactionManager.getTransaction(null);<br />
Iniciar<br />
transacción<br />
try {<br />
accountDao.remove(accountId);<br />
} catch (RuntimeException e) {<br />
transactionManager.rollback(transactionStatus);<br />
throw e;<br />
} catch (InstanceNotFoundException e) {<br />
transactionManager.commit(transactionStatus);<br />
throw e;<br />
}<br />
transactionManager.commit(transactionStatus);<br />
Finalizar<br />
transacción<br />
}
AOP: Gestión <strong>de</strong> Transacciones (4)<br />
public void withdrawFromAccount(Long accountId, double amount)<br />
throws InstanceNotFoundException, InsufficientBalanceException {<br />
TransactionStatus transactionStatus =<br />
transactionManager.getTransaction(null);<br />
Iniciar<br />
transacción<br />
try {<br />
/* Find account. */<br />
Account account = accountDao.find(accountId);<br />
/* Modify balance. */<br />
double currentBalance = account.getBalance();<br />
if (currentBalance < amount) {<br />
throw new InsufficientBalanceException(accountId,<br />
currentBalance,amount);<br />
}<br />
account.setBalance(currentBalance - amount);<br />
accountDao.update(account);
AOP: Gestión <strong>de</strong> Transacciones (5)<br />
/* Register account operation. */<br />
accountOperationDao.create(new AccountOperation(account,<br />
Calendar.getInstance(), AccountOperation.Type.WITHDRAW,<br />
amount));<br />
} catch (RuntimeException e) {<br />
transactionManager.rollback(transactionStatus);<br />
throw e;<br />
} catch (InstanceNotFoundException e) {<br />
transactionManager.commit(transactionStatus);<br />
throw e;<br />
} catch (InsufficientBalanceException e) {<br />
transactionManager.commit(transactionStatus);<br />
throw e;<br />
}<br />
transactionManager.commit(transactionStatus);<br />
Finalizar<br />
transacción<br />
}<br />
...<br />
}
AOP: Gestión <strong>de</strong> Transacciones (6)<br />
• El código para todos <strong>los</strong> casos <strong>de</strong> uso transaccionales<br />
es similar:<br />
• Iniciar la transacción (<strong>con</strong> las propieda<strong>de</strong>s a<strong>de</strong>cuadas) a<br />
partir <strong>de</strong>l gestor <strong>de</strong> transacciones utilizado<br />
• El gestor <strong>de</strong> transacciones pue<strong>de</strong> <strong>de</strong>clararse en el archivo <strong>de</strong><br />
<strong>con</strong>figuración <strong>de</strong> <strong>Spring</strong> como un bean y obtenerlo a través <strong>de</strong>l<br />
método getBean <strong>de</strong>spués <strong>de</strong> instanciar el <strong>con</strong>tenedor<br />
• Ejecutar la lógica propia <strong>de</strong>l caso <strong>de</strong> uso<br />
• Finalizar transacción (commit o rollback) en función <strong>de</strong> si se<br />
ha producido alguna excepción o no y <strong>de</strong> su tipo<br />
• El código común para la gestión <strong>de</strong> las transacciones<br />
pue<strong>de</strong> eliminarse <strong>de</strong> la implementación <strong>de</strong> todos <strong>los</strong><br />
casos <strong>de</strong> uso que lo precisen, y <strong>de</strong>clarativamente<br />
<strong>de</strong>cir cuándo <strong>de</strong>be ejecutarse
AOP: Gestión <strong>de</strong> Transacciones (y 7)<br />
• Declarativamente es posible indicar<br />
• El gestor <strong>de</strong> transacciones a utilizar<br />
• Que <strong>los</strong> métodos createAccount, removeAccount y<br />
todos <strong>los</strong> <strong>de</strong>más <strong>de</strong> la clase AccountServiceImpl son<br />
transaccionales<br />
• <strong>Spring</strong> intercepta las invocaciones a <strong>los</strong> métodos <strong>de</strong>clarados<br />
como transaccionales:<br />
• [Antes <strong>de</strong> que se ejecute el método] Ejecuta el código<br />
necesario para comenzar la transacción<br />
• Declarativamente pue<strong>de</strong>n indicarse parámetros como, por<br />
ejemplo, el nivel <strong>de</strong> aislamiento <strong>de</strong>seado para la transacción<br />
• [Después <strong>de</strong> que se ejecute el método] Ejecuta el código<br />
necesario para finalizar la transacción (ya sea <strong>con</strong> un commit o<br />
un rollback)<br />
• Pue<strong>de</strong>n indicarse qué excepciones <strong>de</strong>ben provocar un rollback y<br />
cuales no
AccountServiceImpl.java (1)<br />
@Transactional<br />
public class AccountServiceImpl implements AccountService {<br />
private AccountDao accountDao;<br />
private AccountOperationDao accountOperationDao;<br />
public void setAccountDao(AccountDao accountDao) {<br />
this.accountDao = accountDao;<br />
}<br />
public void setAccountOperationDao(<br />
AccountOperationDao accountOperationDao) {<br />
this.accountOperationDao = accountOperationDao;<br />
}<br />
public Account createAccount(Account account) { ... }<br />
@Transactional(readOnly = true)<br />
public Account findAccount(Long accountId)<br />
throws InstanceNotFoundException { ... }<br />
public void addToAccount(Long accountId, double amount)<br />
throws InstanceNotFoundException { ... }
AccountServiceImpl.java (2)<br />
public void withdrawFromAccount(Long accountId, double amount)<br />
throws InstanceNotFoundException, InsufficientBalanceException {<br />
/* Find account. */<br />
Account account = accountDao.find(accountId);<br />
/* Modify balance. */<br />
double currentBalance = account.getBalance();<br />
if (currentBalance < amount) {<br />
throw new InsufficientBalanceException(accountId,<br />
currentBalance,amount);<br />
}<br />
account.setBalance(currentBalance - amount);<br />
accountDao.update(account);<br />
}<br />
/* Register account operation. */<br />
accountOperationDao.create(new AccountOperation(account,<br />
Calendar.getInstance(), AccountOperation.Type.WITHDRAW,<br />
amount));
AccountServiceImpl.java (3)<br />
@Transactional(readOnly = true)<br />
public AccountBlock findAccountsByUserId(Long userId,<br />
int startIn<strong>de</strong>x, int count) { ... }<br />
public void removeAccount(Long accountId)<br />
throws InstanceNotFoundException { ... }<br />
public void transfer(Long sourceAccountId,<br />
Long <strong>de</strong>stinationAccountId, double amount)<br />
throws InstanceNotFoundException,<br />
InsufficientBalanceException { ... }<br />
@Transactional(readOnly = true)<br />
public int getNumberOfAccountOperations(Long accountId,<br />
Calendar startDate, Calendar endDate)<br />
throws InstanceNotFoundException { ... }
AccountServiceImpl.java (y 4)<br />
@Transactional(readOnly = true)<br />
public List findAccountOperationsByDate(<br />
Long accountId, Calendar startDate, Calendar endDate,<br />
int startIn<strong>de</strong>x, int count)<br />
throws InstanceNotFoundException { ... }<br />
private List toAccountOperationInfos(<br />
List accountOperations) { ... }<br />
}
Transacciones <strong>con</strong> Anotaciones<br />
• Si se <strong>de</strong>sean utilizar anotaciones para <strong>de</strong>clarar las<br />
transacciones, el gestor <strong>de</strong> transacciones a utilizar se<br />
<strong>de</strong>clara a través <strong>de</strong> la etiqueta annotation-driven,<br />
perteneciente al espacio <strong>de</strong> nombres tx<br />
<br />
• El valor <strong>de</strong>l atributo transaction-manager indica<br />
el nombre <strong>de</strong>l bean que <strong>de</strong>be ser utilizado como<br />
gestor <strong>de</strong> transacciones<br />
• Este atributo es opcional, y si no está presente toma el valor<br />
por <strong>de</strong>fecto “transactionManager”<br />
• Por tanto en nuestro caso podríamos no haberlo<br />
especificado
Anotación @Transactional (1)<br />
• Pue<strong>de</strong> utilizarse para anotar una clase o un método <strong>con</strong>creto<br />
• En una clase se aplica a todos <strong>los</strong> métodos que <strong>con</strong>tiene<br />
• En un método sobrescribe el comportamiento especificado para la<br />
clase a la que pertenece<br />
• Propieda<strong>de</strong>s:<br />
• propagation:<br />
• PROPAGATION_REQUIRED (valor por <strong>de</strong>fecto): El método <strong>de</strong>be<br />
ejecutarse <strong>de</strong>ntro <strong>de</strong> una transacción; si ya existe una se ejecuta en<br />
esa y si no se crea una nueva<br />
• PROPAGATION_MANDATORY: Igual que el anterior pero la transacción<br />
<strong>de</strong>be existir (si no se lanza una excepción)<br />
• PROPAGATION_REQUIRES_NEW: El método <strong>de</strong>be ejecutarse <strong>de</strong>ntro <strong>de</strong><br />
una transacción nueva (si ya existe una se suspen<strong>de</strong> mientras)<br />
• PROPAGATION_NESTED: El método <strong>de</strong>be ejecutarse en una<br />
transacción anidada si ya existe una transacción en progreso. Si no<br />
existe se comporta igual que PROPAGATION_REQUIRED<br />
• PROPAGATION_NEVER: El método no <strong>de</strong>be ejecutarse <strong>de</strong>ntro <strong>de</strong> una<br />
transacción (si existe una en progreso se lanza una excepción)<br />
• PROPAGATION_NOT_SUPPORTED: Igual que el anterior pero si existe<br />
una transacción se suspen<strong>de</strong> en vez <strong>de</strong> lanzar una excepción<br />
• PROPAGATION_SUPPORTS: No es necesario que el método se ejecute<br />
<strong>de</strong>ntro <strong>de</strong> una transacción pero si ya existe una se ejecuta <strong>de</strong>ntro <strong>de</strong><br />
ella
Anotación @Transactional (2)<br />
• Propieda<strong>de</strong>s (<strong>con</strong>t.):<br />
• isolation: Nivel <strong>de</strong> aislamiento (por <strong>de</strong>fecto el <strong>de</strong> la<br />
plataforma)<br />
• ISOLATION_DEFAULT: El <strong>de</strong> la plataforma<br />
• ISOLATION_READ_UNCOMMITED: Pue<strong>de</strong>n ocurrir “dirty reads”,<br />
“non-repeatable reads” y “phamton reads”<br />
• ISOLATION_READ_COMMITED: Pue<strong>de</strong>n ocurrir “nonrepeatable<br />
reads” y “phamton reads”<br />
• ISOLATION_REPETEABLE_READ: Pue<strong>de</strong>n ocurrir “phamton<br />
reads”<br />
• ISOLATION_SERIALIZABLE: Elimina todos <strong>los</strong> problemas <strong>de</strong><br />
<strong>con</strong>currencia<br />
• readOnly: Lectura/escritura (<strong>de</strong>fecto) o solo lectura<br />
• Importante indicarlo para po<strong>de</strong>r hacer optimizaciones !!<br />
• timeout: Timeout <strong>de</strong> la transacción (por <strong>de</strong>fecto el <strong>de</strong> la<br />
plataforma)<br />
• Si el timeout expira entonces se hace un rollback <strong>de</strong> la<br />
transacción
Anotación @Transactional (y 3)<br />
• Por <strong>de</strong>fecto cualquier excepción <strong>de</strong> tipo “unchecked” (hija <strong>de</strong><br />
RuntimeException) provocará un rollback, y cualquier<br />
excepción “checked” (hija <strong>de</strong> Exception) no<br />
• Es posible modificar este comportamiento a través <strong>de</strong> propieda<strong>de</strong>s<br />
<strong>de</strong> la anotación @Transactional<br />
• rollbackFor/rollBackForClassName: Array <strong>de</strong> clases/nombres<br />
<strong>de</strong> excepciones que <strong>de</strong>ben causar un rollback<br />
• Ejemp<strong>los</strong>:<br />
@Transactional(rollbackFor={Exception1.class, Exception2.class})<br />
@Transactional(rollbackForClassName={"es.udc.Exception1", "es.udc.Exception2"})<br />
• noRollbackFor/noRollbackForClassName: Array <strong>de</strong><br />
clases/nombres <strong>de</strong> excepciones que no <strong>de</strong>ben causar un rollback<br />
• La utilizaremos en las fachadas <strong>de</strong>l mo<strong>de</strong>lo (servicios) para<br />
<strong>de</strong>clarar la transaccionalidad <strong>de</strong> sus métodos<br />
• Es posible anotar la interfaz que <strong>de</strong>clara las operaciones <strong>de</strong>l<br />
servicio, pero es más recomendable anotar la clase <strong>de</strong><br />
implementación <strong>de</strong>l servicio
Implementación <strong>de</strong> Fachadas (1)<br />
• Los casos <strong>de</strong> uso se implementan en términos <strong>de</strong><br />
DAOs (tal como se vio en el apartado 3.2)<br />
• En <strong>los</strong> DAOs se inyecta un objeto <strong>de</strong> tipo<br />
org.hibernate.SesionFactory proporcionado por<br />
<strong>Spring</strong><br />
• Los DAOs se inyectan en la clase <strong>de</strong> implementación <strong>de</strong> la<br />
Fachada<br />
• Se utiliza un mecanismo proporcionado por <strong>Spring</strong> para<br />
<strong>con</strong>vertir las excepciones <strong>de</strong> persistencia nativas a una<br />
jerarquía propia in<strong>de</strong>pendiente <strong>de</strong>l framework utilizado para<br />
la persistencia
Implementación <strong>de</strong> Fachadas (y 2)<br />
• Para indicar la transaccionalidad <strong>de</strong> <strong>los</strong> casos <strong>de</strong> uso<br />
utilizamos la anotación @Transactional sobre la<br />
clase <strong>de</strong> implementación <strong>de</strong> la Fachada<br />
• Se <strong>de</strong>clara un gestor <strong>de</strong> transacciones que <strong>de</strong>lega en el<br />
gestor <strong>de</strong> transacciones <strong>de</strong> Hibernate<br />
• Se le inyecta el mismo SesionFactory que el creado para<br />
inyectar en <strong>los</strong> DAOs para que pueda acce<strong>de</strong>r a través <strong>de</strong> él al<br />
gestor <strong>de</strong> transacciones <strong>de</strong> Hibernate<br />
• Se le inyecta un DataSource que se recupera vía JNDI<br />
(proporcionado por el <strong>con</strong>tenedor <strong>de</strong> aplicaciones)<br />
• La implementación <strong>de</strong> las Fachadas es in<strong>de</strong>pendiente<br />
<strong>de</strong>l software utilizado para el acceso a datos (en<br />
nuestro caso Hibernate)