54126935-3-4-implementaci

62
3.4 Implementación de los Casos de Uso con Spring

Transcript of 54126935-3-4-implementaci

Page 1: 54126935-3-4-implementaci

3.4 Implementación de los Casos de Uso con Spring

Page 2: 54126935-3-4-implementaci

Índice

Introducción a Spring

Declaración y Configuración de “beans”

Excepciones de Persistencia

Declaración de DataSources

Integración con Hibernate 3

Gestión de Transacciones

Page 3: 54126935-3-4-implementaci

¿Qué es Spring? (1)

Framework de código abierto creado por Rod Johnson http://www.springframework.org

Motivación: Facilitar el desarrollo de aplicaciones Java EE, promoviendo buenas prácticas de diseño y programación Simplifica el uso de muchas de las APIs de Java EE

Dispone de alternativas a algunas de las APIs de Java EE Internamente se apoyan en APIs de Java EE de más bajo nivel

Facilita el uso de patrones de diseño ampliamente reconocidos dentro de la industria del desarrollo de software (Factory, Abstract Factory, Builder, Decorator, Service Locator, etc.)

Soporte para capa modelo e interfaz Web Es modular: es posible usar algunos de los módulos sin

comprometerse con el uso del resto

Page 4: 54126935-3-4-implementaci

¿Qué es Spring? (y 2)

Nosotros utilizaremos el soporte de Spring para implementar casos de uso a nivel de capa modelo

Alternativa al uso de los Session Bean de EJB

Puede actuar como una capa de integración entre diferentes APIs (JDBC, JNDI, etc.) y frameworks

En nuestro caso entre Tapestry e Hibernate

Page 5: 54126935-3-4-implementaci

Conceptos Básicos en Spring

Inyección de dependencias (Dependency Injection, DI)

Programación orientada a aspectos (Aspect-Oriented Programming, AOP)

Page 6: 54126935-3-4-implementaci

Inyección de Dependencias

Tradicionalmente cada objeto es responsable de obtener sus propias referencias a los objetos con los que colabora

Cuando se aplica la Inyección de dependencias (DI), alguna entidad externa es la responsable de proporcionar a un objeto sus dependencias cuando se crea el objeto (las dependencias se inyectan en el objeto)

Ventajas

Pérdida de acoplamiento entre los objetos

Si un objeto conoce sus dependencias a través de interfaces, es posible cambiar la implementación de esas dependencias transparentemente para el objeto que las contiene

Page 7: 54126935-3-4-implementaci

Programación Orientada a Aspectos (AOP)

Técnica de programación que promueve la separación de funcionalidades AOP: http://en.wikipedia.org/wiki/Aspect-oriented_programming

Algunos servicios son utilizados repetidas veces en diferentes componentes de un sistema, cuya responsabilidad principal es otra E.g. servicios de logging o gestión de transacciones y seguridad

