05 Configuración de la aplicación Spring AOP
1 、 Spring JdbcTemplate
Para operar JDBC de manera más conveniente en primavera, se define una capa de abstracción sobre la base de JDBC. El propósito de este diseño es proporcionar métodos de plantilla para diferentes tipos de operaciones JDBC. Cada método de plantilla puede controlar todo el proceso y permitir la entrada De esta manera, se puede mantener la flexibilidad tanto como sea posible para tareas específicas en el proceso de cobertura y se puede minimizar la carga de trabajo del acceso a la base de datos.
1. Configure y pruebe la fuente de datos
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mashibing</groupId>
<artifactId>spring_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
</project>
dbconfig.properties
jdbc.username=root123
password=123456
url=jdbc:mysql://localhost:3306/demo
driverClassName=com.mysql.jdbc.Driver
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
</beans>
MyTest.java
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class);
System.out.println(dataSource);
System.out.println(dataSource.getConnection());
}
}
2. Agregue JdbcTemplate al contenedor de primavera
El contenedor de primavera proporciona una clase JdbcTemplate para facilitar el funcionamiento de la base de datos.
1. Agregar dependencia de pom
pom.xml
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
jdbcTemplate.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
</beans>
MyTest.java
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
System.out.println(jdbcTemplate);
}
}
3. Insertar datos
MyTest.java
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into emp(empno,ename) values(?,?)";
int result = jdbcTemplate.update(sql, 1111, "zhangsan");
System.out.println(result);
}
}
4. Insertar datos en lotes
MyTest.java
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into emp(empno,ename) values(?,?)";
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[]{1,"zhangsan1"});
list.add(new Object[]{2,"zhangsan2"});
list.add(new Object[]{3,"zhangsan3"});
int[] result = jdbcTemplate.batchUpdate(sql, list);
for (int i : result) {
System.out.println(i);
}
}
}
5. Consultar un valor y devolverlo como objeto.
MyTest.java
import com.mashibing.bean.Emp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select * from emp where empno = ?";
Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class), 7369);
System.out.println(emp);
}
}
6. La consulta devuelve el objeto de colección.
MyTest.java
import com.mashibing.bean.Emp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
import java.util.List;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select * from emp where sal > ?";
List<Emp> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class), 1500);
for (Emp emp : query) {
System.out.println(emp);
}
}
}
7, devuelve el valor de la función combinada
MyTest.java
import com.mashibing.bean.Emp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
import java.util.List;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select max(sal) from emp";
Double aDouble = jdbcTemplate.queryForObject(sql, Double.class);
System.out.println(aDouble);
}
}
8. Use JdbcTemplate con funciones nombradas
jdbcTemplate.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
</beans>
MyTest.java
import com.mashibing.bean.Emp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
NamedParameterJdbcTemplate jdbcTemplate = context.getBean("namedParameterJdbcTemplate", NamedParameterJdbcTemplate.class);
String sql = "insert into emp(empno,ename) values(:empno,:ename)";
Map<String,Object> map = new HashMap<>();
map.put("empno",2222);
map.put("ename","sili");
int update = jdbcTemplate.update(sql, map);
System.out.println(update);
}
}
9. Integrar EmpDao
jdbcTemplate.xml
<context:component-scan base-package="com.mashibing"></context:component-scan>
EmpDao.java
package com.mashibing.dao;
import com.mashibing.bean.Emp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
public class EmpDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void save(Emp emp){
String sql = "insert into emp(empno,ename) values(?,?)";
int update = jdbcTemplate.update(sql, emp.getEmpno(), emp.getEname());
System.out.println(update);
}
}
MyTest.java
import com.mashibing.bean.Emp;
import com.mashibing.dao.EmpDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
EmpDao empDao = context.getBean("empDao", EmpDao.class);
empDao.save(new Emp(3333,"wangwu"));
}
}
2. Asuntos declarativos
1. Preparación ambiental
tx.sql
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50528
Source Host : localhost:3306
Source Database : tx
Target Server Type : MYSQL
Target Server Version : 50528
File Encoding : 65001
Date: 2020-02-13 19:19:32
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `account`
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`username` varchar(10) NOT NULL DEFAULT '',
`balance` double DEFAULT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO account VALUES ('lisi', '1000');
INSERT INTO account VALUES ('zhangsan', '1000');
-- ----------------------------
-- Table structure for `book`
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`id` int(10) NOT NULL,
`book_name` varchar(10) DEFAULT NULL,
`price` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of book
-- ----------------------------
INSERT INTO book VALUES ('1', '西游记', '100');
INSERT INTO book VALUES ('2', '水浒传', '100');
INSERT INTO book VALUES ('3', '三国演义', '100');
INSERT INTO book VALUES ('4', '红楼梦', '100');
-- ----------------------------
-- Table structure for `book_stock`
-- ----------------------------
DROP TABLE IF EXISTS `book_stock`;
CREATE TABLE `book_stock` (
`id` int(255) NOT NULL DEFAULT '0',
`stock` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of book_stock
-- ----------------------------
INSERT INTO book_stock VALUES ('1', '1000');
INSERT INTO book_stock VALUES ('2', '1000');
INSERT INTO book_stock VALUES ('3', '1000');
INSERT INTO book_stock VALUES ('4', '1000');
BookDao.java
package com.mashibing.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 减去某个用户的余额
* @param userName
* @param price
*/
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
/**
* 按照图书的id来获取图书的价格
* @param id
* @return
*/
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
/**
* 减库存,减去某本书的库存
* @param id
*/
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
}
}
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
}
MyTest.java
import com.mashibing.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
BookService bookService = context.getBean("bookService", BookService.class);
bookService.checkout("zhangsan","1");
}
}
Resumen: En términos de control de transacciones, hay dos categorías principales:
Transacción programática: agregue directamente la lógica de procesamiento de transacciones al código, es posible que deba llamar explícitamente a beginTransaction (), commit (), rollback () y otros métodos relacionados con la gestión de transacciones en el código
Transacción declarativa: agregue anotaciones fuera del método o defínalo directamente en el archivo de configuración, separe el código de administración de transacciones del método comercial e implemente la administración de transacciones de manera declarativa. Spring's AOP puede simplemente completar esta función: el modo fijo de código de gestión de transacciones se utiliza como un enfoque transversal, a través del método AOP de modularización, y luego lograr transacciones declarativas.
2. Configuración simple de transacciones declarativas
Spring abstrae un conjunto de mecanismos de gestión de transacciones de diferentes API de gestión de transacciones, lo que permite que los códigos de gestión de transacciones sean independientes de tecnologías de transacciones específicas. Los desarrolladores llevan a cabo la gestión de transacciones a través de la configuración sin tener que comprender cómo se implementa la capa subyacente.
La abstracción principal de la gestión de transacciones de Spring es PlatformTransactionManager. Encapsula un conjunto de métodos independientes de la tecnología para la gestión de transacciones. Independientemente de la estrategia de gestión de transacciones de Spring (programática o declarativa), el administrador de transacciones es imprescindible.
El administrador de transacciones se puede declarar en el contenedor Spring IOC en forma de un bean ordinario. La imagen a continuación es el administrador de transacciones proporcionado por Spring
1. Agregue un administrador de transacciones al archivo de configuración
jdbcTemplate.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<context:component-scan base-package="com.mashibing"></context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--事务控制-->
<!--配置事务管理器的bean-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启基于注解的事务控制模式,依赖tx名称空间-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
}
3. Atributos de la configuración de transacciones
Aislamiento: establezca el nivel de aislamiento de la transacción
Propagación: el comportamiento de propagación de la transacción
NoRollbackFor: esas transacciones anormales no se pueden revertir
NoRollbackForClassName: complete el parámetro es el nombre completo de la clase
RollbackFor: qué transacciones anormales deben revertirse
RollbackForClassName: el parámetro completado es el nombre completo de la clase
ReadOnly: establece si la transacción es de solo lectura
Tiempo de espera: la transacción se termina automáticamente y se revierte después del tiempo de ejecución especificado, en segundos
4. Probar propiedades de tiempo de espera
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(timeout = 3)
public void checkout(String username,int id){
bookDao.updateStock(id);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
}
5. Establecer transacción de solo lectura
Si ejecuta una sola instrucción de consulta a la vez, no es necesario habilitar el soporte de transacciones La base de datos admite la coherencia de lectura durante la ejecución de SQL de forma predeterminada;
Si ejecuta varias sentencias de consulta a la vez, como consulta estadística, consulta de informe, en este escenario, SQL de consulta múltiple debe garantizar la coherencia de lectura general; de lo contrario, después de la consulta SQL anterior, antes de la consulta SQL siguiente, los datos Si cambiada por otros usuarios, la consulta estadística general parecerá inconsistente en la lectura de datos. En este momento, el soporte de transacciones debe estar habilitado.
Para consultas de solo lectura, puede especificar el tipo de transacción como de solo lectura, es decir, transacción de solo lectura. Dado que no hay modificación de datos para transacciones de solo lectura, la base de datos proporcionará algunos métodos de optimización para transacciones de solo lectura
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(timeout = 3,readOnly = true)
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
}
6. Establecer qué excepciones no se revierten
Nota: Las excepciones en tiempo de ejecución se revierten de forma predeterminada y las excepciones en tiempo de compilación no se revierten de forma predeterminada.
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(timeout = 3,noRollbackFor = {ArithmeticException.class,NullPointerException.class})
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
int i = 1/0;
}
@Transactional(timeout = 3,noRollbackForClassName = {"java.lang.ArithmeticException"})
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
int i = 1/0;
}
}
7. Establezca las excepciones que desee revertir
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(timeout = 3,rollbackFor = {FileNotFoundException.class})
public void checkout(String username,int id) throws FileNotFoundException {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
// int i = 1/0;
new FileInputStream("aaa.txt");
}
}
8. Establecer el nivel de aislamiento
Cuatro tipos:
- Aislamiento SERIALIZABLE
- REPEATABLE_READ
- READ_COMMITTED
- READ_UNCOMMITTED
- DEFECTO
Los estudiantes que no están en contacto con el nivel de aislamiento pueden ver mi video de negocios anterior, que tiene una explicación detallada, por lo que no lo repetiré aquí.
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(timeout = 3,isolation = Isolation.READ_COMMITTED)
public void checkout(String username,int id) throws FileNotFoundException {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
// int i = 1/0;
new FileInputStream("aaa.txt");
}
}
9. Las características de propagación de las transacciones.
La característica de propagación de la transacción se refiere a cuando un método de transacción es llamado por otro método de transacción, ¿cómo debe proceder este método de transacción?
Hay 7 tipos de comportamientos de propagación de transacciones de primavera:
10. Pruebe las características de propagación de la transacción.
BookDao.java
package com.mashibing.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 减去某个用户的余额
* @param userName
* @param price
*/
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
/**
* 按照图书的id来获取图书的价格
* @param id
* @return
*/
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
/**
* 减库存,减去某本书的库存
* @param id
*/
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
}
/**
* 修改图书价格
* @param id
* @param price
*/
public void updatePrice(int id,int price){
String sql = "update book set price=? where id =?";
jdbcTemplate.update(sql,price,id);
}
}
BookService.java
package com.mashibing.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
int i = 1/0;
}
}
MulService.java
package com.mashibing.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional
public void mulTx(){
bookService.checkout("zhangsan",1);
bookService.updatePrice(1,1000);
}
}
MyTest.java
import com.mashibing.service.MulService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
MulService mulService = context.getBean("mulService", MulService.class);
mulService.mulTx();
}
}
A partir del resultado de la figura anterior, se encuentra que si el conjunto de características de propagación es Requerido, entonces todas las transacciones se unificarán en una transacción. Una vez que ocurre un error, todos los datos deben ser revertidos.
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
int i = 1/0;
}
}
Al modificar la característica de propagación del método de pago a Required_new, se encuentra que el precio se ha revertido, pero los otros datos no se han revertido.
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
}
}
MulService.java
package com.mashibing.service;
import com.mashibing.bean.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional
public void mulTx(){
bookService.checkout("zhangsan",1);
bookService.updatePrice(1,1000);
int i = 1/0;
}
}
Establezca el comportamiento de propagación del método bookservice en Requerido y establezca el informe de errores en MulService, y se revertirá si se encuentra.
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
}
}
MulService.java
package com.mashibing.service;
import com.mashibing.bean.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional
public void mulTx(){
bookService.checkout("zhangsan",1);
bookService.updatePrice(1,1000);
int i = 1/0;
}
}
Establezca el comportamiento de propagación del método bookservice en Require_new y establezca el informe de errores en MulService, y no se revertirá.
BookService.java
package com.mashibing.service;
import com.mashibing.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
}
@Transactional
public void mulTx(){
checkout("zhangsan",1);
updatePrice(1,1000);
int i = 1/0;
}
}
Si lo ejecuta en el servicio de libros, encontrará que el efecto acaba de desaparecer. La razón es el AOP utilizado en la llamada externa, pero la llamada de este método de clase en sí es la llamada más común, que es la misma transacción .
para resumir:
Preste atención al problema: la diferencia entre REQUERIDO y ANIDADO:
Parece que las dos funciones son iguales, pero REQUIRED se enviará junto con la tarea principal y fallarán juntas. Y NESTED es que si la transacción actual es anormal, realizará la operación de reversión, pero la tarea principal no realizará la operación de reversión, pero si la tarea principal es anormal, la tarea secundaria realizará la operación de reversión. (Este conocimiento en el video es incorrecto, porque estas dos transacciones propagan anomalías en la tarea principal, lo que afectará a la tarea secundaria para realizar la operación de reversión)
1. El nivel de propagación de la transacción es REQUERIDO. Cuando se llama a checkout () (suponiendo que lo llame commit () en otra clase), si el código en checkout () arroja una excepción, incluso si se detecta, otros códigos en la confirmación () Retrocederá
2. Es REQUIRES_NEW. Si el código en checkout () arroja una excepción y es capturado, otro código en commit () no se revertirá; si otro código en commit () arroja una excepción y no es capturado, no causará checkout () para revertir
3. Está ANIDADO. Si el código en checkout () arroja una excepción y es capturado, otro código en commit () no se revertirá; si otro código en commit () arroja una excepción y no es capturado, causará checkout () Retroceder
PROPAGATION_REQUIRES_NEW inicia una nueva transacción "interna" que no depende del entorno. Esta transacción se confirmará por completo o se revertirá sin depender de transacciones externas, tiene su propio alcance de aislamiento, sus propios bloqueos, etc. Cuándo comienza la transacción interna Cuándo ejecutada, la transacción externa se suspenderá y, cuando finalice la transacción interna, la transacción externa continuará ejecutándose.
Por otro lado, PROPAGATION_NESTED inicia una transacción "anidada", que es una subtransacción real de una transacción existente. Anidado transacción Cuando comience la ejecución, obtendrá un punto de guardado. Si esta transacción anidada falla, retrocederemos a este punto de guardado. La transacción latente es parte de la transacción externa y solo se confirmará después de que finalice la transacción externa.
Puede Tenga en cuenta que PROPAGATION_REQUIRES_NEW La mayor diferencia con PROPAGATION_NESTED es que PROPAGATION_REQUIRES_NEW es una transacción completamente nueva, mientras que PROPAGATION_NESTED es una subtransacción de una transacción externa. Si la transacción externa se confirma, la transacción anidada también se confirmará. Esta regla también se aplica a roll espalda.
3. Configuración de transacciones basada en XML
jdbcTemplate.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<context:component-scan base-package="com.mashibing"></context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--事务控制-->
<!--配置事务管理器的bean-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
基于xml配置的事务:依赖tx名称空间和aop名称空间
1、spring中提供事务管理器(切面),配置这个事务管理器
2、配置出事务方法
3、告诉spring哪些方法是事务方法(事务切面按照我们的切入点表达式去切入事务方法)
-->
<bean id="bookService" class="com.mashibing.service.BookService"></bean>
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* com.mashibing.service.*.*(..))"/>
<!--事务建议:advice-ref:指向事务管理器的配置-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
</aop:config>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--事务属性-->
<tx:attributes>
<!--指明哪些方法是事务方法-->
<tx:method name="*"/>
<tx:method name="checkout" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
</beans>