Commit 34ee0a7c by Antonio Rueda

Modificados atributos de transacción. Bloqueo pesimista en operaciones

en las cuentas.
parent c0779679
......@@ -49,7 +49,14 @@
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
......
......@@ -11,7 +11,6 @@ import es.ujaen.dae.ujacoin.util.ExprReg;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
......@@ -19,7 +18,7 @@ import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.PositiveOrZero;
......@@ -49,6 +48,9 @@ public class Cuenta {
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="cuenta_num")
List<Movimiento> movimientos;
// @Version
// int version;
public Cuenta() {
}
......
......@@ -11,6 +11,7 @@ import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
......@@ -18,11 +19,12 @@ import org.springframework.transaction.annotation.Transactional;
* @author ajrueda
*/
@Repository
@Transactional
@Transactional(propagation = Propagation.REQUIRED)
public class RepositorioClientes {
@PersistenceContext
EntityManager em;
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Optional<Cliente> buscar(String dni) {
return Optional.ofNullable(em.find(Cliente.class, dni));
}
......
......@@ -6,10 +6,13 @@
package es.ujaen.dae.ujacoin.repositorios;
import es.ujaen.dae.ujacoin.entidades.Cuenta;
import es.ujaen.dae.ujacoin.entidades.movimientos.Reintegro;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
......@@ -17,16 +20,22 @@ import org.springframework.transaction.annotation.Transactional;
* @author ajrueda
*/
@Repository
@Transactional
@Transactional(propagation = Propagation.REQUIRED)
public class RepositorioCuentas {
@PersistenceContext
EntityManager em;
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Optional<Cuenta> buscar(String num) {
return Optional.ofNullable(em.find(Cuenta.class, num));
}
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Optional<Cuenta> buscarYBloquear(String num) {
return Optional.ofNullable(em.find(Cuenta.class, num, LockModeType.PESSIMISTIC_WRITE));
}
public void guardar(Cuenta cuenta) {
em.persist(cuenta);
}
}
}
......@@ -126,13 +126,14 @@ public class ServicioUjaCoin {
* @param dni el DNI del cliente
* @param tarjeta la tarjeta a registrar
*/
@Transactional
// @Transactional
public void registrarTarjeta(@NotBlank String dni, @NotNull @Valid Tarjeta tarjeta) {
// Cliente cliente = Optional.ofNullable(clientes.get(dni)).orElseThrow(ClienteNoRegistrado::new);
Cliente cliente = repositorioClientes.buscar(dni).orElseThrow(ClienteNoRegistrado::new);
cliente.verTarjeta(tarjeta.getNum()).ifPresent(x -> { throw new TarjetaYaRegistrada(); } );
cliente.nuevaTarjeta(tarjeta);
repositorioClientes.actualizar(cliente);
}
/**
......@@ -176,7 +177,7 @@ public class ServicioUjaCoin {
// Cuenta cuenta = Optional.ofNullable(cuentas.get(numCuenta))
// .orElseThrow(CuentaNoRegistrada::new);
Cuenta cuenta = repositorioCuentas.buscar(numCuenta)
Cuenta cuenta = repositorioCuentas.buscarYBloquear(numCuenta)
.orElseThrow(CuentaNoRegistrada::new);
Tarjeta tarjeta = cuenta.getTitular().verTarjeta(numTarjeta)
......@@ -196,8 +197,8 @@ public class ServicioUjaCoin {
//Cuenta cuenta = Optional.ofNullable(cuentas.get(numCuenta))
// .orElseThrow(CuentaNoRegistrada::new);
Cuenta cuenta = repositorioCuentas.buscar(numCuenta)
.orElseThrow(CuentaNoRegistrada::new);
Cuenta cuenta = repositorioCuentas.buscarYBloquear(numCuenta)
.orElseThrow(CuentaNoRegistrada::new);
Tarjeta tarjeta = cuenta.getTitular().verTarjeta(numTarjeta)
.orElseThrow(TarjetaNoRegistrada::new);
......@@ -216,13 +217,13 @@ public class ServicioUjaCoin {
// Cuenta cuentaOrigen = Optional.ofNullable(cuentas.get(numCuentaOrigen))
// .orElseThrow(CuentaNoRegistrada::new);
Cuenta cuentaOrigen = repositorioCuentas.buscar(numCuentaOrigen)
Cuenta cuentaOrigen = repositorioCuentas.buscarYBloquear(numCuentaOrigen)
.orElseThrow(CuentaNoRegistrada::new);
// Cuenta cuentaDestino = Optional.ofNullable(cuentas.get(numCuentaDestino))
// .orElseThrow(CuentaNoRegistrada::new);
Cuenta cuentaDestino = repositorioCuentas.buscar(numCuentaDestino)
Cuenta cuentaDestino = repositorioCuentas.buscarYBloquear(numCuentaDestino)
.orElseThrow(CuentaNoRegistrada::new);
cuentaOrigen.nuevoMovimiento(new TransferenciaEmitida(cuentaDestino, importe));
......
......@@ -12,16 +12,22 @@ import es.ujaen.dae.ujacoin.entidades.movimientos.Ingreso;
import es.ujaen.dae.ujacoin.entidades.movimientos.Movimiento;
import es.ujaen.dae.ujacoin.entidades.movimientos.TransferenciaEmitida;
import es.ujaen.dae.ujacoin.entidades.movimientos.TransferenciaRecibida;
import es.ujaen.dae.ujacoin.excepciones.SaldoInsuficienteParaOperacion;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.validation.ConstraintViolationException;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
/**
*
......@@ -211,6 +217,83 @@ public class ServicioUjaCoinTest {
Assertions.assertThat(cuentaDestino.getSaldo()).isEqualTo(500);
}
@Test
public void testReintegroDoble() {
// Registrar cliente
Cliente cliente = new Cliente(
"11995667D",
"Juan España España",
LocalDate.of(1990, 11, 1),
"Cl La Luz, 13 - Jaén",
"988674533",
"jee@gmail.com",
"claveyyy");
Cuenta cuenta = servicioUjaCoin.altaCliente(cliente);
// Añadir una tarjeta
Tarjeta tarjeta = new Tarjeta("4111111111111111", cliente.getNombre(), LocalDate.of(2022, 12, 1), "365");
servicioUjaCoin.registrarTarjeta(cliente.getDni(), tarjeta);
cliente.nuevaTarjeta(tarjeta);
// Obtener cuenta y realizar ingreso en cuenta
servicioUjaCoin.ingreso(cuenta.getNum(), tarjeta.getNum(), 1000);
Assertions.assertThatThrownBy(() -> {
servicioUjaCoin.reintegro(cuenta.getNum(), tarjeta.getNum(), 1000);
servicioUjaCoin.reintegro(cuenta.getNum(), tarjeta.getNum(), 1000);
}).isInstanceOfAny(SaldoInsuficienteParaOperacion.class);
}
@Test
public void testReintegroDobleParalelo() {
// Registrar cliente
Cliente cliente = new Cliente(
"11995667D",
"Juan España España",
LocalDate.of(1990, 11, 1),
"Cl La Luz, 13 - Jaén",
"988674533",
"jee@gmail.com",
"claveyyy");
Cuenta cuenta = servicioUjaCoin.altaCliente(cliente);
// Añadir una tarjeta
Tarjeta tarjeta = new Tarjeta("4111111111111111", cliente.getNombre(), LocalDate.of(2022, 12, 1), "365");
servicioUjaCoin.registrarTarjeta(cliente.getDni(), tarjeta);
cliente.nuevaTarjeta(tarjeta);
// Obtener cuenta y realizar ingreso en cuenta
servicioUjaCoin.ingreso(cuenta.getNum(), tarjeta.getNum(), 1000);
Assertions.assertThatThrownBy(() -> {
// Lanzar un reintegro en un thread secundario
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> result = executor.submit(
() -> {
servicioUjaCoin.reintegro(cuenta.getNum(), tarjeta.getNum(), 1000);
}
);
executor.shutdown();
// Con una pausa siempre funciona :-)
// Thread.sleep(1000);
// Ejecutar otro reintegro en el thread principal
servicioUjaCoin.reintegro(cuenta.getNum(), tarjeta.getNum(), 1000);
// Esperar finalización del reintegro en el thread secundario
// Y obtener excepciones si las hay
try {
result.get();
}
catch(ExecutionException e) {
throw e.getCause();
}
}).isInstanceOfAny(SaldoInsuficienteParaOperacion.class);
}
@BeforeEach
void limpiarBaseDatos() {
limpiadorBaseDatos.limpiar();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment