java - example - spring transaction propagation
¿A dónde pertenece la anotación @Transactional? (19)
¿Debería colocar @Transactional
en las clases DAO
y / o sus métodos o es mejor anotar las clases de servicio que están usando los objetos DAO? ¿O tiene sentido anotar ambas "capas"?
¡Es mejor tenerlo en la capa de servicio! ¡Esto se explica claramente en uno de los artículos que encontré ayer! Aquí está el enlace que puedes revisar!
Además, Spring recomienda solo usar la anotación en clases concretas y no interfaces.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Creo que las transacciones pertenecen a la capa de servicio. Es el que conoce sobre unidades de trabajo y casos de uso. Es la respuesta correcta si tiene varios DAO inyectados en un Servicio que necesitan trabajar juntos en una sola transacción.
El caso normal sería anotar en un nivel de capa de servicio, pero esto realmente depende de sus requisitos.
La anotación en una capa de servicio resultará en transacciones más largas que en la anotación en el nivel DAO. Dependiendo del nivel de aislamiento de la transacción que pueda usar, ya que las transacciones concurrentes no se verán los cambios de los demás, por ejemplo. READATABLE LEER.
Las anotaciones en los DAO mantendrán las transacciones lo más cortas posible, con el inconveniente de que la funcionalidad que su capa de servicio está exponiendo no se realizará en una sola transacción (reversible).
No tiene sentido anotar ambas capas si el modo de propagación está configurado por defecto.
En general, estoy de acuerdo con los demás que indican que las transacciones generalmente se inician en el nivel de servicio (dependiendo de la granularidad que requiera, por supuesto).
Sin embargo, @Transactional(propagation = Propagation.MANDATORY)
también comencé a agregar @Transactional(propagation = Propagation.MANDATORY)
a mi capa DAO (y otras capas a las que no se les permite iniciar transacciones pero que requieren de las existentes) porque es mucho más fácil detectar errores donde usted tiene olvidado iniciar una transacción en la persona que llama (por ejemplo, el servicio). Si su DAO está anotado con propagación obligatoria, obtendrá una excepción que indica que no hay una transacción activa cuando se invoca el método.
También tengo una prueba de integración en la que verifico todos los beans (postprocesador de bean) para esta anotación y fallo si hay una anotación @Transactional
con propagación diferente a la obligatoria en un bean que no pertenece a la capa de servicios. De esta manera me aseguro de que no iniciemos transacciones en la capa incorrecta.
En primer lugar vamos a definir dónde tenemos que usar la transacción ?
Creo que la respuesta correcta es: cuando necesitamos asegurarnos de que la secuencia de acciones se complete como una sola operación atómica o no se realicen cambios, incluso si falla una de las acciones.
Es una práctica bien conocida poner la lógica de negocios en los servicios. Por lo tanto, los métodos de servicio pueden contener diferentes acciones que deben realizarse como una sola unidad lógica de trabajo. Si es así, entonces dicho método debe estar marcado como Transaccional . Por supuesto, no todos los métodos requieren dicha limitación, por lo que no es necesario que marque todo el servicio como transaccional .
Y aún más: no olvide tener en cuenta que @Transactional , obviamente, puede reducir el rendimiento del método. Para ver la imagen completa, debe conocer los niveles de aislamiento de las transacciones. Saber eso podría ayudarlo a evitar el uso de @Transactional donde no es necesariamente necesario.
Es mejor mantener @Transactional en una capa intermedia separada entre DAO y Service Layer. Dado que la reversión es muy importante, puede colocar toda la manipulación de su base de datos en la capa intermedia y escribir la lógica empresarial en la capa de servicio. La capa intermedia interactuará con tus capas DAO.
Esto lo ayudará en muchas situaciones como ObjectOptimisticLockingFailureException : esta excepción se produce solo después de que finalice su transacción. Por lo tanto, no puede capturarlo en la capa intermedia, pero puede capturarlo en su capa de servicio ahora. Esto no sería posible si tiene @Transactional en la capa de Servicio. Aunque puede atrapar en el Controlador, el Controlador debe estar lo más limpio posible.
Si está enviando correo o SMS en un hilo separado después de completar todas las opciones de guardar, eliminar y actualizar, puede hacer esto en servicio después de que se complete la transacción en su capa intermedia. Nuevamente, si menciona @Transactional en la capa de servicio, su correo irá incluso si su transacción falla.
Por lo tanto, tener una capa intermedia de @Transaction ayudará a que su código sea mejor y más fácil de manejar. De lo contrario, si utiliza en la capa DAO, es posible que no pueda deshacer todas las operaciones. Si usa en la capa de Servicio, puede que tenga que usar AOP (Programación Orientada a Aspectos) en ciertos casos.
Idealmente, la capa de servicio (administrador) representa la lógica de su negocio y, por lo tanto, debe anotarse con @Transactional
La capa de servicio puede llamar a diferentes DAO para realizar operaciones de base de datos. Asumamos situaciones en las que tiene un número N de operaciones DAO en un método de servicio. Si su primera operación DAO falló, es posible que se sigan pasando otros y terminará en un estado de base de datos inconsistente. La capa de servicio de anotación puede salvarlo de tales situaciones.
La capa de servicio es el mejor lugar para agregar @Transactional
anotaciones @Transactional
ya que la mayoría de la lógica empresarial presente aquí contiene un comportamiento de caso de uso de nivel de detalle.
Supongamos que lo agregamos a DAO y desde el servicio estamos llamando a 2 clases DAO, una fallida y otra exitosa, en este caso si @Transactional
no está en servicio, se comprometerá una base de datos y la otra se revertirá.
Por lo tanto, mi recomendación es usar sabiamente esta anotación y usarla solo en la capa de Servicio.
La respuesta correcta para las arquitecturas tradicionales de Spring es colocar la semántica transaccional en las clases de servicio, por las razones que otros ya han descrito.
Una tendencia emergente en Spring es hacia el diseño controlado por dominio (DDD). Spring Roo ejemplifica muy bien la tendencia. La idea es hacer que el objeto de dominio POJO sea mucho richer que en las arquitecturas típicas de Spring (generalmente son anemic ) y, en particular, poner semántica de transacción y persistencia en los objetos de dominio. En los casos en los que todo lo que se necesita son simples operaciones CRUD, los controladores web operan directamente en los objetos de dominio (POJO) del objeto (funcionan como entidades en este contexto) y no hay nivel de servicio. En los casos en que se necesita algún tipo de coordinación entre los objetos del dominio, puede tener un bean de servicio que se encargue de eso, con @Transaction
según la tradición. Puede establecer la propagación de transacciones en los objetos del dominio en algo así como REQUIRED
para que los objetos del dominio utilicen cualquier transacción existente, como las transacciones que se iniciaron en el bean de servicio.
Técnicamente, esta técnica hace uso de AspectJ y <context:spring-configured />
. Roo usa las definiciones de los tipos de AspectJ para separar la semántica de la entidad (transacciones y persistencia) de las cosas del objeto del dominio (básicamente, campos y métodos comerciales).
Las anotaciones transaccionales se deben colocar alrededor de todas las operaciones que son inseparables.
Por ejemplo, su llamada es "cambiar contraseña". Que consta de dos operaciones.
- Cambia la contraseña.
- Auditar el cambio.
- Envíe un correo electrónico al cliente que la contraseña ha cambiado.
Entonces, en lo anterior, si la auditoría falla, ¿también debería fallar el cambio de contraseña? Si es así, entonces la transacción debe ser de alrededor de 1 y 2 (por lo tanto, en la capa de servicio). Si el correo electrónico falla (probablemente debería haber algún tipo de protección contra fallas en este para que no falle), ¿debería revertir la contraseña de cambio y la auditoría?
Este es el tipo de preguntas que debe hacer al decidir dónde colocar @Transactional
.
Para la transacción en el nivel de base de datos
principalmente, utilicé @Transactional
en DAO''s solo en el nivel de método, por lo que la configuración puede ser específicamente para un método / usando el valor predeterminado (requerido)
El método de DAO que obtiene la obtención de datos (selección ..): no necesita
@Transactional
esto puede generar cierta sobrecarga debido al interceptor de transacciones / y al proxy AOP que también deben ejecutarse.Los métodos de DAO que hacen insertar / actualizar obtendrán
@Transactional
Muy buen blog sobre transctional
Para nivel de aplicación -
Estoy usando transaccional para la lógica empresarial. Me gustaría poder reinvertir en caso de un error inesperado.
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Por lo general, uno debe poner una transacción en la capa de servicio.
Pero como se dijo antes, la atomicidad de una operación es lo que nos dice dónde es necesaria una anotación. Por lo tanto, si utiliza marcos como Hibernate, donde una sola operación de "guardar / actualizar / eliminar / ... modificación" en un objeto tiene la posibilidad de modificar varias filas en varias tablas (debido a la cascada a través del gráfico de objetos), de Por supuesto, también debe haber administración de transacciones en este método DAO específico.
Prefiero usar @Transactional
en la capa de servicios en el nivel de método.
El @Transactional
debe usarse en la capa de servicio ya que contiene la lógica de negocios. La capa DAO usualmente tiene solo operaciones de base de datos CRUD.
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
Spring doc: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
@Transactional
anotaciones de @Transactional
deben colocar alrededor de todas las operaciones que son inseparables. El uso de la propagación de transacciones @Transactional
se maneja automáticamente. En este caso, si otro método es llamado por el método actual, entonces ese método tendrá la opción de unirse a la transacción en curso.
Así que vamos a tomar el ejemplo:
Tenemos 2 modelos, es decir, Country
y City
. El modelo de mapeo relacional de Country
y City
es como un Country
puede tener múltiples ciudades, por lo que el mapeo es:
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
Aquí el país mapeado a varias ciudades con ir a buscarlas Lazily
. Así que aquí viene el rol de @Transactinal
cuando recuperamos el objeto País de la base de datos, luego obtendremos todos los datos del objeto País, pero no obtendremos el Conjunto de ciudades porque estamos buscando ciudades LAZILY
.
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
Cuando deseamos acceder al conjunto de ciudades del objeto país, obtendremos valores nulos en ese conjunto porque el objeto del conjunto creado solo este conjunto no se inicializa con los datos para obtener los valores del conjunto que usamos @Transactional
, es decir,
//with @Transactional
@Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
Object object = country.getCities().size();
}
Básicamente, @Transactional
is Service puede hacer varias llamadas en una sola transacción sin cerrar la conexión con el punto final.
@Transactional
el @Transactional
en la capa @Service
y configuro rollbackFor
cualquier excepción y readOnly
para optimizar aún más la transacción.
Por defecto, @Transactional
solo buscará RuntimeException
(Excepciones no verificadas), al establecer rollback en Exception.class
(Excepciones marcadas) se revertirá para cualquier excepción.
@Transactional(readOnly = false, rollbackFor = Exception.class)
@Transactional
utiliza en la capa de servicio que se llama mediante la capa de controlador ( @Controller
) y la llamada de la capa de servicio a la capa DAO ( @Repository
), es decir, la operación relacionada con la base de datos.
¿O tiene sentido anotar ambas "capas"? - ¿No tiene sentido anotar tanto la capa de servicio como la capa dao? - si uno quiere asegurarse de que el método DAO siempre sea llamado (propagado) desde una capa de servicio con propagación "obligatoria" en DAO. Esto proporcionaría alguna restricción para que los métodos DAO no sean llamados desde la capa UI (o controladores). Además, cuando la unidad de prueba de la capa DAO en particular, tener una anotación DAO también asegurará que se pruebe la funcionalidad transaccional.