Download SessionFactory

Document related concepts
no text concepts found
Transcript
Tema 4: Uso de Spring en la Capa
Modelo
Índice
n 
n 
n 
n 
n 
n 
Introducción a Spring
Declaración y Configuración de “beans”
Excepciones de Persistencia
Declaración de DataSources
Integración con Hibernate 4
Gestión de Transacciones
4-2
¿Qué es Spring?
n 
Framework de código abierto creado por Rod Johnson
n 
n 
http://www.springframework.org
Motivación: facilitar el desarrollo de aplicaciones Java EE,
promoviendo buenas prácticas de diseño y programación
n 
n 
Simplifica el uso de muchas de las APIs de Java EE
Dispone de alternativas a algunas de las APIs de Java EE
n 
n 
Internamente se apoyan en APIs de Java EE de más bajo nivel
Soporte para capa modelo e interfaz Web
n 
n 
Es modular: es posible usar algunos de los módulos sin
comprometerse con el uso del resto
Nosotros utilizaremos fundamentalmente el soporte de Spring para
implementar casos de uso a nivel de capa modelo
n 
n 
Inyección de dependencias
Gestión declarativa de transacciones (mediante AOP)
4-3
Módulos/Paquetes (1)
DAO
Spring JDBC
Transaction
management
Web
ORM
Hibernate
JPA
JDO
TopLink
OJB
iBatis
AOP
JEE
JMX
JMS
JCA
Remotig
EJBs
Email
Spring AOP
Integración AspectJ
Spring Web MVC
Framework Integration
Struts
WebWork
Tapestry
JSF
Rich View Support
JSPs
Velocity
FreeMarker
PDF
Jasper Reports
Excel
Spring Portlet MVC
Core
IoC Container
4-4
Módulos/Paquetes (2)
n 
Core
n 
n 
Constituye la parte fundamental del framework y
proporciona la característica de Inyección de Dependencias
(DI) / Inversión de Control (IoC)
DAO
n 
n 
Proporciona una manera de gestionar transacciones tanto
programática como declarativamente
También 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
4-5
Módulos/Paquetes (y 3)
n 
ORM
n 
n 
n 
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
n 
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
4-6
El Contenedor (1)
n 
El contenedor de IoC es el núcleo del sistema
n 
n 
n 
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 sus descendientes
ListableBeanFactory y ApplicationContext
representan la interfaz del contenedor
n 
Spring proporciona varias implementaciones
n 
E.g. ClassPathXmlApplicationContext
4-7
El Contenedor (2)
n 
Instanciación
try {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {SPRING_CONFIG_FILE, SPRING_CONFIG_TEST_FILE});
AccountService accountService = ctx.getBean(AccountService.class);
...
} catch (Exception e) {
e.printStackTrace();
}
4-8
El Contenedor (y 3)
n 
ClassPathXmlApplicationContext
n 
n 
n 
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
Tiene métodos que permiten obtener referencias a los objetos
declarados, a partir de su tipo o nombre. Algunos son
n 
T getBean(Class<T> requiredType)
n 
n 
Object getBean(String name)
n 
n 
Devuelve un bean del tipo indicado solamente si hay exactamente
uno de ese tipo. En otro caso lanza una excepción
Devuelven el bean que tiene el nombre indicado
T getBean(String name, Class<T> requiredType)
n 
Equivalente al anterior pero además proporciona “type safety” y
lanza una excepción si el bean no es del tipo indicado
4-9
Declaración de Beans en XML (1)
<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" />
n 
Es equivalente a
<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>
4 - 10
Declaración de Beans en XML (2)
n 
n 
Se declaran con la etiqueta bean
Parámetros básicos
n 
n 
n 
id: Nombre o identificador del bean
class: Clase de implementación del bean
Inyección de dependencias basada en “setters”
n 
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
n 
Se indica el nombre de la propiedad que se desea inyectar y el
valor que se le desea proporcionar
4 - 11
Declaración de Beans en XML (y 3)
n 
Inyección de dependencias basada en
“setters” (cont)
n 
Es posible especificarlas
n 
A través de un elemento anidado property, que acepta los
siguientes atributos
n 
n 
n 
n 
Con sintaxis abreviada (utilizando el espacio de nombres p) a
través de los atributos
n 
n 
n 
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
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
4 - 12
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 {
...
}
4 - 13
Declaración de Beans Usando Anotaciones (2)
n 
Spring proporciona un mecanismo para detectar
automáticamente clases anotadas y registrarlas como
beans en el ApplicationContext
n 
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
n 
n 
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 value, 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
n 
NOTA: Si una anotación sólo tiene un parámetro y se llama
value, no hace falta poner @Anotacion(value="valor"),
basta poner @Anotacion("valor")
4 - 14
Declaración de Beans Usando Anotaciones (y 3)
n 
Spring proporciona un mecanismo para detectar
automáticamente clases anotadas y registrarlas como
beans en el ApplicationContext (cont)
n 
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>
4 - 15
Auto-inyección de Dependencias
n 
El contenedor de Spring es capaz de auto-inyectar dependencias
entre beans según diferentes criterios
n 
n 
n 
n 
n 
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 autoinyecció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
n 
Ventajas
n 
n 
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
n 
n 
Las relaciones entre los beans dejan de estar documentadas
explícitamente
Si hay varios beans con el mismo tipo no puede utilizarse la autoinyección por tipo (que es la que suele utilizarse)
4 - 16
Auto-Inyección de Dependencias Usando Anotaciones (1)
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired
private AccountOperationDao accountOperationDao;
...
}
4 - 17
Auto-Inyección de Dependencias Usando Anotaciones (y 2)
n 
n 
n 
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>
4 - 18
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.hibernate4.LocalSessionFactoryBean"
... />
...
</beans>
4 - 19
AccountServiceImpl.java
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired
private AccountOperationDao accountOperationDao;
...
}
4 - 20
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 {
...
}
4 - 21
GenericDaoHibernate.java
public class GenericDaoHibernate<E, PK extends Serializable>
implements GenericDao<E, PK> {
private SessionFactory sessionFactory;
...
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
...
}
4 - 22
Beans de la capa modelo de MiniBank
n 
Declaración:
n 
Se declaran los siguientes beans de usuario mediante
anotaciones
n 
n 
n 
n 
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
n 
n 
Las dependencias entre los beans se auto-inyectan mediante
anotaciones
La implementación del servicio usa los DAOs
n 
n 
Se inyectan directamente sobre las propiedades privadas que
los referencian y por tanto no son necesarios los setters
Los DAOs usan la SessionFactory
n 
Se inyecta en GenericDaoHibernate sobre el setter
correspondiente (el setter es necesario ya que es invocado
desde las clases de prueba)
4 - 23
Beans: Inyección de Dependencias
accountDao
AccountDaoHibernate
-  sessionFactory : SessionFactory
accountService
AccountServiceImpl
-  accountDao : AccountDao
-  accountOperationDao :
AccountOperationDao
sessionFactory
accountOperationDao
AccountOperationDaoHibernate
-  sessionFactory : SessionFactory
4 - 24
Ámbito de los Beans
n 
n 
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:
n 
singleton (valor por defecto)
n 
n 
prototype
n 
n 
n 
El contenedor usa siempre la misma instancia (ya sea cuando
se le pide a través de la API o cuando necesita inyectarlo)
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
4 - 25
Excepciones de Persistencia (1)
n 
En JDBC se lanza la excepción
java.sql.SQLException cuando se produce
cualquier tipo de error en el acceso a los datos
n 
n 
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)
n 
n 
Ventaja: Permite diferenciar entre qué tipos de errores
capturar
Problema: Son específicas del framework utilizado para
realizar la persistencia de los datos
4 - 26
Excepciones de Persistencia (y 2)
n 
Spring proporciona una jerarquía de excepciones de
acceso a datos (heredan de
DataAccessException) que resuelve ambos
problemas
n 
n 
n 
n 
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
n 
Para que Spring realice automáticamente la conversión, en
clases anotadas con @Repository, es suficiente con
declarar el siguiente bean en el fichero de configuración
<bean class=
"org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
4 - 27
DataSources
n 
n 
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
n 
DataSources definidos directamente sobre un driver JDBC
n 
n 
n 
SingleConnectionDataSource
DriverManagerDataSource
DataSources que son localizados vía JNDI
n 
Cualquier contenedor Java EE puede poner accesible vía JNDI
un DataSource (que normalmente implementará pool de
conexiones)
4 - 28
SingleConnectionDataSource
n 
n 
Cuando se crea, solicita una conexión a la base de datos
El método getConnection siempre devuelve esa conexión (o un
proxy)
n 
n 
n 
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
n 
n 
n 
Este DataSource sólo es válido para entornos single-thread
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
n 
n 
n 
n 
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" />
4 - 29
DriverManagerDataSource
n 
Implementación simple de la interfaz DataSource
n 
n 
n 
n 
n 
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
n 
n 
n 
n 
n 
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
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)
4 - 30
Acceso a DataSources mediante JNDI (1)
n 
JNDI (Java Naming and Directory Interface)
n 
n 
n 
n 
n 
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
n 
n 
n 
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.)
n 
Ej.: En Tomcat se definen en conf/server.xml
4 - 31
Acceso a DataSources mediante JNDI (y 2)
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"
p:jndiName="jdbc/pojo-examples-ds"
p:resourceRef="true" />
n 
n 
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
n 
n 
n 
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
n 
n 
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
4 - 32
Integración con Hibernate 4 (1)
n 
Los DAOs implementados con Hibernate necesitan un objeto de
tipo org.hibernate.SessionFactory del que obtener la
sesión actual
n 
n 
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
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:/pojo-minibank-hibernate-config.xml"/>
n 
n 
n 
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-hibernateconfig.xml localizado en el classpath de ejecución)
LocalSessionFactoryBean es un FactoryBean (igual que la
clase JndiObjectFactoryBean explicada en la transparencia
anterior) cuyo método getObject devuelve una instancia que
implementa SessionFactory
4 - 33
Integración con Hibernate 4 (2)
n 
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;
}
...
}
4 - 34
Integración con Hibernate 4 (3)
n 
Fichero de configuración de Hibernate: pojominibank-hibernate-config.xml
<hibernate-configuration>
<session-factory>
<property
name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<mapping class="es.udc.pojo.minibank.model.account.Account" />
<mapping
class="es.udc.pojo.minibank.model.accountoperation.AccountOperation" />
</session-factory>
</hibernate-configuration>
4 - 35
Integración con Hibernate 4 (y 4)
n 
Fichero de configuración de Hibernate: pojominibank-hibernate-config.xml (cont)
n 
No contiene la configuración de la BD/pool porque al
SessionFactory inyectado por Spring en el DAO genérico
ya se le ha inyectado un DataSource
n 
n 
n 
Cuando se ejecuta la aplicación en un servidor de aplicaciones,
se utiliza el DataSource (accesible vía JNDI) proporcionado
por el servidor de aplicaciones
Cuando se ejecutan las pruebas se utiliza un DataSource de
tipo SingleConnectionDataSource declarado en el fichero
de configuración de Spring para pruebas (pojo-minibankspring-config-test.xml) que sobrescribe al anterior
No se utiliza la siguiente propiedad porque el
SessionFactory inyectado por Spring en el DAO genérico
ya implementa esa semántica
<property name="current_session_context_class">thread</property>
4 - 36
Transacciones
n 
Spring no gestiona directamente las transacciones, sino que lo
hace a través de una abstracción (patrón Strategy)
n 
Interfaz
org.springframework.transaction.PlatformTransactionManager
n 
n 
n 
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
n 
n 
Es posible hacerlo mediante XML o mediante anotaciones Java
Utilizaremos la opción de las anotaciones porque simplifica la
declaración de las transacciones
4 - 37
Transacciones con Hibernate 4
n 
Si la capa de persistencia del modelo está implementada con
Hibernate, entonces el gestor de transacciones a utilizar es el
siguiente
<bean id="transactionManager“
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
n 
Es preciso inyectarle un objeto SessionFactory de Hibernate
n 
n 
Ya hemos declarado uno para utilizar en los DAOs
HibernateTransactionManager delega la gestión de las
transacciones a un objeto org.hibernate.Transaction
n 
n 
A partir del objeto SessionFactory obtiene la sesión Hibernate
A partir de la sesión Hibernate obtiene el objeto Transaction
4 - 38
API Transacciones (1)
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status)
throws TransactionException;
void rollback(TransactionStatus status)
throws TransactionException;
}
4 - 39
API Transacciones (y 2)
n 
Un gestor de transacciones implementa la interfaz
PlatformTransactionManager
n 
n 
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
n 
TransactionStatus es una interfaz que representa una
transacción
n 
n 
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)
n 
n 
Puede ser la transacción actual o una nueva
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
4 - 40
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;
4 - 41
AOP: Gestión de Transacciones (2)
public Account createAccount(Account account) {
TransactionStatus transactionStatus =
transactionManager.getTransaction(null);
Iniciar
transacción
try {
accountDao.save(account);
transactionManager.commit(transactionStatus);
} catch (RuntimeException|Error e) {
transactionManager.rollback(transactionStatus);
throw e;
}
Finalizar
transacción
return account;
}
4 - 42
AOP: Gestión de Transacciones (3)
public void removeAccount(Long accountId)
throws InstanceNotFoundException {
TransactionStatus transactionStatus =
transactionManager.getTransaction(null);
Iniciar
transacción
try {
accountDao.remove(accountId);
accountOperationDao.removeByAccountId(accountId);
transactionManager.commit(transactionStatus);
} catch (RuntimeException|Error e) {
transactionManager.rollback(transactionStatus);
throw e;
} catch (InstanceNotFoundException e) {
transactionManager.commit(transactionStatus);
throw e;
}
Finalizar
transacción
}
4 - 43
AOP: Gestión de Transacciones (4)
public void withdrawFromAccount(Long accountId, double amount)
throws InstanceNotFoundException, InsufficientBalanceException {
TransactionStatus transactionStatus =
transactionManager.getTransaction(null);
Iniciar
transacción
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);
4 - 44
AOP: Gestión de Transacciones (5)
/* Register account operation. */
accountOperationDao.save(new AccountOperation(account,
Calendar.getInstance(), AccountOperation.Type.WITHDRAW,
amount));
transactionManager.commit(transactionStatus);
} catch (RuntimeException|Error e) {
transactionManager.rollback(transactionStatus);
throw e;
} catch (InstanceNotFoundException|
InsufficientBalanceException e) {
transactionManager.commit(transactionStatus);
throw e;
}
Finalizar
transacción
}
...
}
4 - 45
AOP: Gestión de Transacciones (6)
n 
El código para todos los casos de uso transaccionales
es similar:
n 
n 
n 
n 
Iniciar la transacción (con las propiedades adecuadas) a
partir del gestor de transacciones utilizado
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
4 - 46
AOP: Gestión de Transacciones (y 7)
n 
Declarativamente es posible indicar
n 
n 
n 
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:
n 
[Antes de que se ejecute el método] Ejecuta el código
necesario para comenzar la transacción
n 
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)
n 
Pueden indicarse qué excepciones deben provocar un rollback y
cuales no
4 - 47
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 { ... }
4 - 48
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));
}
4 - 49
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 { ... }
}
4 - 50
Transacciones con Anotaciones
n 
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
<tx:annotation-driven transaction-manager="transactionManager" />
n 
El valor del atributo transaction-manager indica
el nombre del bean que debe ser utilizado como
gestor de transacciones
n 
n 
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
4 - 51
Anotación @Transactional (1)
n 
Puede utilizarse para anotar una clase o un método
concreto
n 
n 
n 
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:
n 
propagation:
n 
n 
n 
Permite especificar la semántica de la creación de la
transacción
El enumerado
org.springframework.transaction.annotation.Prop
agation define diversos valores
Por defecto toma el valor Propagation.REQUIRED: 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
4 - 52
Anotación @Transactional (2)
n 
Propiedades (cont.):
n 
isolation: Nivel de aislamiento
n 
n 
n 
n 
n 
n 
readOnly: Lectura/escritura (defecto) o solo lectura
n 
n 
Isolation.DEFAULT (valor por defecto): El de la plataforma
Isolation.READ_UNCOMMITED: Pueden ocurrir “dirty
reads”, “non-repeatable reads” y “phanton reads”
Isolation.READ_COMMITED: Pueden ocurrir “nonrepeatable reads” y “phamton reads”
Isolation.REPETEABLE_READ: Pueden ocurrir “phanton
reads”
Isolation.SERIALIZABLE: Elimina todos los problemas de
concurrencia
Importante indicarlo para poder hacer optimizaciones !!
timeout: Timeout de la transacción (por defecto el de la
plataforma)
n 
Si el timeout expira entonces se hace un rollback de la
transacción
4 - 53
Anotación @Transactional (y 3)
n 
Por defecto cualquier excepción de tipo “unchecked” (hija de
RuntimeException) provocará un rollback, y cualquier
excepción “checked” (hija de Exception) no
n 
Es posible modificar este comportamiento a través de propiedades
de la anotación @Transactional
n 
rollbackFor: array de excepciones que deben causar un rollback
n 
Ejemplo:
@Transactional(rollbackFor={Exception1.class, Exception2.class})
n 
n 
noRollbackFor: array de excepciones que no deben causar un
rollback
La utilizaremos en los servicios del modelo para declarar la
transaccionalidad de sus métodos
n 
Es posible anotar la interfaz que declara las operaciones del
servicio, pero es más recomendable anotar la clase de
implementación del servicio
4 - 54
Invocación de casos de uso
n 
n 
n 
Clase WithdrawFromAccount
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 = ctx.getBean(AccountService.class);
accountService.withdrawFromAccount(accountId, amount);
} catch (InstanceNotFoundException e) {
e.printStackTrace();
} catch (InsufficientBalanceException e) {
e.printStackTrace();
}
...
4 - 55
Implementación de Servicios (1)
n 
Los casos de uso se implementan en términos de
DAOs (tal como se vio en el tema 2)
n 
n 
n 
En los DAOs se inyecta un objeto de tipo
org.hibernate.SessionFactory creado por Spring (al
que a su vez se le inyecta un DataSource)
Los DAOs se inyectan en la clase de implementación del
Servicio
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
4 - 56
Implementación de Servicios (y 2)
n 
Para indicar la transaccionalidad de los casos de uso
utilizamos la anotación @Transactional sobre la
clase de implementación del Servicio
n 
Se declara un gestor de transacciones que delega en el
gestor de transacciones de Hibernate
n 
n 
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
La implementación de los Servicios es independiente
del software utilizado para el acceso a datos (en
nuestro caso Hibernate)
4 - 57