Se conocen como aspectos transversales ("cross cutting concerns”)

Un framework de AOP hace posible modularizar estos aspectos o servicios y aplicarlos declarativamente a los componentes que los precisen Cada aspecto se implementa en un único punto

Declarativamente se especifican los métodos que el framework tiene que interceptar para aplicarles el o los aspectos que el desarrollador desea

Cada componente debe preocuparse únicamente de su funcionalidad principal sin preocuparse de los servicios del sistema que precise

Page 8: 54126935-3-4-implementaci

Módulos/Paquetes (1)

CoreIoC Container

AOPSpring AOP

Integración AspectJ

DAOSpring JDBCTransaction management

ORMHibernate

JPAJDO

TopLinkOJB

iBatis

JEEJMXJMSJCA

RemotigEJBsEmail

WebSpring Web MVC

Framework IntegrationStruts

WebWorkTapestry

JSFRich View Support

JSPsVelocity

FreeMarkerPDF

Jasper ReportsExcel

Spring Portlet MVC

Page 9: 54126935-3-4-implementaci

Módulos/Paquetes (2)

Core Constituye la parte fundamental del framework y

proporciona la característica de Inyección de Dependencias (DI) / Inversión de Control (IoC)

Proporciona una implementación sofisticada del patrón Factoría que permite desacoplar la configuración y especificación de dependencias de la lógica de la aplicación

DAO Proporciona una capa de abstracción sobre JDBC que

elimina la necesidad de codificar y analizar los códigos de error específicos de cada BBDD

También proporciona una manera de gestionar transacciones tanto programática como declarativamente, no sólo para clases que implementen ciertas interfaces, sino para cualquier objeto Java (POJO)

Page 10: 54126935-3-4-implementaci

Módulos/Paquetes (y 3)

ORM Proporciona capas de integración para las APIs de

mapeadores objeto-relacionales más populares: Hibernate, JPA, JDO, iBatis

Utilizando este paquete es posible utilizar cualquiera de estos mapeadores objeto-relacionales en combinación con las demás características que ofrece Spring (como por ejemplo con la gestión declarativa de transacciones)

AOP Proporciona una implementación del paradigma de la

programación orientada a aspectos (conforme a la AOP Alliance), que es utilizada, transparentemente para el programador, por otros paquetes de Spring, pero que también puede ser usada directamente

Page 11: 54126935-3-4-implementaci

El Contenedor (1)

El contenedor de IoC es el núcleo del sistema

Responsable de la creación y configuración de los Beans

Nota: Un bean, en el contexto de Spring, es un POJO que es creado y manejado por el contenedor de IoC

La interfaz BeanFactory o su hija ApplicationContext representan la interfaz del

contenedor

Spring proporciona varias implementaciones

Page 12: 54126935-3-4-implementaci

El Contenedor (2)

Instanciación

try {

ApplicationContext ctx = new ClassPathXmlApplicationContext(

new String[] {SPRING_CONFIG_FILE, SPRING_CONFIG_TEST_FILE});

AccountService accountService =

(AccountService) ctx.getBean("accountService");

...

} catch (Exception e) {

e.printStackTrace();

}

Page 13: 54126935-3-4-implementaci

El Contenedor (y 3)

ClassPathXmlApplicationContext

Permite declarar los objetos que componen la aplicación, y las dependencias entre ellos en XML

A partir de los metadatos de configuración en XML es capaz de crear y configurar los objetos que componen la aplicación

A través del método getBean es posible obtener referencias

a los objetos declarados, a partir de su nombre

Page 14: 54126935-3-4-implementaci

Declaración de Beans en XML (1)

Es equivalente a

<bean id="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource"

p:driverClassName="com.mysql.jdbc.Driver"

p:url="jdbc:mysql://localhost/pojo" p:username="pojo"

p:password="pojo" />

<bean id="accountService"

class="es.udc.pojo.minibank.model.accountservice.AccountServiceImpl"

p:accountDao-ref="accountDao"

p:accountOperationDao-ref="accountOperationDao" />

<bean id="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource" >

<property name="driverClassName" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost/pojo" />

<property name="username" value="pojo" />

<property name="password" value="pojo" />

</bean>

<bean id="accountService"

class="es.udc.pojo.minibank.model.accountservice.AccountServiceImpl"

<property name="accountDao" ref="accountDao" />

<property name="accountOperationDao" ref="accountOperationDao" />

</bean>

Page 15: 54126935-3-4-implementaci

Declaración de Beans en XML (2)

Se declaran con la etiqueta bean

Parámetros básicos id: Nombre o identificador del bean

class: Clase de implementación del bean

Inyección de dependencias basada en “setters”

Permite inyectar valores u otros beans (a través de referencias), invocando al método set correspondiente del bean sobre el que se está realizando la inyección

Se indica el nombre de la propiedad que se desea inyectar y el valor que se le desea proporcionar

Page 16: 54126935-3-4-implementaci

Declaración de Beans en XML (y 3)

Inyección de dependencias basada en “setters” (cont)

Es posible especificarlas

A través de un elemento anidado property, que acepta los

siguientes atributos

name: Nombre de la propiedad donde se desea inyectar el valor

value: Para inyectar un valor constante

ref: Para inyectar otro bean a partir de su nombre

Con sintaxis abreviada (utilizando el espacio de nombres p) a través de los atributos

p:nombrePropiedad: Para inyectar un valor constante en la

propiedad indicada

p:nombrePropiedad-ref: Para inyectar otro bean a partir de su nombre en la propiedad indicada

El bean se crea a partir de su constructor vacío y a continuación se invocan los métodos set con los valores

adecuados

Page 17: 54126935-3-4-implementaci

Declaración de Beans Usando Anotaciones (1)

@Repository("accountDao")

public class AccountDaoHibernate

extends GenericDaoHibernate<Account, Long>

implements AccountDao {

...

}

@Repository("accountOperationDao")

public class AccountOperationDaoHibernate

extends GenericDaoHibernate<AccountOperation, Long>

implements AccountOperationDao {

...

}

@Service("accountService")

public class AccountServiceImpl implements AccountService {

...

}

Page 18: 54126935-3-4-implementaci

Declaración de Beans Usando Anotaciones (2)

Spring proporciona un mecanismo para detectar automáticamente clases anotadas y registrarlas como beans en el ApplicationContext

Se puede utilizar la anotación genérica @Component sobre cualquier clase, o sus especializaciones @Repository, @Service y @Controller para anotar clases en las capas

de persistencia, servicios y vista respectivamente

La documentación de Spring aconseja utilizar las especializaciones porque otros frameworks o herramientas pueden aplicarles semántica especial y el propio Spring puede añadírsela en futuras versiones

Si la anotación contiene el atributo name, entonces el valor de

ese atributo será el que dé nombre al bean; en otro caso, se le dará el mismo nombre que la clase pero empezando por minúscula

Page 19: 54126935-3-4-implementaci

Declaración de Beans Usando Anotaciones (y 3)

Spring proporciona un mecanismo para detectar automáticamente clases anotadas y registrarlas como beans en el ApplicationContext (cont)

Además debe utilizarse el tag component-scan en el

fichero de configuración de Spring indicando en el atributo base-package el o los paquetes (separados por comas)

que incluyen las clases anotadas (puede ser un paquete padre, no es necesario que sean los paquetes que contienen las clases directamente)

<beans xmlns="http://www.springframework.org/schema/beans"

...

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="... ">

...

<context:component-scan base-package="es.udc.pojo.minibank.model"/>

...

</beans>

Page 20: 54126935-3-4-implementaci

Auto-inyección de Dependencias

El contenedor de Spring es capaz de auto-inyectar dependencias entre beans según diferentes criterios Por tipo: Busca un bean con el mismo tipo que la propiedad Por nombre: Busca un bean con el mismo id que la propiedad

Por constructor: Busca uno o más beans cuyos tipos coincidan con los parámetros de uno de los constructores de ese bean

Cuando se usan metadatos de configuración en XML, la auto-inyección de dependencias se especifica a través del atributo autowire de la etiqueta <bean/>

También es posible realizar auto-inyección de dependencias utilizando la anotación @Autowired Ventajas

La cantidad de configuración necesaria puede verse reducida significativamente, y también las necesidades de cambios en dicha configuración según avanza el desarrollo

Desventajas Las relaciones entre los beans dejan de estar documentadas

explícitamente Si hay varios beans con el mismo tipo no puede utilizarse la auto-

inyección por tipo (que es la que suele utilizarse)

Page 21: 54126935-3-4-implementaci

Auto-Inyección de Dependencias Usando Anotaciones (1)

@Service("accountService")

public class AccountServiceImpl implements AccountService {

@Autowired

private AccountDao accountDao;

@Autowired

private AccountOperationDao accountOperationDao;

...

}

Page 22: 54126935-3-4-implementaci

Auto-Inyección de Dependencias Usando Anotaciones (y 2)

La anotación @Autowired puede ser aplicada a setters, pero

también a métodos con nombres arbitrarios y múltiples argumentos, a constructores y a propiedades

Por defecto la auto-inyección se realiza por tipo y si no hay ningún bean que la satisfaga entonces se produce un error

Para poder utilizar esta anotación es necesario especificar la etiqueta annotation-config en el fichero de configuración

de Spring

<beans xmlns="http://www.springframework.org/schema/beans"

...

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="... ">

...

<context:annotation-config/>

...

</beans>

Page 23: 54126935-3-4-implementaci

Beans de la capa modelo de MiniBank (pojo-minibank-spring-config.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

...

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="...">

<!-- Enable usage of @Autowired. -->

<context:annotation-config/>

<!-- Enable component scanning for defining beans with annotations. -->

<context:component-scan base-package="es.udc.pojo.minibank.model"/>

...

<!-- Hibernate Session Factory -->

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"

... />

...

</beans>

Page 24: 54126935-3-4-implementaci

AccountServiceImpl.java@Service("accountService")

public class AccountServiceImpl implements AccountService {

@Autowired

private AccountDao accountDao;

@Autowired

private AccountOperationDao accountOperationDao;

...

}

Page 25: 54126935-3-4-implementaci

AccountDaoHibernate.java y AccountOperationDaoHibernate.java

@Repository("accountDao")

public class AccountDaoHibernate

extends GenericDaoHibernate<Account, Long>

implements AccountDao {

...

}

@Repository("accountOperationDao")

public class AccountOperationDaoHibernate

extends GenericDaoHibernate<AccountOperation, Long>

implements AccountOperationDao {

...

}

Page 26: 54126935-3-4-implementaci

GenericDaoHibernate.javapublic class GenericDaoHibernate<E, PK extends Serializable>

implements GenericDao<E, PK> {

private SessionFactory sessionFactory;

...

@Autowired

public void setSessionFactory(SessionFactory sessionFactory) {

this.sessionFactory = sessionFactory;

}

...

}

Page 27: 54126935-3-4-implementaci

Beans de la capa modelo de MiniBank

Declaración: Se declaran los siguientes beans de usuario mediante

anotaciones Un bean para cada DAO de Hibernate

Un bean para la implementación del servicio

Se declara un bean en XML para la SessionFactory (que usan los DAOs de Hibernate)

Inyección de dependencias Las dependencias entre los beans se auto-inyectan mediante

anotaciones

La implementación del servicio usa los DAOs Se inyectan directamente sobre las propiedades privadas que

los referencian y por tanto no son necesarios los setters

Los DAOs usan la SessionFactory Se inyecta en GenericDaoHibernate sobre el setter

correspondiente (el setter es necesario ya que es invocado desde las clases de prueba)

Page 28: 54126935-3-4-implementaci

Beans: Inyección de Dependencias

AccountServiceImpl

- accountDao : AccountDao- accountOperationDao :

AccountOperationDao

AccountDaoHibernate

- sessionFactory : SessionFactory

AccountOperationDaoHibernate

- sessionFactory : SessionFactory

accountDao

accountService

accountOperationDao

sessionFactory

Page 29: 54126935-3-4-implementaci

Ámbito de los Beans

El ámbito de un bean se especifica a través del atributo scope de la etiqueta bean o a través de la anotación @Scope (según se use configuración XML o basada en anotaciones)

Algunos posibles valores son: singleton (valor por defecto)

El contenedor usa siempre la misma instancia (ya sea cuando se le pide a través de la API o cuando necesita inyectarlo)

prototype

Indica que el contenedor debe crear una nueva instancia del bean cada vez que se precise una

Puede ser necesario, por ejemplo, si el bean tiene estado

OJO con los beans de tipo singleton con dependencias con beans de tipo prototype

Page 30: 54126935-3-4-implementaci

Más sobre Declaración de Beans

Otras funcionalidades que ofrece Spring

Instanciación de beans a partir de factorías

Inyección de dependencias a través de constructores

Los valores o referencias a otros beans se inyectan a través de los argumentos de un constructor

Inyección de propiedades multivaluadas (arrays, listas, conjuntos, mapas)

Inicialización y liberación de recursos de un bean a través de métodos que deben ser invocados justo después de haberse creado y justo antes de ser destruido, respectivamente

...

Page 31: 54126935-3-4-implementaci

Excepciones de Persistencia (1)

En JDBC se lanza la excepción java.sql.SQLException cuando se produce

cualquier tipo de error en el acceso a los datos

Problema: Hay que capturarla siempre y analizarla para saber de qué tipo de error se trata

Algunos frameworks (e.g. Hibernate) ofrecen una jerarquía de excepciones más descriptiva (una excepción diferente para cada tipo de error)

Ventaja: Permite diferenciar entre qué tipos de errores capturar

Problema: Son específicas del framework utilizado para realizar la persistencia de los datos

Page 32: 54126935-3-4-implementaci

Excepciones de Persistencia (2)public class GenericDaoHibernate<E, PK extends Serializable>

implements GenericDao<E, PK> {

...

protected DataAccessException convertHibernateAccessException(

HibernateException e) {

return SessionFactoryUtils.convertHibernateAccessException(e);

}

public void save(E entity) {

try {

getSession().saveOrUpdate(entity);

} catch (HibernateException e) {

throw convertHibernateAccessException(e);

}

}

...

}

Page 33: 54126935-3-4-implementaci

Excepciones de Persistencia (3)@Repository("accountDao")

public class AccountDaoHibernate extends

GenericDaoHibernate<Account, Long>

implements AccountDao {

@SuppressWarnings("unchecked")

public List<Account> findByUserId(Long userId, int startIndex,

int count) {

try {

return getSession().createQuery(

"SELECT a FROM Account a WHERE a.userId = :userId " +

"ORDER BY a.accountId").

setParameter("userId", userId).

setFirstResult(startIndex).

setMaxResults(count).list();

} catch (HibernateException e) {

throw convertHibernateAccessException(e);

}

}

}

Page 34: 54126935-3-4-implementaci

Excepciones de Persistencia (y 4)

Spring proporciona una jerarquía de excepciones de acceso a datos (heredan de DataAccessException) que resuelve ambos problemas Cada excepción representa un error concreto

No son específicas del framework de persistencia de datos utilizado, que por tanto se oculta a las capas superiores

Son excepciones unchecked

Spring proporciona mecanismos para realizar la conversión entre las excepciones nativas del framework de persistencia utilizado y la jerarquía propia El método convertHibernateAccessException de la clase

SessionFactoryUtils realiza esta conversión para el caso de Hibernate

Se ha creado un método protegido en el DAO genérico para realizar la conversión

En todos los métodos de los DAOs (incluidos los del DAO genérico) se captura HibernateException y se lanza hacia las capas superiores el resultado de convertirla a una excepción de la jerarquía de DataAccessException

Page 35: 54126935-3-4-implementaci

DataSources

Independientemente del framework de persistencia utilizado probablemente se necesitará configurar una referencia a un DataSource

Spring proporciona, entre otras, las siguientes opciones para configurar un bean de tipo DataSource

DataSources definidos directamente sobre un driver JDBC

SingleConnectionDataSource

DriverMangerDataSource

DataSources que son localizados vía JNDI

Cualquier contenedor Java EE puede poner accesible vía JNDI un DataSource (que normalmente implementará pool de conexiones)

Page 36: 54126935-3-4-implementaci

SingleConnectionDataSource

Cuando se crea, solicita una conexión a la base de datos El método getConnection siempre devuelve esa conexión (o un

proxy) Este DataSource sólo es válido para entornos single-thread

Es usual especificar suppressClose="true" para que devuelva un proxy de la conexión cuyo método close (el del proxy) no cierra la conexión

Un DataSource con estas características es suficiente y eficiente para los tests de integración Suficiente: llega con disponer de una conexión Eficiente: la conexión a la base de datos sólo se pide cuando se crea el

DataSource

Además, hay que indicar también las siguientes propiedades El nombre de la clase del driver JDBC La URL de conexión a la BD El usuario para conectarse a la BD La contraseña del usuario indicado

<bean id="dataSource"

class="org.springframework.jdbc.datasource.SingleConnectionDataSource"

p:driverClassName="com.mysql.jdbc.Driver"

p:url="jdbc:mysql://localhost/pojotest" p:username="pojo"

p:password="pojo" p:suppressClose="true" />

Page 37: 54126935-3-4-implementaci

DriverMangerDataSource

Implementación simple de la interfaz DataSource No implementa pool de conexiones

El método getConnection devuelve una conexión nueva cada vez que es invocado

Válido para entornos multi-thread

Un DataSource con estas características es suficientemente eficiente y seguro para probar una aplicación Web a nivel de usuario

Hay que indicar las siguientes propiedades El nombre de la clase del driver JDBC

La URL de conexión a la BD

El usuario para conectarse a la BD

La contraseña del usuario indicado

Es el que usamos con el plugin de Jetty para Maven (src/main/jetty/jetty-env.xml)

Page 38: 54126935-3-4-implementaci

Acceso a DataSources mediante JNDI (1)

JNDI (Java Naming and Directory Interface) Familia de paquetes javax.naming (Java SE)

Es una API que abstrae el acceso a un servicio de nombres y directorios (e.g. LDAP)

Es posible registrar objetos mediante un nombre jerárquico

Los servidores de aplicaciones Java EE exponen diversos objetos mediante JNDI

Los servidores de aplicaciones Java EE proporcionan implementaciones de DataSource Cada objeto DataSource es accesible a partir de un nombre JNDI

de la forma java:comp/env/XXX/YYY, donde XXX suele (recomendado) ser "jdbc" e YYY el nombre de un DataSource concreto

Habitualmente estos objetos DataSource implementan la estrategia de pool de conexiones

Requiere configuración en el servidor (driver, URL, usuario, contraseña, parámetros específicos al pool, etc.) Ej.: En Tomcat se definen en conf/server.xml

Page 39: 54126935-3-4-implementaci

Acceso a DataSources mediante JNDI (y 2)

El atributo jndiName se utiliza para indicar el nombre del recurso accesible vía JNDI

Si la aplicación está ejecutándose dentro de un servidor de aplicaciones Java EE El valor del atributo resourceRef debe ser true

El nombre indicado en jndiName es relativo al contexto java:comp/env/

JndiObjectFactoryBean implementa org.springframework.beans.factory.FactoryBean

Los beans que implementan esta interfaz se definen igual que el resto de beans, pero cuando se referencian desde otros beans no se inyecta una instancia de ese tipo sino el objeto que devuelve su método getObject

El método getObject de JndiObjectFactoryBean devuelve el objeto asociado al nombre JNDI especificado en la configuración

<bean id="dataSource"

class="org.springframework.jndi.JndiObjectFactoryBean"

p:jndiName="jdbc/pojo-examples-ds"

p:resourceRef="true" />

Page 40: 54126935-3-4-implementaci

Integración con Hibernate 3 (1)

Los DAOs implementados con Hibernate necesitan un objeto de tipo org.hibernate.SessionFactory del que obtener la sesión actual Como veremos más adelante, el gestor de transacciones de

Hibernate también precisa un objeto de ese tipo

La siguiente declaración permite definir un bean para obtener un SessionFactory que utiliza las anotaciones de Hibernate en las entidades

datasource: indica el nombre de un bean de tipo DataSource(para obtener las conexiones a la BD)

configLocation: Indica donde está el fichero de configuración de Hibernate (fichero llamado pojo-minibank-hibernate-config.xml localizado en el classpath de ejecución)

AnnotationSessionFactoryBean es un FactoryBean (igual que la clase JndiObjectFactoryBean explicada en la transparencia anterior) cuyo método getObject devuelve una instancia que implementa SessionFactory

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"

p:dataSource-ref="dataSource"

p:configLocation="classpath:/pojo-minibank-hibernate-config.xml"/>

Page 41: 54126935-3-4-implementaci

Integración con Hibernate 3 (y 2)

Como ya hemos visto con anterioridad el bean sessionFactory se inyecta en el DAO genérico

public class GenericDaoHibernate<E, PK extends Serializable>

implements GenericDao<E, PK> {

private SessionFactory sessionFactory;

...

@Autowired

public void setSessionFactory(SessionFactory sessionFactory) {

this.sessionFactory = sessionFactory;

}

...

}

Page 42: 54126935-3-4-implementaci

Transacciones

Spring no gestiona directamente las transacciones, sino que lo hace a través de una abstracción (patrón Strategy)

Interfazorg.springframework.transaction.PlatformTransactionManager

Se puede trabajar con las transacciones independientemente de la implementación del gestor de transacciones concreto que se esté utilizando

Spring proporciona una serie de gestores de transacciones que delegan la responsabilidad de la gestión de las transacciones a implementaciones específicas de la plataforma utilizada

Se puede trabajar con transacciones a través de la API Java de Spring, pero también se pueden definir de forma declarativa haciendo uso del framework AOP de Spring, que incluye una implementación del aspecto de transaccionalidad

Es posible hacerlo mediante XML o mediante anotaciones Java

Utilizaremos la opción de las anotaciones porque simplifica la declaración de las transacciones

Page 43: 54126935-3-4-implementaci

Transacciones con Hibernate 3

Si la capa de persistencia del modelo está implementada con Hibernate, entonces el gestor de transacciones a utilizar es el siguiente

Es preciso inyectarle un objeto SessionFactory de Hibernate

Ya hemos declarado uno para utilizar en los DAOs

HibernateTransactionManager delega la gestión de las transacciones a un objeto org.hibernate.Transaction

A partir del objeto SessionFactory obtiene la sesión Hibernate

A partir de la sesión Hibernate obtiene el objeto Transaction

<bean id="transactionManager“

class="org.springframework.orm.hibernate3.HibernateTransactionManager"

p:sessionFactory-ref="sessionFactory" />

Page 44: 54126935-3-4-implementaci

API Transacciones (1)

public interface PlatformTransactionManager {

TransactionStatus getTransaction(TransactionDefinition definition)

throws TransactionException;

void commit(TransactionStatus status)

throws TransactionException;

void rollback(TransactionStatus status)

throws TransactionException;

}

public interface TransactionStatus {

boolean isNewTransaction();

void setRollbackOnly();

boolean isRollbackOnly();

}

Page 45: 54126935-3-4-implementaci

API Transacciones (y 2)

Un gestor de transacciones implementa la interfaz PlatformTransactionManager

TransactionException es una excepción unchecked

El método getTransaction devuelve un objeto de tipo TransactionStatus en función de un parámetro de tipo TransactionDefinition

TransactionStatus representa una transacción

Puede ser la transacción actual o una nueva

TransactionDefinition es una interfaz a través de la cual se pueden especificar las características de la transacción que se quiere obtener (propagación, nivel de aislamiento, timeout, propiedad read-only)

La subinterfaz TransactionAttribute permite, además, especificar la lista de excepciones que provocan o no un rollback

Si se pasa null en getTransaction, se utilizan los valores por defecto

Los métodos commit y rollback se utilizan para hacer un commit o un rollback de la transacción que se les pasa

Page 46: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (1)

@Service("accountService")

public class AccountServiceImpl implements AccountService {

@Autowired

private PlatformTransactionManager transactionManager;

@Autowired

private AccountDao accountDao;

@Autowired

private AccountOperationDao accountOperationDao;

Page 47: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (2)

public Account createAccount(Account account) {

TransactionStatus transactionStatus =

transactionManager.getTransaction(null);

try {

accountDao.save(account);

} catch (RuntimeException e) {

transactionManager.rollback(transactionStatus);

throw e;

}

transactionManager.commit(transactionStatus);

return account;

}

Iniciartransacción

Finalizartransacción

Page 48: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (3)

public void removeAccount(Long accountId)

throws InstanceNotFoundException {

TransactionStatus transactionStatus =

transactionManager.getTransaction(null);

try {

accountDao.remove(accountId);

accountOperationDao.removeByAccountId(accountId);

} catch (RuntimeException e) {

transactionManager.rollback(transactionStatus);

throw e;

} catch (InstanceNotFoundException e) {

transactionManager.commit(transactionStatus);

throw e;

}

transactionManager.commit(transactionStatus);

}

Iniciartransacción

Finalizartransacción

Page 49: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (4)

public void withdrawFromAccount(Long accountId, double amount)

throws InstanceNotFoundException, InsufficientBalanceException {

TransactionStatus transactionStatus =

transactionManager.getTransaction(null);

try {

/* Find account. */

Account account = accountDao.find(accountId);

/* Modify balance. */

double currentBalance = account.getBalance();

if (currentBalance < amount) {

throw new InsufficientBalanceException(accountId,

currentBalance,amount);

}

account.setBalance(currentBalance - amount);

Iniciartransacción

Page 50: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (5)

/* Register account operation. */

accountOperationDao.save(new AccountOperation(account,

Calendar.getInstance(), AccountOperation.Type.WITHDRAW,

amount));

} catch (RuntimeException e) {

transactionManager.rollback(transactionStatus);

throw e;

} catch (InstanceNotFoundException e) {

transactionManager.commit(transactionStatus);

throw e;

} catch (InsufficientBalanceException e) {

transactionManager.commit(transactionStatus);

throw e;

}

transactionManager.commit(transactionStatus);

}

...

}

Finalizartransacción

Page 51: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (6)

El código para todos los casos de uso transaccionales es similar:

Iniciar la transacción (con las propiedades adecuadas) a partir del gestor de transacciones utilizad

Ejecutar la lógica propia del caso de uso

Finalizar transacción (commit o rollback) en función de si se ha producido alguna excepción o no y de su tipo

El código común para la gestión de las transacciones puede eliminarse de la implementación de todos los casos de uso que lo precisen, y declarativamente decir cuándo debe ejecutarse

Page 52: 54126935-3-4-implementaci

AOP: Gestión de Transacciones (y 7)

Declarativamente es posible indicar

El gestor de transacciones a utilizar

Que los métodos createAccount, removeAccount, withdrawFromAccount y todos los demás de la clase AccountServiceImpl son transaccionales

Spring intercepta las invocaciones a los métodos declarados como transaccionales:

[Antes de que se ejecute el método] Ejecuta el código necesario para comenzar la transacción

Declarativamente pueden indicarse parámetros como, por ejemplo, el nivel de aislamiento deseado para la transacción

[Después de que se ejecute el método] Ejecuta el código necesario para finalizar la transacción (ya sea con un commit o un rollback)

Pueden indicarse qué excepciones deben provocar un rollback y cuales no

Page 53: 54126935-3-4-implementaci

AccountServiceImpl.java (1)@Service("accountService")

@Transactional

public class AccountServiceImpl implements AccountService {

@Autowired

private AccountDao accountDao;

@Autowired

private AccountOperationDao accountOperationDao;

public Account createAccount(Account account) { ... }

@Transactional(readOnly = true)

public Account findAccount(Long accountId)

throws InstanceNotFoundException { ... }

public void addToAccount(Long accountId, double amount)

throws InstanceNotFoundException { ... }

Page 54: 54126935-3-4-implementaci

AccountServiceImpl.java (2)public void withdrawFromAccount(Long accountId, double amount)

throws InstanceNotFoundException, InsufficientBalanceException {

/* Find account. */

Account account = accountDao.find(accountId);

/* Modify balance. */

double currentBalance = account.getBalance();

if (currentBalance < amount) {

throw new InsufficientBalanceException(accountId,

currentBalance,amount);

}

account.setBalance(currentBalance - amount);

/* Register account operation. */

accountOperationDao.save(new AccountOperation(account,

Calendar.getInstance(), AccountOperation.Type.WITHDRAW,

amount));

}

Page 55: 54126935-3-4-implementaci

AccountServiceImpl.java (3)@Transactional(readOnly = true)

public AccountBlock findAccountsByUserId(Long userId,

int startIndex, int count) { ... }

public void removeAccount(Long accountId)

throws InstanceNotFoundException { ... }

public void transfer(Long sourceAccountId,

Long destinationAccountId, double amount)

throws InstanceNotFoundException,

InsufficientBalanceException { ... }

@Transactional(readOnly = true)

public int getNumberOfAccountOperations(Long accountId,

Calendar startDate, Calendar endDate)

throws InstanceNotFoundException { ... }

@Transactional(readOnly = true)

public List<AccountOperation> findAccountOperationsByDate(

Long accountId, Calendar startDate, Calendar endDate,

int startIndex, int count)

throws InstanceNotFoundException { ... }

}

Page 56: 54126935-3-4-implementaci

Transacciones con Anotaciones

Si se desean utilizar anotaciones para declarar las transacciones, el gestor de transacciones a utilizar se declara a través de la etiqueta annotation-driven, perteneciente al espacio de nombres tx

El valor del atributo transaction-manager indica

el nombre del bean que debe ser utilizado como gestor de transacciones

Este atributo es opcional, y si no está presente toma el valor por defecto “transactionManager”

Por tanto en nuestro caso podríamos no haberlo especificado

<tx:annotation-driven transaction-manager="transactionManager" />

Page 57: 54126935-3-4-implementaci

Anotación @Transactional (1)

Puede utilizarse para anotar una clase o un método concreto En una clase se aplica a todos los métodos que contiene En un método sobrescribe el comportamiento especificado para la

clase a la que pertenece

Propiedades: propagation:

PROPAGATION_REQUIRED (valor por defecto): El método debe ejecutarse dentro de una transacción; si ya existe una, se ejecuta en esa, y si no, se crea una nueva

PROPAGATION_MANDATORY: Igual que el anterior pero la transacción debe existir (si no se lanza una excepción)

PROPAGATION_REQUIRES_NEW: El método debe ejecutarse dentro de una transacción nueva (si ya existe una se suspende mientras)

PROPAGATION_NESTED: El método debe ejecutarse en una transacción anidada si ya existe una transacción en progreso. Si no existe se comporta igual que PROPAGATION_REQUIRED

PROPAGATION_NEVER: El método no debe ejecutarse dentro de una transacción (si existe una en progreso se lanza una excepción)

PROPAGATION_NOT_SUPPORTED: Igual que el anterior pero si existe una transacción se suspende en vez de lanzar una excepción

PROPAGATION_SUPPORTS: No es necesario que el método se ejecute dentro de una transacción pero si ya existe una se ejecuta dentro de ella

Page 58: 54126935-3-4-implementaci

Anotación @Transactional (2)

Propiedades (cont.): isolation: Nivel de aislamiento (por defecto el de la

plataforma) ISOLATION_DEFAULT: El de la plataforma

ISOLATION_READ_UNCOMMITED: Pueden ocurrir “dirty reads”, “non-repeatable reads” y “phanton reads”

ISOLATION_READ_COMMITED: Pueden ocurrir “non-repeatable reads” y “phamton reads”

ISOLATION_REPETEABLE_READ: Pueden ocurrir “phanton reads”

ISOLATION_SERIALIZABLE: Elimina todos los problemas de concurrencia

readOnly: Lectura/escritura (defecto) o solo lectura

Importante indicarlo para poder hacer optimizaciones !!

timeout: Timeout de la transacción (por defecto el de la plataforma) Si el timeout expira entonces se hace un rollback de la

transacción

Page 59: 54126935-3-4-implementaci

Anotación @Transactional (y 3)

Por defecto cualquier excepción de tipo “unchecked” (hija de RuntimeException) provocará un rollback, y cualquier excepción “checked” (hija de Exception) no

Es posible modificar este comportamiento a través de propiedades de la anotación @Transactional

rollbackFor/rollBackForClassName: Array de clases/nombres

de excepciones que deben causar un rollback

Ejemplos:

noRollbackFor/noRollbackForClassName: Array de

clases/nombres de excepciones que no deben causar un rollback

La utilizaremos en los servicios del modelo para declarar la transaccionalidad de sus métodos

Es posible anotar la interfaz que declara las operaciones del servicio, pero es más recomendable anotar la clase de implementación del servicio

@Transactional(rollbackFor={Exception1.class, Exception2.class})

@Transactional(rollbackForClassName={"es.udc.Exception1", "es.udc.Exception2"})

Page 60: 54126935-3-4-implementaci

Invocación de casos de uso

Clase Paquete

es.udc.pojo.minibank.test.experiments.spring

Ilustra cómo obtener una referencia a un servicio implementado con Spring y llamar a un caso de uso

...

try {

/* Get service object. */

ApplicationContext ctx = new ClassPathXmlApplicationContext(

new String[] {SPRING_CONFIG_FILE, SPRING_CONFIG_TEST_FILE});

AccountService accountService = (AccountService) ctx

.getBean("accountService");

accountService.withdrawFromAccount(accountId, amount);

} catch (InstanceNotFoundException e) {

e.printStackTrace();

} catch (InsufficientBalanceException e) {

e.printStackTrace();

}

...

Page 61: 54126935-3-4-implementaci

Implementación de Servicios (1)

Los casos de uso se implementan en términos de DAOs (tal como se vio en el apartado 3.2)

En los DAOs se inyecta un objeto de tipo org.hibernate.SessionFactory proporcionado por

Spring

Los DAOs se inyectan en la clase de implementación de la Fachada

Se utiliza un mecanismo proporcionado por Spring para convertir las excepciones de persistencia nativas a una jerarquía propia, independiente del framework utilizado para la persistencia

Page 62: 54126935-3-4-implementaci

Implementación de Servicios (y 2)

Para indicar la transaccionalidad de los casos de uso utilizamos la anotación @Transactional sobre la

clase de implementación del Servicio

Se declara un gestor de transacciones que delega en el gestor de transacciones de Hibernate

Se le inyecta el mismo SessionFactory que el creado para

inyectar en los DAOs, para que pueda acceder a través de él al gestor de transacciones de Hibernate

Se le inyecta un DataSource

La implementación de los Servicios es independiente del software utilizado para el acceso a datos (en nuestro caso Hibernate)