java @transactional - Primavera @Transaccional-aislamiento, propagación




@transactional(readonly true) (9)

Alguien puede explicar para qué son los parámetros de aislamiento y propagación en la anotación @Transactional través del ejemplo del mundo real. Básicamente, cuándo y por qué debería elegir cambiar sus valores predeterminados.


Answers

Podemos añadir para esto:

@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {

    public Customer getDetail(String customername) {
        // do something
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateCustomer(Customer customer) {
        // do something
    }
}

Casi nunca desea utilizar Read Uncommited ya que no es realmente compatible con ACID . Read Commmited es un buen lugar de inicio predeterminado. Repeatable Read probablemente solo sea necesaria en los escenarios de informes, acumulación o agregación. Tenga en cuenta que muchos DBs, incluidos los postgres, no son compatibles con la Lectura repetible, en su lugar tiene que usar Serializable . Serializable es útil para cosas que sabes que tienen que suceder completamente independientemente de cualquier otra cosa; Piénsalo como synchronized en Java. Serializable va de la mano con la propagación REQUIRES_NEW .

Utilizo REQUIRES para todas las funciones que ejecutan consultas de ACTUALIZACIÓN o BORRADO, así como funciones de nivel de "servicio". Para las funciones de nivel DAO que solo ejecutan SELECTs, uso SUPPORTS que participarán en un TX si uno ya está iniciado (es decir, se está llamando desde una función de servicio).


PROPAGATION_REQUIRED = 0 ; Si DataSourceTransactionObject T1 ya se inició para el Método M1.Si para otro Método M2, se requiere un objeto de Transacción, no se crea ningún nuevo Objeto de Transacción. Se usa el mismo objeto T1 para M2

PROPAGATION_MANDATORY = 2 ; El método debe ejecutarse dentro de una transacción. Si no hay ninguna transacción en curso, se lanzará una excepción

PROPAGATION_REQUIRES_NEW = 3 ; Si DataSourceTransactionObject T1 ya está iniciado para el Método M1 y está en curso (ejecutando el método M1). Si otro método M2 comienza a ejecutarse, entonces T1 se suspende por la duración del método M2 con el nuevo DataSourceTransactionObject T2 para M2.M2 ejecutado en su propio contexto de transacción

PROPAGATION_NOT_SUPPORTED = 4 ; Si DataSourceTransactionObject T1 ya se inició para el Método M1.Si se ejecuta simultáneamente otro método M2. Luego, M2 no debe ejecutarse dentro del contexto de la transacción. T1 se suspende hasta que M2 se termina.

PROPAGATION_NEVER = 5 ; Ninguno de los métodos se ejecuta en el contexto de la transacción.

Un nivel de aislamiento: se trata de cuánto puede verse afectada una transacción por las actividades de otras transacciones concurrentes. Es compatible con la coherencia, dejando los datos en muchas tablas en un estado coherente. Implica el bloqueo de filas y / o tablas en una base de datos.

El problema con la transacción múltiple

Escenario 1. Si la transacción T1 lee datos de la tabla A1 que fue escrita por otra transacción concurrente T2.Si en la forma en que T2 se restaura, los datos obtenidos por T1 no son válidos. Eg a = 2 son datos originales. Si T1 lee a = 1 que fue escrito por T2.Si la reversión de T2 entonces a = 1 se revertirá a a = 2 en DB.But, Ahora, T1 tiene a = 1 pero en la tabla de DB se cambia a a = 2.

Escenario2 . Si la transacción T1 lee datos de la tabla A1.Si otra transacción concurrente (T2) actualiza datos en la tabla A1.Entonces los datos que T1 ha leído son diferentes de la tabla A1.Porque T2 ha actualizado los datos en la tabla A1.Eg si T1 lee a = 1 y T2 se actualiza a = 2.Entonces a! = b.

Escenario 3. Si la transacción T1 lee datos de la tabla A1 con cierto número de filas. Si otra transacción concurrente (T2) inserta más filas en la tabla A1.El número de filas leídas por T1 es diferente de las filas en la tabla A1

El escenario 1 se llama lecturas sucias.

El escenario 2 se llama lecturas no repetibles.

El escenario 3 se llama lecturas fantasmas.

Por lo tanto, el nivel de aislamiento es la extensión a la que se puede evitar el Escenario 1, Escenario 2, Escenario 3 . Puede obtener un nivel de aislamiento completo implementando el bloqueo. Esto impide que se produzcan lecturas y escrituras simultáneas en los mismos datos. Pero afecta el rendimiento. El nivel de aislamiento depende de la aplicación a la aplicación de cuánto aislamiento se requiere.

ISOLATION_READ_UNCOMMITTED : permite leer los cambios que aún no se han confirmado. Sufren de Escenario 1, Escenario 2, Escenario 3

ISOLATION_READ_COMMITTED : Permite lecturas de transacciones concurrentes que se han confirmado. Puede sufrir el Escenario 2 y el Escenario 3. Debido a que otras transacciones pueden estar actualizando los datos.

ISOLATION_REPEATABLE_READ : Las lecturas múltiples del mismo campo producirán los mismos resultados hasta que se modifiquen por sí mismas. Puede sufrir el Escenario 3.Debido a que otras transacciones pueden insertar los datos

ISOLATION_SERIALIZABLE : Escenario 1, Escenario 2, Escenario 3 nunca sucede. Es un aislamiento completo. Implica un bloqueo completo. Afirma el rendimiento debido al bloqueo.

Puedes probar usando

public class TransactionBehaviour {
   // set is either using xml Or annotation
    DataSourceTransactionManager manager=new DataSourceTransactionManager();
    SimpleTransactionStatus status=new SimpleTransactionStatus();
   ;


    public void beginTransaction()
    {
        DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
        // overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
        // set is either using xml Or annotation
        manager.setPropagationBehavior(XX);
        manager.setIsolationLevelName(XX);

        status = manager.getTransaction(Def);

    }

    public void commitTransaction()
    {


            if(status.isCompleted()){
                manager.commit(status);
        } 
    }

    public void rollbackTransaction()
    {

            if(!status.isCompleted()){
                manager.rollback(status);
        }
    }
    Main method{
        beginTransaction()
        M1();
        If error(){
            rollbackTransaction()
        }
         commitTransaction();
    }

}

Puede depurar y ver el resultado con diferentes valores para el aislamiento y la propagación.


Buena pregunta, aunque no trivial de responder.

Propagation

Define cómo las transacciones se relacionan entre sí. Opciones comunes

  • Required : el código siempre se ejecutará en una transacción. Cree una nueva transacción o reutilice una si está disponible.
  • Requires_new : el código siempre se ejecutará en una nueva transacción. Suspender la transacción actual si existe.

Isolation

Define el contrato de datos entre transacciones.

  • Read Uncommitted : permite lecturas sucias
  • Read Committed : no permite lecturas sucias
  • Repeatable Read : si una fila se lee dos veces en la misma transacción, el resultado siempre será el mismo
  • Serializable : Realiza todas las transacciones en una secuencia.

Los diferentes niveles tienen diferentes características de rendimiento en una aplicación de múltiples subprocesos. Creo que si entiendes el concepto de dirty reads podrás seleccionar una buena opción.

Ejemplo cuando puede ocurrir una lectura sucia

  thread 1   thread 2      
      |         |
    write(x)    |
      |         |
      |        read(x)
      |         |
    rollback    |
      v         v 
           value (x) is now dirty (incorrect)

Por lo tanto, un valor predeterminado sano (si se puede reclamar tal) podría ser Read Comitted , que solo le permite leer valores que ya han sido cometidos por otras transacciones en ejecución, en combinación con un nivel de propagación de Required . Entonces puedes trabajar desde allí si tu aplicación tiene otras necesidades.

Un ejemplo práctico donde siempre se creará una nueva transacción al ingresar a la rutina de servicio de provideService y se completará al salir.

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

Si hubiésemos utilizado Required la transacción permanecerá abierta si la transacción ya estaba abierta al ingresar la rutina. Tenga en cuenta también que el resultado de una rollback podría ser diferente, ya que varias ejecuciones podrían participar en la misma transacción.

Podemos verificar fácilmente el comportamiento con una prueba y ver cómo los resultados difieren con los niveles de propagación

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {

    private @Autowired TransactionManager transactionManager;
    private @Autowired FooService fooService;

    @Test
    public void testProvideService() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        fooService.provideService();
        transactionManager.rollback(status);
        // assert repository values are unchanged ... 
}

Con un nivel de propagación de

  • Requires new , esperaríamos que fooService.provideService() NO se haya revertido desde que creó su propia sub-transacción.

  • Required , esperaríamos que todo retrocediera y la tienda de respaldo permaneciera sin cambios.


Una transacción representa una unidad de trabajo con una base de datos.

En la interfaz Spring TransactionDefinition que define las propiedades de transacción compatibles con Spring. @Transactional anotación @Transactional transaccional describe atributos de transacción en un método o clase.

@Autowired
private TestDAO testDAO;

@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {

  // Interact with testDAO

}

Propagación (reproducción): se utiliza para la relación entre transacciones. (análogo a la comunicación entre subprocesos java)

+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value |        Propagation        |                                             Description                                              |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
|    -1 | TIMEOUT_DEFAULT           | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
|     0 | PROPAGATION_REQUIRED      | Support a current transaction; create a new one if none exists.                                      |
|     1 | PROPAGATION_SUPPORTS      | Support a current transaction; execute non-transactionally if none exists.                           |
|     2 | PROPAGATION_MANDATORY     | Support a current transaction; throw an exception if no current transaction exists.                  |
|     3 | PROPAGATION_REQUIRES_NEW  | Create a new transaction, suspending the current transaction if one exists.                          |
|     4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally.                     |
|     5 | PROPAGATION_NEVER         | Do not support a current transaction; throw an exception if a current transaction exists.            |
|     6 | PROPAGATION_NESTED        | Execute within a nested transaction if a current transaction exists.                                 |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+

Aislamiento: El aislamiento es una de las propiedades ACID (Atomicidad, Consistencia, Aislamiento, Durabilidad) de las transacciones de base de datos. El aislamiento determina cómo la integridad de la transacción es visible para otros usuarios y sistemas. Se utiliza para el bloqueo de recursos, es decir, control de concurrencia, asegúrese de que solo una transacción pueda acceder al recurso en un punto determinado.

Percepción de bloqueo: el nivel de aislamiento determina la duración de los bloqueos.

+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode      |  Read             |   Insert    |   Update    |       Lock Scope       |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED          |  uncommitted data | Allowed     | Allowed     | No Lock                |
| READ_COMMITTED (Default)  |   committed data  | Allowed     | Allowed     | Lock on Committed data |
| REPEATABLE_READ           |   committed data  | Allowed     | Not Allowed | Lock on block of table |
| SERIALIZABLE              |   committed data  | Not Allowed | Not Allowed | Lock on full table     |
+---------------------------+-------------------+-------------+-------------+------------------------+

Percepción de lectura: ocurren los siguientes 3 tipos de problemas principales:

  • Lecturas sucias : lee datos no confirmados de otra tx (transacción).
  • Lecturas no repetibles : lee las UPDATES confirmadas de otro tx.
  • DELETES fantasma : lee INSERTS y / o DELETES de otro tx

Niveles de aislamiento con diferentes tipos de lecturas:

+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode      |  Dirty reads   | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED          | allows         | allows               | allows         |
| READ_COMMITTED (Default)  | prevents       | allows               | allows         |
| REPEATABLE_READ           | prevents       | prevents             | allows         |
| SERIALIZABLE              | prevents       | prevents             | prevents       |
+---------------------------+----------------+----------------------+----------------+

por ejemplo


He ejecutado outerMethod , method_1 y method_2 con diferentes modos de propagación.

A continuación se muestra la salida para diferentes modos de propagación.

  • Método externo

    @Transactional
    @Override
    public void outerMethod() {
        customerProfileDAO.method_1();
        iWorkflowDetailDao.method_2();
    }
    
  • Método 1

    @Transactional(propagation=Propagation.MANDATORY)
    public void method_1() {
        Session session = null;
        try {
            session = getSession();
            Temp entity = new Temp(0l, "XXX");
            session.save(entity);
            System.out.println("Method - 1 Id "+entity.getId());
        } finally {
            if (session != null && session.isOpen()) {
            }
        }
    }
    
  • Método_2

    @Transactional()
    @Override
    public void method_2() {
        Session session = null;
        try {
            session = getSession();
            Temp entity = new Temp(0l, "CCC");
            session.save(entity);
            int i = 1/0;
            System.out.println("Method - 2 Id "+entity.getId());
        } finally {
            if (session != null && session.isOpen()) {
            }
        }
    }
    
      • exterior Método, sin transacción
      • method_1 - Propagation.MANDATORY) -
      • method_2 - Anotación de transacción solamente
      • Salida: method_1 lanzará la excepción de que no hay transacción existente
      • exterior Método, sin transacción
      • method_1 - Anotación de transacción solamente
      • method_2 - Propagation.MANDATORY)
      • Salida: method_2 lanzará la excepción de que no hay transacción existente
      • Salida: method_1 persistirá el registro en la base de datos.
      • exterior Método, con la transacción
      • method_1 - Anotación de transacción solamente
      • method_2 - Propagation.MANDATORY)
      • Salida: method_2 persistirá el registro en la base de datos.
      • Salida: method_1 persistirá el registro en la base de datos. - Aquí se utiliza la transacción principal externa existente para los métodos 1 y 2
      • exterior Método, con la transacción
      • method_1 - Propagation.MANDATORY) -
      • method_2 - Anotación de transacción solamente y lanza excepción
      • Salida: no se conserva ningún registro en la base de datos significa que se realiza la reversión.
      • exterior Método, con la transacción
      • method_1 - Propagation.REQUIRES_NEW)
      • method_2 - Propagation.REQUIRES_NEW) y lanza la excepción 1/0
      • Salida: method_2 lanzará una excepción, por lo que method_2 record no persistirá.
      • Salida: method_1 persistirá el registro en la base de datos.
      • Salida: no hay reversión para method_1

La explicación suficiente sobre cada parámetro está dada por otras respuestas; Sin embargo, solicitó un ejemplo del mundo real, aquí está el que aclara el propósito de las diferentes opciones de propagación :

Supongamos que está a cargo de implementar un servicio de registro en el que se envía un correo electrónico de confirmación al usuario. Se le ocurren dos objetos de servicio, uno para inscribir al usuario y otro para enviar correos electrónicos, al que se llama este último dentro del primero. Por ejemplo, algo como esto:

/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
 ...
 void SignUp(User user){
    ...
    emailService.sendMail(User);
 }
}

/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
 ...
 void sendMail(User user){
  try{
     ... // Trying to send the e-mail
  }catch( Exception)
 }
}

Es posible que haya notado que el segundo servicio es del tipo de propagación REQUIRES_NEW y, además, es probable que arroje una excepción (servidor SMTP inactivo, correo electrónico no válido u otras razones). Es probable que no desee que todo el proceso retroceda, como eliminar la información del usuario de la base de datos u otras cosas; por lo tanto, usted llama al segundo servicio en una transacción separada.

De vuelta a nuestro ejemplo, esta vez le preocupa la seguridad de la base de datos, por lo que define sus clases DAO de esta manera:

/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
 // some CRUD methods
}

Lo que significa que cada vez que se crea un objeto DAO y, por lo tanto, un acceso potencial a db, debemos asegurarnos de que la llamada se realizó desde uno de nuestros servicios, lo que implica que debe existir una transacción en vivo; de lo contrario se produce una excepción. Por lo tanto, la propagación es de tipo MANDATORIO .


