3.4 Implementación de los Casos de Uso con Spring
Í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
¿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
¿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
Conceptos Básicos en Spring
Inyección de dependencias (Dependency Injection, DI)
Programación orientada a aspectos (Aspect-Oriented Programming, AOP)
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
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
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
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)
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 parte otros paquetes de Spring, pero que también puede ser usada directamente
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
El Contenedor (2)
Instanciación
try {
ApplicationContext ctx =
new ClassPathXmlApplicationContext(
GlobalNames.SPRING_CONFIG_FILE_LOCATION);
AccountService accountService =
(AccountService) ctx.getBean("accountService");
...
} catch (Exception e) {
e.printStackTrace();
}
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 una
referencia a los objetos declarados, a partir de su nombre
Declaración de Beans (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>
Declaración de Beans (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
Declaración de Beans (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
Beans de la capa modelo de MiniBank (spring-config.xml)
<!-- Hibernate Session Factory -->
<bean id="sessionFactory“
class=
"org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
... />
...
<!-- ======================== Business Objects ======================== -->
<!-- DAOs. -->
<bean id="accountDao"
class="es.udc.pojo.minibank.model.account.AccountDaoHibernate"
p:sessionFactory-ref="sessionFactory" />
<bean id="accountOperationDao"
class=
"es.udc.pojo.minibank.model.accountoperation.AccountOperationDaoHibernate"
p:sessionFactory-ref="sessionFactory" />
<!-- Service layer. -->
<bean id="accountService"
class="es.udc.pojo.minibank.model.accountservice.AccountServiceImpl"
p:accountDao-ref="accountDao"
p:accountOperationDao-ref="accountOperationDao" />
Beans de la capa modelo de MiniBank
Declaramos los siguientes beans
Un bean para cada DAO Hibernate
Un bean para la implementación de la fachada
Un bean para la SesionFactory (que usan los DAOs de
Hibernate)
Dependencias entre beans
La fachada usa los DAOs
Los DAOs usan la SesionFactory
Utilizamos la DI de Spring basada en métodos set
para resolver las dependencias entre beans
AccountServiceImpl.javapublic class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
private AccountOperationDao accountOperationDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setAccountOperationDao(
AccountOperationDao accountOperationDao) {
this.accountOperationDao = accountOperationDao;
}
...
}
AccountDaoHibernate.java y AccountDaoOperationHibernate.java
public class AccountDaoHibernate extends
GenericDaoHibernate<Account, Long>
implements AccountDao {
...
}
public class AccountOperationDaoHibernate
extends GenericDaoHibernate<AccountOperation, Long>
implements AccountOperationDao {
...
}
GenericAccountDAO.javapublic class GenericDaoHibernate<E, PK extends Serializable>
implements GenericDao<E, PK> {
private SessionFactory sessionFactory;
...
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
...
}
Beans: Inyección de Dependencias
AccountServiceImpl
- accountDao : AccountDao- accountOperationDao :
AccountOperationDao
+ métodos set
AccountDaoHibernate
- sessionFactory : SessionFactory
+ métodos set
AccountOperationDaoHibernate
- sessionFactory : SessionFactory
+ métodos set
accountDao
accountService
accountOperationDao
sessionFactory
Ámbito de los Beans
El ámbito de un bean se especifica a través del atributo scope de la etiqueta bean
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
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 (listas, conjuntos, mapas)
Autoinyección (autowiring)
Por nombre: Busca un bean con el mismo id que la propiedad
Por tipo: Busca un bean con el mismo tipo 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
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
Etc.
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
Excepciones de Persistencia (y 2)
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, por tanto se oculta a las capas superiores
Son excepciones unchecked
Para que Spring realice la conversión entre las excepciones nativas y la jerarquía propia es necesario declarar el siguiente bean:
<bean class=
"org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
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
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)
DataSources JDBC
DriverManagerDataSource: Devuelve una nueva conexión a la BD cada vez que se le pide una Es decir, no implementa la estrategia pool de conexiones
(conceptualmente similar al SimpleDataSource estudiado en el apartado 3.1)
Debe indicársele como 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
Útil para tests de integración
<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" />
DataSources 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 JDNI
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/ZZZ, 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
DataSources 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" />
Integración con Hibernate 3 (1)
Los DAOs implementados con Hibernate necesitan un objeto de tipo org.hibernate.SesionFactory 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 “hibernate.cfg.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:hibernate.cfg.xml"/>
Integración con Hibernate 3 (y 2)
Como ya hemos visto con anterioridad el bean sesionFactory se inyecta en los DAOs
<bean id="accountDao"
class="es.udc.pojo.minibank.model.account.AccountDaoHibernate"
p:sessionFactory-ref="sessionFactory" />
<bean id="accountOperationDao"
class="es.udc.pojo.minibank.model.accountoperation.AccountOperationDaoHibernate"
p:sessionFactory-ref="sessionFactory" />
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 que simplifica la declaración de las transacciones
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 SesionFactory 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 SesionFactory 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" />
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();
}
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
Para utilizar los valores por defecto puede ser “null”
Los métodos commit y rollback se utilizan para hacer un
commit o un rollback de la transacción que se les pasa
AOP: Gestión de Transacciones (1)
public class AccountServiceImpl implements AccountService {
static {
ApplicationContext context = ...;
transactionManager = (PlatformTransactionManager) context
.getBean("transactionManager");
}
private static PlatformTransactionManager transactionManager;
private AccountDao accountDao;
private AccountOperationDao accountOperationDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setAccountOperationDao(
AccountOperationDao accountOperationDao) {
this.accountOperationDao = accountOperationDao;
}
AOP: Gestión de Transacciones (2)
public Account createAccount(Account account) {
TransactionStatus transactionStatus =
transactionManager.getTransaction(null);
try {
accountDao.create(account);
} catch (RuntimeException e) {
transactionManager.rollback(transactionStatus);
throw e;
}
transactionManager.commit(transactionStatus);
return account;
}
Iniciartransacción
Finalizartransacción
AOP: Gestión de Transacciones (3)
public void removeAccount(Long accountId)
throws InstanceNotFoundException {
TransactionStatus transactionStatus =
transactionManager.getTransaction(null);
try {
accountDao.remove(accountId);
} catch (RuntimeException e) {
transactionManager.rollback(transactionStatus);
throw e;
} catch (InstanceNotFoundException e) {
transactionManager.commit(transactionStatus);
throw e;
}
transactionManager.commit(transactionStatus);
}
Iniciartransacción
Finalizartransacción
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);
accountDao.update(account);
Iniciartransacción
AOP: Gestión de Transacciones (5)
/* Register account operation. */
accountOperationDao.create(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
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 utilizado
El gestor de transacciones puede declararse en el archivo de configuración de Spring como un bean y obtenerlo a través del método getBean después de instanciar el contenedor
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
AOP: Gestión de Transacciones (y 7)
Declarativamente es posible indicar
El gestor de transacciones a utilizar
Que los métodos createAccount, removeAccount 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
AccountServiceImpl.java (1)@Transactional
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
private AccountOperationDao accountOperationDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setAccountOperationDao(
AccountOperationDao accountOperationDao) {
this.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 { ... }
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);
accountDao.update(account);
/* Register account operation. */
accountOperationDao.create(new AccountOperation(account,
Calendar.getInstance(), AccountOperation.Type.WITHDRAW,
amount));
}
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 { ... }
AccountServiceImpl.java (y 4)
@Transactional(readOnly = true)
public List<AccountOperationInfo> findAccountOperationsByDate(
Long accountId, Calendar startDate, Calendar endDate,
int startIndex, int count)
throws InstanceNotFoundException { ... }
private List<AccountOperationInfo> toAccountOperationInfos(
List<AccountOperation> accountOperations) { ... }
}
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" />
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
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 “phamton reads”
ISOLATION_READ_COMMITED: Pueden ocurrir “non-repeatable reads” y “phamton reads”
ISOLATION_REPETEABLE_READ: Pueden ocurrir “phamton 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
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 las fachadas del modelo (servicios) 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"})
Implementación de Fachadas (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.SesionFactory 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
Implementación de Fachadas (y 2)
Para indicar la transaccionalidad de los casos de uso utilizamos la anotación @Transactional sobre la
clase de implementación de la Fachada
Se declara un gestor de transacciones que delega en el gestor de transacciones de Hibernate
Se le inyecta el mismo SesionFactory 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 que se recupera vía JNDI
(proporcionado por el contenedor de aplicaciones)
La implementación de las Fachadas es independiente del software utilizado para el acceso a datos (en nuestro caso Hibernate)
Top Related