¿Dónde pertenece la anotación @Transactional?

528

¿Debería colocar el @Transactionalen las DAOclases y / o sus métodos o es mejor anotar las clases de Servicio que están llamando usando los objetos DAO? ¿O tiene sentido anotar ambas "capas"?

Thomas Einwaller
fuente

Respuestas:

537

Creo que las transacciones pertenecen a la capa de Servicio. Es el que sabe 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.

duffymo
fuente
Estoy de acuerdo con eso. A veces no importa, pero a veces puede beneficiarse de eso, por ejemplo, la sesión de Hibernate se extiende por la transacción while, por lo que todos los objetos cargados están en la caché de primer nivel y no necesita volver a conectar los objetos a la sesión nuevamente, además de cargar lentamente Las propiedades funcionan sin fuzz.
dma_k
55
¿Puede una transacción global consistir en más de uno de estos @Transactional (propagation = Propagation.REQUIRED)? ¿O @Transactional es siempre un límite para una transacción? No estoy seguro de que tengo de la documentación, pero parece un poco que se puede crear incluso las transacciones que consisten en métodos @Transactional y todo lo que se ejecuta dentro
Lisak
1
Sí, creo que el @Transactional más externo es el que se convierte en el límite de la transacción si Propagation.REQUIRED está activado.
duffymo
309

En general, estoy de acuerdo con los otros que afirman 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)mientras tanto, también comencé a agregar a mi capa DAO (y otras capas que no pueden iniciar transacciones pero requieren las existentes) porque es mucho más fácil detectar errores en los que ha 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 donde verifico todos los beans (postprocesador de bean) para esta anotación y fallo si hay una @Transactionalanotación con propagación que no sea obligatoria en un bean que no pertenece a la capa de servicios. De esta manera, me aseguro de no iniciar transacciones en la capa incorrecta.

mnp
fuente
3
Si esto se hace en la capa dao (interfaces), en el dao impl. capa o ambos?
Johan
3
No sé si encaja en esta discusión, pero otro consejo podría ser agregar @Transactional (readOnly = true) en el dao impl en las operaciones no escritas.
maxivis
10
@Johan Spring aconseja colocar las anotaciones de Transacción en las clases de implementación en lugar de las interfaces.
Mathias G.
Realmente me gusta esta idea, intentar eso también para mis proyectos
Thomas Einwaller
1
Déjame ver si entiendo ... ¿estás diciendo que debería poner @Transactionalla clase de implementación del Servicio, y debería poner @Transactional(propagation = MANDATORY)la implementación de la clase DAO (repositorio)?
John Henckel
93

Las anotaciones transaccionales se deben colocar alrededor de todas las operaciones que son inseparables.

Por ejemplo, su llamada es "cambiar contraseña". Que consiste en dos operaciones

  1. Cambia la contraseña.
  2. Auditar el cambio.
  3. 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í, la transacción debería ser alrededor de 1 y 2 (por lo tanto, en la capa de servicio). Si el correo electrónico falla (probablemente debería tener algún tipo de protección contra fallas para que no falle), ¿debería revertir el cambio de contraseña y la auditoría?

Este es el tipo de preguntas que debe hacerse al decidir dónde colocar el @Transactional.

Michael Wiles
fuente
41

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 la primavera es hacia el diseño impulsado por dominio (DDD). Spring Roo ejemplifica muy bien la tendencia. La idea es hacer que los POJO de objetos de dominio sean mucho más ricos de lo que son en las arquitecturas típicas de Spring (generalmente son anémicas ), y en particular poner semántica de transacciones y persistencia en los objetos de dominio. En los casos en que todo lo que se necesita son operaciones CRUD simples, los controladores web operan directamente en los POJO de objetos de dominio (funcionan como entidades en este contexto), y no hay nivel de servicio. En los casos en que se necesite algún tipo de coordinación entre los objetos de dominio, puede tener un identificador de servicio que, con@Transactionsegún la tradición Puede establecer la propagación de transacciones en los objetos de dominio en algo así REQUIREDpara que los objetos de 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 utiliza las definiciones entre tipos de AspectJ para separar la semántica de la entidad (transacciones y persistencia) de las cosas del objeto de dominio (básicamente campos y métodos comerciales).