El nivel de aislamiento define cómo los cambios realizados en un repositorio de datos por una transacción afectan a otras transacciones simultáneas simultáneas, y también cómo y cuándo ese cambio de datos está disponible para otras transacciones. Cuando definimos una transacción utilizando el marco de Spring, también podemos configurar en qué nivel de aislamiento se ejecutará esa misma transacción.

@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {

}

El nivel de aislamiento READ_UNCOMMITTED indica que una transacción puede leer datos que aún no están comprometidos por otras transacciones.

El nivel de aislamiento READ_COMMITTED indica que una transacción no puede leer datos que otras transacciones aún no han confirmado.

El nivel de aislamiento REPEATABLE_READ indica que si una transacción lee un registro de la base de datos varias veces, el resultado de todas esas operaciones de lectura debe ser siempre el mismo.

El nivel de aislamiento SERIALIZABLE es el más restrictivo de todos los niveles de aislamiento. Las transacciones se ejecutan con bloqueo en todos los niveles (bloqueo de lectura, rango y escritura), por lo que aparecen como si se hubieran ejecutado de forma serializada.

La propagación es la capacidad de decidir cómo deben encapsularse los métodos de negocio en transacciones lógicas o físicas.

El comportamiento de Spring REQUIRED significa que se utilizará la misma transacción si ya hay una transacción abierta en el contexto de ejecución del método de bean actual.

El comportamiento de REQUIRES_NEW significa que el contenedor siempre creará una nueva transacción física.

El comportamiento NESTED hace que las transacciones Spring anidadas utilicen la misma transacción física, pero establece puntos de salvaguarda entre invocaciones anidadas para que las transacciones internas también puedan revertirse independientemente de las transacciones externas.

El comportamiento OBLIGATORIO indica que una transacción abierta existente ya debe existir. Si no, se lanzará una excepción por el contenedor.

El comportamiento NUNCA indica que una transacción abierta existente no debe existir ya. Si existe una transacción, el contenedor lanzará una excepción.

El comportamiento NOT_SUPPORTED se ejecutará fuera del alcance de cualquier transacción. Si ya existe una transacción abierta, se pausará.

El comportamiento de SOPORTES se ejecutará en el ámbito de una transacción si ya existe una transacción abierta. Si no hay una transacción ya abierta, el método se ejecutará de todos modos, pero de manera no transaccional.


Un @Service para citar documentación de primavera,

Indica que una clase anotada es un "Servicio", originalmente definido por Domain-Driven Design (Evans, 2003) como "una operación ofrecida como una interfaz que se encuentra sola en el modelo, sin estado encapsulado". También puede indicar que una clase es un "Business Service Facade" (en el sentido de los patrones Core J2EE), o algo similar. Esta anotación es un estereotipo de propósito general y los equipos individuales pueden restringir su semántica y usar según sea apropiado.

