Transacciones Locales vs Distribuidas: Implementación en Java

Las transacciones son un componente fundamental en las aplciaciones web modernas, ya que garantizan la atomicidad, consistencia, aislamiento y durabilidad (propiedades ACID) de las operaciones de los usuarios. Es crucial entender sus dos implementaciones principales: las transacciones locales y las distribuidas.

Transacciones Locales

Una transacción local se gestiona dentro de un único recurso, como una base de datos, y opera sobre la conexión JDBC específica a ese recurso. Su ámbito está limitado a dicho recurso, lo que la hace sencilla y eficiente para operaciones con una sola fuente de datos, aunque no puede coordinar cambios en múltiples bases de datos.

El siguiente ejemplo muestra una transferencia entre cuentas utilizando una transacción local:


public void realizarTransferenciaLocal() {
    try (Connection conexion = obtenerOrigenDatos().getConnection()) {
        // Desactivar el autocommit para iniciar una transacción explícita
        conexion.setAutoCommit(false);
        
        try (Statement sentencia = conexion.createStatement()) {
            // Primer movimiento: débito en cuenta origen
            sentencia.executeUpdate(
                "UPDATE cuentas SET saldo = saldo - 300 WHERE id = 'ORIGEN'"
            );
            // Segundo movimiento: crédito en cuenta destino
            sentencia.executeUpdate(
                "UPDATE cuentas SET saldo = saldo + 300 WHERE id = 'DESTINO'"
            );
            
            // Si todo es correcto, confirmar los cambios en la base de datos
            conexion.commit();
            System.out.println("Transferencia local completada con éxito.");
        } catch (SQLException e) {
            // Deshacer todos los cambios en caso de error
            conexion.rollback();
            throw new RuntimeException("Error en la transferencia local", e);
        }
    } catch (SQLException e) {
        throw new RuntimeException("No se pudo obtener la conexión", e);
    }
}

En este código, el flujo consiste en establecer setAutoCommit(false) para iniciar un bloque transaccional, ejecutar las operaciones DML y finalmente invocar commit() si todo fue exitoso o rollback() si se produjo una excepción.

Transacciones Distribuidas

Las transacciones distribuidas están diseñadas para coordinar operaciones a través de múltiples recursos (bases de datos, sistemas de mensajería, etc.) garantizando su atomicidad. Esto se logra mediante un gestor de transacciones y el protocolo XA, que define la interacción entre el gestor y los diferentes gestores de recursos.

El siguiente código ilustra un escenaroi donde se modifica información en dos bases de datos distintas dentro de una sola transacción:


import javax.transaction.UserTransaction;
import javax.naming.InitialContext;

public void transferirEntreBaseDeDatos() {
    UserTransaction transaccion = null;
    try {
        // Obtener el objeto de transacción desde el entorno JNDI
        InitialContext contexto = new InitialContext();
        transaccion = (UserTransaction) contexto.lookup("java:comp/UserTransaction");
        
        // Obtener conexiones a las distintas bases de datos
        try (Connection connOracle = obtenerDataSourceOracle().getConnection();
             Connection connPostgres = obtenerDataSourcePostgres().getConnection()) {
            
            transaccion.begin(); // Iniciar la transacción distribuida
            
            // Operación en Oracle
            Statement stmtOracle = connOracle.createStatement();
            stmtOracle.executeUpdate(
                "UPDATE inventario SET stock = stock - 10 WHERE producto = 'ABC'"
            );
            
            // Operación en PostgreSQL
            Statement stmtPostgres = connPostgres.createStatement();
            stmtPostgres.executeUpdate(
                "INSERT INTO registro_ventas(producto, cantidad) VALUES ('ABC', 10)"
            );
            
            transaccion.commit(); // Confirmar ambos cambios de forma atómica
            System.out.println("Transacción distribuida finalizada.");
        }
    } catch (Exception e) {
        try {
            if (transaccion != null) {
                transaccion.rollback(); // Deshacer todos los cambios si hay un fallo
            }
        } catch (Exception rollbackEx) {
            // Manejo de error durante el rollback
        }
        throw new RuntimeException("Fallo en la transacción distribuida", e);
    }
}

La ejecución exitosa de commit() asegura que los cambios en ambos sistemas de bases de datos se apliquan de manera conjunta. Cualquier fallo durante el proceso desencadena un rollback() que revierte todas las operaciones participantes.

Etiquetas: JDBC JTA Transacciones Distribuidas Transacciones Locales java

Publicado el 6-16 01:37