naXa
fuente
38

El caso normal sería anotar en un nivel de capa de servicio, pero esto realmente depende de sus requisitos.

Anotar en una capa de servicio dará como resultado transacciones más largas que anotar en el nivel DAO. Dependiendo del nivel de aislamiento de la transacción que puede solucionar problemas, ya que las transacciones concurrentes no verán los cambios de cada uno, por ejemplo. LECTURA REPETIBLE.

Anotar en los DAO mantendrá 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 (revertible).

No tiene sentido anotar ambas capas si el modo de propagación está configurado por defecto.

Hammarback
fuente
31

Coloco el @Transactionalen la @Servicecapa y establezco rollbackForcualquier excepción y readOnlypara optimizar aún más la transacción.

De manera predeterminada @Transactional, solo buscará RuntimeException(Excepciones no marcadas), al establecer la reversión en Exception.class(Excepciones marcadas), se revertirá para cualquier excepción.

@Transactional(readOnly = false, rollbackFor = Exception.class)

Consulte Excepciones marcadas frente a no marcadas .

user2601995
fuente
17

¿O tiene sentido anotar ambas "capas"? - no tiene sentido anotar tanto la capa de servicio como la capa de dao - si uno quiere asegurarse de que el método DAO siempre se llame (propague) desde una capa de servicio con propagación "obligatoria" en DAO. Esto proporcionaría alguna restricción para que los métodos DAO se invoquen desde la capa UI (o controladores). Además, cuando la unidad prueba la capa DAO en particular, tener DAO anotado también garantizará que se pruebe la funcionalidad transaccional.

tweekran
fuente
¿Hacer esto no daría lugar a transacciones anidadas? ¿Y todos los problemas sutiles que lo acompañan?
HDave
11
No, no hay transacciones anidadas en JPA. Ponerlos en ambos estaría perfectamente bien: si ya está en una transacción cuando golpea el DAO, esa transacción continuará.
fool4jesus
44
La transacción anidada puede ocurrir si la usa propagation=Propagation.REQUIRES_NEW. De lo contrario, para la mayoría de los casos, incluida la propagación = obligatoria, el DAO solo participará en la transacción existente iniciada por la capa de servicio.
Dan Carter
15

Para transacciones en el nivel de base de datos

principalmente lo usé @Transactionalen DAO solo a nivel de método, por lo que la configuración puede ser específicamente para un método / uso predeterminado (requerido)

  1. El método de DAO que obtiene la búsqueda de datos (seleccione ...): no es necesario, @Transactional esto puede generar algunos gastos generales debido al interceptor de transacciones / y al proxy AOP que también deben ejecutarse.

  2. Los métodos de DAO que insertan / actualizan obtendrán @Transactional

muy buen blog sobre transctional

Para el nivel de aplicación:
estoy usando la lógica transaccional para negocios, me gustaría poder deshacerme en caso de error inesperado

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}
lukass77
fuente
66
+1 muy buen artículo sobre TransactionalenJava
roble
11

Por lo general, uno debe colocar una transacción en la capa de servicio.

Pero como se dijo anteriormente, 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 "guardar / actualizar / eliminar / ... modificación" en un objeto tiene el potencial de modificar varias filas en varias tablas (debido a la cascada a través del gráfico de objetos), de Por supuesto, también debería haber gestión de transacciones en este método DAO específico.

yannick555
fuente
10

@TransactionalLas anotaciones deben colocarse alrededor de todas las operaciones que son inseparables. El uso de la @Transactionalpropagación de transacciones se maneja automáticamente. En este caso, si el método actual llama a otro método, ese método tendrá la opción de unirse a la transacción en curso.

Entonces tomemos ejemplo:

Tenemos 2 modelos de ie Countryy City. El mapeo relacional Countryy el Citymodelo es como uno Countrypuede tener múltiples ciudades, por lo que el mapeo es como,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Aquí el país se asignó a varias ciudades con ir a buscarlas Lazily. Así que aquí viene el papel de @Transactinalcuando recuperamos el objeto Country de la base de datos, entonces obtendremos todos los datos del objeto Country 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 desde el objeto de 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 utilizamos @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();   
}

Entonces, básicamente, el @TransactionalServicio puede realizar múltiples llamadas en una sola transacción sin cerrar la conexión con el punto final.

Harshal Patil
fuente
2
muy informativo, gracias! Justo lo que estaba buscando, una explicación de lo que @Transactionalrealmente es
CybeX 18/06/18
6

¡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 consultar!

límite
fuente
5

ingrese la descripción de la imagen aquí

El @Transactionaldebe utilizar en la capa de servicio, ya que contiene la lógica de negocio. La capa DAO generalmente solo tiene operaciones CRUD de bases de datos.

// 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);
}

Documento de Spring: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html

Alin
fuente
5

La capa de servicio es el mejor lugar para agregar @Transactionalanotaciones, ya que la mayoría de la lógica de negocios 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 @Transactionalno está en servicio, una DB se comprometerá y otra se revertirá.

Por lo tanto, mi recomendación es usar esta anotación sabiamente y usarla solo en la capa de Servicio.

Proyecto Github- java-algos

VishalTambe
fuente
Excepciones como ObjectOptimisticLockingFailureException ocurren solo después de que se completa la transacción. Si tiene hilos separados para otra operación como el servicio de correo, este diseño falla totalmente. Estamos sufriendo en este momento. La única solución restante sería AOP.
Nabin Kumar Khatiwada
4

En primer lugar vamos a definir todos de donde tenemos que el uso de transacciones ?

Creo que la respuesta correcta es: cuando necesitamos asegurarnos de que la secuencia de acciones se completará como una operación atómica o no se realizarán cambios, incluso si una de las acciones falla.

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 única unidad lógica de trabajo. Si es así, entonces dicho método debe marcarse como Transaccional . Por supuesto, no todos los métodos requieren tal limitación, por lo que no necesita marcar 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 una imagen completa, debe conocer los niveles de aislamiento de las transacciones. Saber eso podría ayudarlo a evitar usar @Transactional donde no es necesariamente necesario.

smaiakov
fuente
3

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 su manipulación de la base de datos en la capa intermedia y escribir la lógica empresarial en la capa de servicio. La capa intermedia interactuará con sus capas DAO.

Esto lo ayudará en muchas situaciones, como ObjectOptimisticLockingFailureException : esta excepción se produce solo después de que finaliza su transacción. Por lo tanto, no puede atraparlo en la capa intermedia, pero puede atraparlo en su capa de servicio ahora. Esto no sería posible si tiene @Transactional en la capa de Servicio. Aunque puede atrapar 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 hacerlo en el 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 se enviará incluso si su transacción falla.

Por lo tanto, tener una capa media de @Transaction ayudará a que su código sea mejor y más fácil de manejar. De lo contrario, si usa 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.

Nabin Kumar Khatiwada
fuente
2

Idealmente, la capa de Servicio (Administrador) representa la lógica de su negocio y, por lo tanto, debe anotarse con. @TransactionalLa capa de servicio puede llamar a diferentes DAO para realizar operaciones de base de datos. Supongamos una situación en la que tiene N número de operaciones DAO en un método de servicio. Si su primera operación DAO falló, es posible que se pasen otras y usted terminará en un estado de base de datos inconsistente. La capa de servicio de anotación puede salvarlo de tales situaciones.

salsinga
fuente
1

Prefiero usar @Transactionalen la capa de servicios a nivel de método.

Spark-Beginner
fuente
1

@Transactionalutiliza en la capa de servicio que se llama mediante el uso de 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.

se sentó
fuente