Si nos fijamos en el diseño impulsado por el dominio por eric evans,

Un SERVICIO es una operación que se ofrece como una interfaz independiente del modelo, sin encapsular el estado, como lo hacen ENTIDADES y OBJETOS DE VALOR. Los SERVICIOS son un patrón común en los marcos técnicos, pero también pueden aplicarse en la capa de dominio. El servicio de nombres enfatiza la relación con otros objetos. A diferencia de las ENTIDADES y los OBJETOS DE VALOR, se define puramente en términos de lo que puede hacer por un cliente. Un SERVICIO tiende a ser nombrado para una actividad, en lugar de una entidad, un verbo en lugar de un sustantivo. Un SERVICIO todavía puede tener una definición abstracta e intencional; solo tiene un sabor diferente a la definición de un objeto. Un SERVICIO aún debe tener una responsabilidad definida, y esa responsabilidad y la interfaz que la cumple deben definirse como parte del modelo de dominio. Los nombres de las operaciones deben provenir del IDIOMA UBIQUITOSO o introducirse en él. Los parámetros y resultados deben ser objetos de dominio. Los SERVICIOS deben usarse con prudencia y no se les debe permitir despojar a las ENTIDADES y OBJETOS DE VALOR de todo su comportamiento. Pero cuando una operación es realmente un concepto de dominio importante, un SERVICIO forma parte natural de un DISEÑO MODELO CON CONDUCCIÓN. Declarada en el modelo como SERVICIO, en lugar de como un objeto falso que no representa nada, la operación independiente no engañará a nadie.

y un Repository según Eric Evans,

Un REPOSITORIO representa todos los objetos de un determinado tipo como un conjunto conceptual (generalmente emulado). Actúa como una colección, excepto con una capacidad de consulta más elaborada. Los objetos del tipo apropiado se agregan y eliminan, y la maquinaria detrás del REPOSITORIO los inserta o elimina de la base de datos. Esta definición reúne un conjunto cohesivo de responsabilidades para proporcionar acceso a las raíces de AGREGADOS desde el comienzo del ciclo de vida hasta el final.







java spring transactional isolation propagation