CodeGym /Cursos /Módulo 5. Spring /Qué son las transacciones y para qué sirven

Qué son las transacciones y para qué sirven

Módulo 5. Spring
Nivel 6 , Lección 0
Disponible

Cada día nos enfrentamos a operaciones que deben completarse por completo o no ejecutarse en absoluto. Imagina una transferencia bancaria: el dinero debe desaparecer de una cuenta y aparecer en otra. No puede quedarse a medio camino. En programación, a esas operaciones se les llama transacciones.

La transacción como unidad básica de trabajo

Una transacción es un grupo de operaciones que deben ejecutarse como un todo. O bien todas las operaciones pasan con éxito (commit), o el sistema revierte todos los cambios (rollback), como si no hubiera pasado nada. Es como un sistema de puntos de guardado en un juego: si algo sale mal, siempre puedes volver a la última partida guardada.

Para que una transacción se considere correcta, debe tener las siguientes propiedades, que se agrupan en la abreviatura ACID:

Propiedad Descripción
Atomicidad (Atomicity) Todas las operaciones dentro de la transacción se ejecutan como un todo. O todo, o nada.
Consistencia (Consistency) Tras completar la transacción, el sistema queda en un estado consistente.
Aislamiento (Isolation) Las operaciones de la transacción están aisladas de las transacciones paralelas (sin "interferencias").
Durabilidad (Durability) Tras completar la transacción, sus resultados se conservan incluso en caso de fallo del sistema.

Imaginemos una transferencia bancaria. Quieres transferir dinero de tu cuenta a la cuenta de un amigo. El proceso se puede dividir en los siguientes pasos: 1. Disminuir el saldo en tu cuenta. 2. Aumentar el saldo en la cuenta del amigo.

Si ocurre un fallo entre esos pasos (por ejemplo, se interrumpe la conexión con el banco), nos arriesgamos a "perder" dinero o a "crearlo" de la nada. Para evitar estas situaciones, ambas operaciones se agrupan en una transacción. Si alguno de los pasos falla, todos los cambios se deshacen y el sistema vuelve al estado inicial.


Por qué se necesitan las transacciones en las aplicaciones

En el mundo del desarrollo, las transacciones juegan un papel clave para asegurar la corrección de los datos, especialmente cuando las aplicaciones trabajan con bases de datos. Veamos por qué no se puede prescindir de ellas.

Asegurar la consistencia de los datos

Volvamos al ejemplo de la transferencia bancaria: imagina que el servidor del banco de pronto "se cae" justo después de debitar el dinero de tu cuenta. Sin transacciones, tu amigo no recibiría la transferencia y tú te quedarías sin dinero. No es la mejor situación, ¿verdad?

Las transacciones resuelven esto de forma elegante: o el dinero se transfiere con éxito, o vuelve a tu cuenta — no hay pagos atascados. Esto es crítico para aplicaciones serias: tiendas online, sistemas de reservas, plataformas médicas.

Trabajo en entornos multiusuario

En aplicaciones modernas a menudo se dan situaciones donde varios usuarios intentan modificar los mismos datos:

  • Dos compradores van a por el último par de zapatillas
  • Varios gestores editan simultáneamente la ficha de un cliente en la CRM

Sin transacciones esto puede llevar al caos en los datos. Las transacciones actúan como un regulador inteligente: no permiten que las operaciones se interfieran entre sí.


Problemas que resuelven las transacciones

Ahora veamos los problemas principales que se resuelven con transacciones.

Prevención de cambios parciales

Imagina que trabajas en el modelo de un pedido en una tienda online. Crear un pedido puede incluir los siguientes pasos:

  1. Crear la entrada del nuevo pedido en la base de datos.
  2. Reducir la cantidad disponible del producto en el inventario.
  3. Enviar una notificación al usuario.

Si ocurre un fallo, por ejemplo en el segundo paso, se crea una situación de "completitud parcial": el pedido quedará registrado en el sistema, pero el producto seguirá apareciendo como disponible. Esto puede provocar problemas serios, sobre todo en sistemas a escala. Las transacciones ayudan a evitar esos escenarios.

Integridad de datos ante fallos

Los fallos, como sabemos, son inevitables. Por ejemplo:

  • Algo falló al guardar datos en la base.
  • La red se cayó.
  • La aplicación lanzó una excepción.

Con un buen sistema transaccional puedes estar seguro: ningún cambio "roto" llegará a tu base de datos.

Eliminación de conflictos en trabajo paralelo

Otro problema es cuando varios procesos o usuarios intentan modificar al mismo tiempo los mismos datos. Por ejemplo:

  1. Empleado A añadió un comentario en la CRM.
  2. Empleado B simultáneamente borró ese mismo comentario.

¿Quién gana? Sin un mecanismo de aislamiento de transacciones, el resultado puede ser impredecible. Pero con la configuración adecuada, las transacciones paralelas estarán protegidas de interferencias mutuas.


Ejemplo: transacciones en una tienda online

Vamos a crear un ejemplo simple donde se usan transacciones para procesar un pedido en una tienda online. Nuestro escenario:

  1. Crear la entrada del nuevo pedido.
  2. Reducir el stock del producto en el inventario.
  3. Si ocurre un fallo en cualquiera de los pasos, deshacer todos los cambios.

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private InventoryService inventoryService;

    @Transactional
    public void processOrder(OrderRequest orderRequest) {
        // Paso 1: Crear nuevo pedido
        Order order = new Order();
        order.setProductId(orderRequest.getProductId());
        order.setQuantity(orderRequest.getQuantity());
        order.setStatus("PROCESSING");
        orderRepository.save(order);

        // Paso 2: Reducir stock en el inventario
        inventoryService.reduceStock(orderRequest.getProductId(), orderRequest.getQuantity());

        // Paso 3: Actualizar el estado del pedido a "FINALIZADO"
        order.setStatus("COMPLETED");
        orderRepository.save(order);
    }
}

¿Qué pasa aquí?

  • Marcamos el método processOrder() como transaccional con la anotación @Transactional. Eso significa que todas las operaciones dentro del método se ejecutan en una sola transacción.
  • Si en cualquier paso ocurre un error (por ejemplo, el producto se ha acabado), los cambios se revertirán automáticamente y la base de datos quedará en un estado consistente.

Ejemplo de error y rollback

Vamos a añadir un error "artificial" para comprobar que la transacción realmente se revierte.


public void reduceStock(String productId, int quantity) {
    int currentStock = inventoryRepository.getStock(productId);
    if (currentStock < quantity) {
        throw new RuntimeException("Insufficient stock for product: " + productId);
    }
    inventoryRepository.updateStock(productId, currentStock - quantity);
}

Si no hay suficiente stock, el método reduceStock lanzará una excepción y toda la transacción será cancelada: el pedido no se guardará y el stock no cambiará.


Aplicación real

En proyectos reales las transacciones se usan prácticamente en todas partes:

  • Operaciones bancarias (transferencias, ingresos, retiradas de cuenta).
  • Pedidos y reservas (compra de entradas, hoteles).
  • Gestión de datos en sistemas empresariales (CRM, ERP).

¿Por qué esto es importante para nosotros? Si en una entrevista te preguntan cómo procesar una operación bancaria o cómo proteger los datos de conflictos, tú, como desarrollador Java con experiencia en Spring, podrás no solo explicar las transacciones, sino también mostrar código con @Transactional.

Ahora estamos listos para pasar al siguiente paso: gestionar transacciones en Spring con la anotación @Transactional y otras herramientas.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION