Este artículo describe la gestión de transacciones en Spring con ejemplos. Compártelo con todos para tu referencia. El análisis específico es el siguiente:
Introducción comercial:
La gestión de transacciones es una tecnología esencial en el desarrollo de aplicaciones empresariales para garantizar la integridad y coherencia de los datos.
Una transacción es una serie de acciones que se tratan como una única unidad de trabajo. O todas estas acciones funcionan o ninguna funciona
Cuatro atributos clave de las transacciones (ACID)
① Atomicidad: una operación atómica en la sala de transacciones consta de una serie de acciones. La atomicidad de las transacciones garantiza que las acciones se completen por completo o no tengan ningún efecto. ② Consistencia: una vez que se completan todas las acciones de la transacción, la transacción se confirma. Los datos y los recursos se encuentran en un estado consistente que cumple con las reglas comerciales ③ Aislamiento: puede haber muchas transacciones procesando los mismos datos al mismo tiempo, por lo que cada cosa debe estar aislada de otras transacciones para evitar la corrupción de datos ④ Durabilidad: una vez que se completa una transacción , sus resultados no deberían verse afectados sin importar los errores del sistema que ocurran. Normalmente, los resultados de una transacción se escriben en un almacenamiento persistente.
Gestión de transacciones en primavera
Como marco de aplicación empresarial, Spring define una capa de abstracción sobre diferentes API de gestión de transacciones. Los desarrolladores de aplicaciones no necesitan comprender la API de gestión de transacciones subyacente para utilizar el mecanismo de gestión de transacciones de Spring.
Spring admite tanto la gestión de transacciones programáticas como la gestión de transacciones declarativas.
Gestión de transacciones programáticas: incruste código de gestión de transacciones en métodos comerciales para controlar el envío y la reversión de transacciones. En las transacciones programáticas, se debe incluir un código de gestión de transacciones adicional en cada operación comercial.
Gestión de transacciones declarativas: en la mayoría de los casos, es mejor utilizarla que la gestión de transacciones programáticas. Separa el código de gestión de transacciones de los métodos comerciales e implementa la gestión de transacciones de forma declarativa. La gestión de transacciones, como preocupación transversal, se puede modularizar mediante métodos AOP. Spring admite la gestión de transacciones declarativas a través del marco Spring AOP.
Propiedades de propagación de las transacciones Spring:
Cuando otro método de transacción llama a un método de transacción, debe especificar cómo se debe propagar la transacción. Por ejemplo: el método puede continuar ejecutándose en la transacción existente o puede iniciar una nueva transacción y ejecutarse en su propia transacción.
El comportamiento de propagación de una transacción puede especificarse mediante el atributo de propagación. Spring define 7 comportamientos de comunicación:
comportamiento de comunicación | significado |
PROPAGACIÓN_MANDATORIA | Indica que el método debe ejecutarse dentro de una transacción. Si la transacción actual no existe, se generará una excepción. |
PROPAGATION_NESTED | Indica que si existe una transacción actualmente, este método se ejecutará en una transacción anidada. Las transacciones anidadas se pueden confirmar o revertir independientemente de la transacción actual. Si la transacción actual no existe, el comportamiento es el mismo que PROPAGATION_REQUIRED. Tenga en cuenta que el soporte de cada fabricante para este comportamiento de propagación es diferente. Puede consultar la documentación de sus administradores de recursos para confirmar si admiten transacciones anidadas. |
PROPAGACIÓN_NUNCA | Indica que el método actual no debe ejecutarse en un contexto de transacción. Si actualmente hay una transacción en ejecución, se generará una excepción. |
PROPAGATION_NOT_SUPPORTED | Indica que este método no debe ejecutarse dentro de una transacción. Si existe una transacción actual, se suspenderá mientras se ejecute este método. Si usa JTATransactionManager, necesita acceder a TransactionManager |
PROPAGACIÓN_REQUIRED | Indica que el método actual debe ejecutarse dentro de una transacción. Si la transacción actual existe, el método se ejecutará en esa transacción. De lo contrario, se iniciará una nueva transacción. |
PROPAGATION_REQUIRED_NEW | Indica que el método actual debe ejecutarse en su propia transacción. Se iniciará una nueva transacción. Si existe una transacción actual, se suspenderá durante la ejecución de este método. Si usa JTATransactionManager, necesita acceder a TransactionManager |
SOPORTES_PROPAGACIÓN | Indica que el método actual no requiere un contexto de transacción, pero si hay una transacción actual, el método se ejecutará en esta transacción. |
Entre ellos PROPAGATION_REQUIRED es el atributo de propagación predeterminado.
Problemas causados por transacciones concurrentes
Pueden surgir muchos problemas inesperados cuando varias transacciones en la misma aplicación o en diferentes aplicaciones se ejecutan simultáneamente en el mismo conjunto de datos.
Los problemas causados por transacciones concurrentes se pueden dividir en las tres categorías siguientes:
① Lectura sucia: la lectura sucia ocurre cuando una transacción lee datos que han sido reescritos por otra transacción pero que aún no se han confirmado. Si la reescritura se revierte posteriormente, los datos obtenidos en la primera transacción no serán válidos.
② Lectura no repetible: la lectura no repetible ocurre cuando una transacción ejecuta la misma consulta dos o más veces, pero obtiene datos diferentes cada vez. Esto suele deberse a que otra transacción simultánea actualizó los datos entre consultas.
③ Lectura fantasma: la lectura fantasma es similar a la lectura no repetible. Ocurre cuando una transacción (T1) lee algunas filas de datos y luego otra transacción simultánea (T2) inserta algunos datos. En consultas posteriores, la primera transacción (T1) encontrará que hay algunos registros más que no existían originalmente.
ejemplo de código
Primero está la tabla de la base de datos:
Incluyendo libro (isbn, nombre_libro, precio), cuenta (nombre de usuario, saldo), stock_libro (isbn, stock)
Luego las clases utilizadas:
LibreríaDao
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
interfaz pública BookShopDao {
//Obtiene el precio unitario del libro según el número de libro
public int findBookPriceByIsbn(String isbn);
//Actualiza el inventario del libro para que el inventario correspondiente al número de libro sea -1
actualización pública vacíaBookStock (String isbn);
//Actualizar el saldo de la cuenta del usuario: hacer el precio del saldo del nombre de usuario
public void updateUserAccount (nombre de usuario de cadena, precio int);
}
LibroTiendaDaoImpl
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
importar org.springframework.beans.factory.annotation.Autowired;
importar org.springframework.jdbc.core.JdbcTemplate;
importar org.springframework.stereotype.Repository;
@Repository("libroTiendaDao")
la clase pública BookShopDaoImpl implementa BookShopDao {
@autocableado
JdbcTemplate privado JdbcTemplate;
@Anular
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECCIONAR precio DEL libro DONDE isbn =?";
devolver JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Anular
actualización pública vacíaBookStock (String isbn) {
//Comprueba si el inventario de libros es suficiente; en caso contrario, lanza una excepción
String sql2 = "SELECCIONAR acciones DE book_stock DONDE isbn =?";
int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
si (acción == 0) {
throw new BookStockException("¡Existencias insuficientes!");
}
String sql = "ACTUALIZAR book_stock SET stock = stock - 1 DONDE isbn =?";
JdbcTemplate.update(sql, isbn);
}
@Anular
public void updateUserAccount (nombre de usuario de cadena, precio int) {
//Comprueba si el saldo es insuficiente; en caso contrario, lanza una excepción
String sql2 = "SELECCIONAR saldo DE la cuenta DONDE nombre de usuario =?";
int balance = JdbcTemplate.queryForObject(sql2, Integer.class, nombre de usuario);
if (saldo <precio) {
throw new UserAccountException("¡Saldo insuficiente!");
}
String sql = "ACTUALIZAR cuenta SET saldo = saldo -? ¿DÓNDE nombre de usuario =?";
JdbcTemplate.update(sql, precio, nombre de usuario);
}
}
ServicioLibrería
Copie el código de la siguiente manera: paquete com.yl.spring.tx;
interfaz pública BookShopService {
compra pública nula (nombre de usuario de cadena, isbn de cadena);
}
LibreríaServicioImpl
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
importar org.springframework.beans.factory.annotation.Autowired;
importar org.springframework.stereotype.Service;
importar org.springframework.transaction.annotation.Isolation;
importar org.springframework.transaction.annotation.Propagation;
importar org.springframework.transaction.annotation.Transactional;
@Service("libroTiendaService")
clase pública BookShopServiceImpl implementa BookShopService {
@autocableado
BookShopDao privado bookShopDao;
/**
* 1. Agregar anotaciones de transacciones
* Utilice la propagación para especificar el comportamiento de propagación de la transacción, es decir, cómo utilizar la transacción cuando otro método de transacción llama al método de transacción actual.
* El valor predeterminado es OBLIGATORIO, lo que significa usar la transacción llamando al método
* REQUIRES_NEW: Utilice su propia transacción y la transacción del método de transacción llamado se suspenderá.
*
* 2. Utilice aislamiento para especificar el nivel de aislamiento de la transacción. El valor más utilizado es READ_COMMITTED.
* 3. De forma predeterminada, la transacción declarativa de Spring revierte todas las excepciones de tiempo de ejecución, que también se pueden configurar mediante las propiedades correspondientes. Normalmente, el valor predeterminado es suficiente.
* 4. Utilice readOnly para especificar si la transacción es de solo lectura. Indica que esta transacción solo lee datos pero no los actualiza, lo que puede ayudar al motor de la base de datos a optimizar la transacción. Si realmente es un método que solo lee la base de datos, se debe establecer readOnly=true.
* 5. Utilice timeOut para especificar el tiempo que puede tardar una transacción antes de forzar una reversión.
*/
@Transactional(propagación=Propagación.REQUIRES_NEW,
aislamiento=Aislamiento.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
solo lectura=verdadero, tiempo de espera=3)
@Anular
compra pública nula (nombre de usuario de cadena, isbn de cadena) {
//1. Obtener el precio unitario del libro.
precio int = bookShopDao.findBookPriceByIsbn(isbn);
//2.Actualizar el inventario de libros
bookShopDao.updateBookStock(isbn);
//3. Actualizar saldo de usuario
bookShopDao.updateUserAccount(nombre de usuario, precio);;
}
}
Excepción de stock de libros
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
La clase pública BookStockException extiende RuntimeException {
/**
*
*/
serialVersionUID largo final estático privado = 1L;
BookStockException pública() {
súper();
// TODO Código auxiliar de constructor generado automáticamente
}
BookStockException pública (cadena arg0, arg1 arrojable, arg2 booleano,
argumento booleano) {
super(arg0, arg1, arg2, arg3);
// TODO Código auxiliar de constructor generado automáticamente
}
BookStockException pública (cadena arg0, arrojable arg1) {
super(arg0,arg1);
// TODO Código auxiliar de constructor generado automáticamente
}
BookStockException pública (cadena arg0) {
super(arg0);
// TODO Código auxiliar de constructor generado automáticamente
}
BookStockException pública (arg0 arrojable) {
super(arg0);
// TODO Código auxiliar de constructor generado automáticamente
}
}
Excepción de cuenta de usuario
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
la clase pública UserAccountException extiende RuntimeException {
/**
*
*/
serialVersionUID largo final estático privado = 1L;
excepción de cuenta de usuario pública() {
súper();
// TODO Código auxiliar de constructor generado automáticamente
}
UserAccountException pública (cadena arg0, arg1 arrojable, arg2 booleano,
argumento booleano) {
super(arg0, arg1, arg2, arg3);
// TODO Código auxiliar de constructor generado automáticamente
}
UserAccountException pública (Cadena arg0, Arg1 arrojable) {
super(arg0,arg1);
// TODO Código auxiliar de constructor generado automáticamente
}
excepción de cuenta de usuario pública (cadena arg0) {
super(arg0);
// TODO Código auxiliar de constructor generado automáticamente
}
UserAccountException pública (arg0 arrojable) {
super(arg0);
// TODO Código auxiliar de constructor generado automáticamente
}
}
Cajero
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
importar java.util.List;
interfaz pública Cajero {
pago nulo público (nombre de usuario de cadena, Lista <String> isbns);
}
CajeroImpl. CashierImpl.checkout y bookShopService.purchase prueban conjuntamente el comportamiento de propagación de transacciones
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
importar java.util.List;
importar org.springframework.beans.factory.annotation.Autowired;
importar org.springframework.stereotype.Service;
importar org.springframework.transaction.annotation.Transactional;
@Servicio("cajero")
clase pública CashierImpl implementa Cashier {
@autocableado
bookShopService privado bookShopService;
@Transaccional
@Anular
pago vacío público (nombre de usuario de cadena, lista <cadena> isbns) {
para (Cadena isbn: isbns) {
bookShopService.purchase(nombre de usuario, isbn);
}
}
}
Clase de prueba:
Copie el código de código de la siguiente manera:
paquete com.yl.spring.tx;
importar java.util.Arrays;
importar org.junit.Test;
importar org.springframework.context.ApplicationContext;
importar org.springframework.context.support.ClassPathXmlApplicationContext;
clase pública SpringTransitionTest {
Contexto de aplicación privado ctx = nulo;
BookShopDao privado bookShopDao = nulo;
bookShopService privado bookShopService = nulo;
cajero privado cajero = nulo;
{
ctx = nuevo ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.clase);
cajero = ctx.getBean(Cajero.clase);
}
@Prueba
prueba pública vacíaBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
@Prueba
prueba de vacío públicoBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
@Prueba
prueba de anulación públicaBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 100);
}
@Prueba
prueba pública vacíaBookShopService(){
bookShopService.purchase("AA", "1001");
}
@Prueba
prueba pública vacíaTransactionPropagation(){
cajero.checkout("AA", Arrays.asList("1001", "1002"));
}
}
Espero que este artículo sea útil para el diseño de programación Spring de todos.