南宁做网站公司品牌云尚网络/aso关键字优化
文章目录
- 1. 定义Spring Bean篇
- 1.1 定义Spring Bean的几种方式
- 1.1.1 XML文件定义Spring Bean
- 1.1.2 JavaConfig定义Spring Bean
- 1.1.3 @Component注解定义SpringBean
- 1.2 装配Spring Bean的四种常用方式
- 1.2.1 手动装配 + XML文件
- 1.2.2 自动装配 + XML文件
- 1.2.3 手动装配 + JavaConfig文件
- 1.2.4 自动装配 + 注解
- 2. Spring Bean注册到容器中
- 2.1 实例化,生成普通对象
- 2.1.1 通过反射机制获取构造器,通过构造器创建普通对象
- 2.1.2 属性注入
- 2.1.3 初始化
- 2.1.4 初始化后,生成代理对象
- 2.1.5 注册销毁
- 2.1.6 将普通对象或者代理对象加入单例池中
- 3. Spring注解开发
- 3.1 使用注解开发前,需要做的配置
- 3.1.1 使用注解开发,必须要保证导入aop包
- 3.1.2 使用注解需要导入context约束、增加注解的支持!
- 3.2 了解常用的@注解
- 3.2.1 @ Component、@Mapper、@Service、@Controller
- 3.2.2 @Bean
- 3.2.3 @ComponentScan
- 3.2.4 @Import
- 3.2.5 @Autowired和@Qualifier
- 3.2.6 @Resource
- 3.2.7 @Value
- 4. Spring事务
- 4.1 本质与原理讲解:`Spring事务是基于AOP实现的,AOP是基于继承原理的。`
- 4.2 事务失效场景
- 4.2.1 访问权限问题private
- 4.2.2 final修饰
- 4.2.3 未被Spring管理
- 4.2.4 表不支持事务
- 4.2.5 未开启事务
- 4.2.6 方法内部调用
- 4.2.7 错误的传播特性
- 4.2.8 自己吞了异常
- 4.2.9 手动抛了别的异常
- 4.2.10 事务嵌套回滚多了
- 4.2.11 大事务
- 4.3 编程式事务
1. 定义Spring Bean篇
1.1 定义Spring Bean的几种方式
1.1.1 XML文件定义Spring Bean
基本类型注册
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student{private String name;private Address address;private String[] books;private List<String> hobbys;private Map<String String> card;private Set<String> games;private String wife;private Properties info;
}
setter方法注入:property的name属性,填写的不是属性的名称,而是set方法去除set,然后将第一个字符小写后的结果
<bean id="student" class="com.kuang.pojo.Student"><!--1. 普通注入--><property name="name" value="狂神说"/><!--2. 数组注入--><property name="books"><array><value>红楼梦</value><value>西游记</value><value>水浒传</value></array></property><!--3. List注入--><property name="hobbys"><list><value>看书</value><value>听歌</value><value>打球</value></list></property><!--4. Map注入--><property name="card"><map><entry key=“身份证” value=“12315454512312”/><entry key=“银行卡” value=“15465415123156”/></map></property><!--5. 数组注入--><property name="games"><set><value>红楼梦</value><value>西游记</value><value>水浒传</value></set></property><!--6. 空值注入--><property name="wife"><null/></property><!--7. Properties注入--><property name="info"><props><prop key="driver">20190525</prop><prop key="url">男/prop><prop key="username">root</prop><prop key="password">123456</prop></props></property>
</bean>
类装配
<!--csBean类有两个属性:title和author--><bean name="cdBean" class="com.my.spring.bean.CDBean"><property name="title" value="The World!!"/><property name="author" value="Mr.D"/></bean> <!--csPlayer类有一个属性:cdBean--><!--对csPlayer的属性csBean进行依赖注入,称为Bean装配,或者依赖关系注入--><bean name="cdPlayer" class="com.my.spring.service.impl.CDPlayerImpl"><property name="cdBean" ref="cdBean"/></bean>
有参构造方法注入
<!--有参构造方法1--><bean id="hello" class="com.kuang.pojo.Hello"><constructor-arg index="0" value="方法1"/></bean>
<!--有参构造方法2--><bean id="hello" class="com.kuang.pojo.Hello"><constructor-arg type="java.lang.String" value="方法2"/></bean>
<!--有参构造方法3--><bean id="hello" class="com.kuang.pojo.Hello"><constructor-arg name="name" value="方法3"/></bean>
扩展注入
<!--p命名空间注入-->
<!--先在beans框架中加入支持:xmlns:p="http://www/springframework.org/schema/p"-->
<!--p命名空间注入,可以直接注入属性的值,property-->
<bean id="user" class="com.kuang.pojo.User" p:name="小李" p:age="10"/><!--c命名空间注入-->
<!--先在beans框架中加入支持:xmlns:c="http://www/springframework.org/schema/c"-->
<!--其次使用c注入的Bean必须存在有参构造器-->
<!--c命名空间注入,通过构造器注入,construct-args-->
<bean id="user" class="com.kuang.pojo.User" c:name="小李" c:age="10"/>
Bean的作用域
- 单例模式(Spring默认机制):从Spring容器中get的每个对象都是同一个对象。
// <bean id="user" class="com.kuang.pojo.User" scope="singleton"/>ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user1 = context.getBean("user");
User user2 = context.getBean("user");
System.out.println(user1==user2); //true
- 原型模式:每次从容器中get的时候,都会产生一个新对象
// <bean id="user" class="com.kuang.pojo.User" scope="prototype"/>ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user1 = context.getBean("user");
User user2 = context.getBean("user");
System.out.println(user1==user2); //false
别名
<!--别名,如果添加了别名,我们可以使用别名获取这个对象-->
<alias name="user" alias="userNew"><!--id:bean的唯一标识符,也就是相当于我们学的对象名class:bean对象所对应的全限定名:包名+类型name:也是别名,而且那么可以同时取多个别名
--><bean id="user" class="com.kuang.pojo.user" name="user1,user2,user3"><property name="name" value="狂神说"/></bean>
Import
如果导入的文件中,bean重名了,那么就会把重名的bean合并成一个,所以不会因为不同的bean.xml存在重名而发生冲突
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
1.1.2 JavaConfig定义Spring Bean
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {@Value("测试名字")public String name;
}
@Configuration
public class JavaConfig {@Beanpublic User getUser() {return new User();} }
1.1.3 @Component注解定义SpringBean
通过@Component、@Repository、@Service、@Controller等注解定义bean,在启动类上加上@ComponentScan注解,在项目启动时@ComponentScan扫描指定路径下的包,包内含有@Component、@Repository、@Service、@Controller等注解的类会被注册成Bean,放到BeanDefinitionMap中,并创建实例到单例池。
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class User {public String name;
}
这里使用了xml配置了componentScan,到了SpringBoot就完全使用注解@ComponentScan
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--指定要扫描的包,这个包下的注解就会生效--><context:component-scan base-package="com.kuang.pojo"/><!--开启注解的支持 --><context:annotation-config/></beans>
1.2 装配Spring Bean的四种常用方式
1.2.1 手动装配 + XML文件
package com.javastudyway.pojo;
public class Cat {public void shout(){System.out.println("我是猫");}
}
package com.javastudyway.pojo;
publicclass Dog {public void shout(){System.out.println("我是狗");}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {/* 人有猫和狗 */private Cat cat;private Dog dog;private String name;
}
<bean id="cat" class="com.javastudyway.pojo.Cat"/><bean id="dog" class="com.javastudyway.pojo.Dog"/><bean id="person" class="com.javastudyway.pojo.Person"><property name="name" value="Java学习之道"/><property name="cat" ref="cat"/><property name="dog" ref="dog"/></bean>
1.2.2 自动装配 + XML文件
<bean id="cat" class="com.javastudyway.pojo.Cat"/><bean id="dog" class="com.javastudyway.pojo.Dog"/><!--byType--><bean id="person" class="com.javastudyway.pojo.Person" autowire="byType"><property name="name" value="Java学习之道"/></bean><!--byName--><bean id="person" class="com.javastudyway.pojo.Person" autowire="byName"><property name="name" value="Java学习之道"/></bean>
1.2.3 手动装配 + JavaConfig文件
@Configuration//标明配置
public class PeopleConfig {@Beanpublic Cat catBean(){return new Cat();}@Beanpublic CDPlayer dogBean(){return new Dog();}@Beanpublic People peopleBean(){return new People(catBean(),dogBean(),"Java学习之道");}
}
1.2.4 自动装配 + 注解
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!-- 开启注解支持 --><context:annotation-config/><bean id="cat" class="com.javastudyway.pojo.Cat"/><bean id="dog" class="com.javastudyway.pojo.Dog"/><bean id="person" class="com.javastudyway.pojo.Person"/></beans>
public class Pepole {@Autowiredprivate Cat cat;@Autowiredprivate Dog dog;private String name;
2. Spring Bean注册到容器中
以UserService讲解Bean注册到容器中的过程。
@mapper
public class UserService{@Autowiredprivate OrderService orderService;public void test(){System.out.println(orderService);}
}
2.1 实例化,生成普通对象
2.1.1 通过反射机制获取构造器,通过构造器创建普通对象
创建Bean过程中,推断所使用的构造器方法
- 如果没有重写构造方法,那么创建Bean对象就会用无参构造方法;
- 如果重写了一个有参构造方法,那么创建Bean对象就会用该有参构造方法;
- 如果重写了两个有参构造方法,那么创建Bean对象不知道用哪个有参构造方法,就会去寻找无参构造方法,如果没有无参构造方法就报错。如果存在无参构造方法就执行无参构造方法。
- 可以通过@Autowired,指定使用该构造方法,即使存在无参构造器,也要使用这个有参构造器
谁向有参构造器public UserService(OrderService orderService)中传入参数?
- 当我们创建Bean对象userService时,需要用到有参构造方法,Spring就会在单例池中找到OrderService对象,并注入到构造方法的参数orderService
如何在单例池中找到自己想要的实例呢?
- 如果通过类型
OrderService
找出beanName唯一,那就直接将Bean对象注入orderService。- 如果通过参数的类OrderService找出beanName不唯一,那就通过参数名orderService找出唯一的beanName,然后将Bean对象注入orderService参数
- 如果这样都找不到,那就报错!
2.1.2 属性注入
2.1.3 初始化
这里通过执行invoke()方法,将普通对象初始化
2.1.4 初始化后,生成代理对象
这里就是我们常说的AOP,如果这里进行了AOP切面编程,那么就会生成代理对象
- 创建普通对象UserService
@Component
public class UserService{@Autowiredprivate OrderService orderService;public void test(){System.out.println(orderService);}}
- 创建代理对象UserServiceProxy,对test()方法进行切面编程
@Aspect
@Component
public class UserServiceProxy{@Before("execution(public void com.zhouyu.service.UserService.test())")public void zhouyuBefore(JoinPoint joinPoint){System.out.println("zhouyuBefore");}
}
现在来思考一个问题,在普通对象userService的属性orderService,有@Autowired进行依赖注入,那么orderService是有值的。但是在初始化后,进行了AOP生成了代理对象userServiceProxy,userServiceProxy的属性orderService并没有注入依赖,是null,这就来问题了。
- 其实第3点的伪代码应该改成如下,这样就解决了null值得问题了,下面说说为什么。
- 试想一下,生成普通对象userService,并为普通对象userService的属性orderService依赖注入,那么为什么生成代理对象userServiceProxy,不为代理对象userServiceProxy的属性orderService进行依赖注入呢?Spring认为是没必要的,就像下面的伪代码一样,除了执行AOP切面编程的代码之外,还是用回普通对象userService属性和方法
- 所以一句话说完:UserServiceProxy代理对象做Bean对象,除了切面编程@Before会执行代理对象,其他的方法和属性可以认为是在执行普通对象
@Aspect
@Component
public class UserServiceProxy{@Autowiredprivate UserService target;@Before("execution(public void com.zhouyu.service.UserService.test())")public void zhouyuBefore(JoinPoint joinPoint){System.out.println("zhouyuBefore");}
}
2.1.5 注册销毁
实现了销毁接口DisposableBean,在registerDisposableBean方法注册指定的Bean在销毁时可以直接执行destroy方法销毁Bean
2.1.6 将普通对象或者代理对象加入单例池中
3. Spring注解开发
3.1 使用注解开发前,需要做的配置
3.1.1 使用注解开发,必须要保证导入aop包
3.1.2 使用注解需要导入context约束、增加注解的支持!
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解的支持 --><context:annotation-config/></beans>
3.2 了解常用的@注解
3.2.1 @ Component、@Mapper、@Service、@Controller
3.2.2 @Bean
用于JavaConfig配置类,充当xml的<bean>
标签
3.2.3 @ComponentScan
- 通过属性basePackages或basePackageClasses,来指定要进行扫描的package
- 如果未指定package,则默认扫描当前@ComponentScan所修饰的类所在的package
3.2.4 @Import
提供了一种显式地从其他地方加载配置类的方式,这里的其他地方一般是指第三方jar
//1. 创建普通Java类public class ConfigA{@Beanpublic A a(){public new A();}}public class A{}//2. 创建一个配置类,直接将刚才创建的ConfigA导入@Configuration@Import(ConfigA.class)public class ConfigB{}//3. 测试并运行public static void main(String[] args){ApplicationContext ctx = new AnnotationCconfigApplicationContext(ConfigB.class);//现在Bean ConfigA和A都在IOC容器中,是可用的ConfigA ca = ctx.getBean(ConfigA.class);A a = ctx.getBean(A.class);System.out.println(ca.getClass.getSimpleName());System.out,println(a.getClass.getSimpleName());}
结果:ConfigA、A
3.2.5 @Autowired和@Qualifier
public class Pepole {private String name;
//1. 通过名字装配@Autowired@Qualifier("books1")private Books books;
//2. 通过类型装配@Autowiredprivate Hobbies hobbies;
}
3.2.6 @Resource
// @Resource指定按type自动装配@Resource(type = Books.class)private Books books;// @Resource指定按name自动装配@Resource(name = books)private Books books;
3.2.7 @Value
- 基于配置文件application.yml
<!--application.yml-->
server:port: 9090tools: car,train,airplane
@Value("${server.port}")private String port;@Value("${server.tools}")private String[] tools
- 基于非配置文件
// 直接将字符串赋值给 str 属性@Value("hello world")private String str;// 注入系统变量@Value("#{systemProperties['os.name']}")private String osName; // 结果:Windows 10// 使用在setter方法上@Value("Windows")public void setName(String name){this.name = name;}
4. Spring事务
4.1 本质与原理讲解:Spring事务是基于AOP实现的,AOP是基于继承原理的。
1. Spring事务是基于AOP实现的
- 现在对UserService的test方法开启MySQL事务
public class UserService{@AutowiredOrderService ordserService;@Transactional //回滚——Spring事务public void test(){jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");throw new NullPointerException();}
}
- @Transaction修饰test()方法对应的伪代码
public class UserServiceProxy extends UserService{UserService target;//不难看出来1.2.3.4.6都是对test做一个切面,由UserServiceProxy代理对象执行;//test方法由普通对象执行public void test(){1. 事务逻辑2. 开启事务3. 事务管理器去创建数据库连接conn并放入了ThreadLocal4. conn.autocommit = false5. target.test(); //UserService普通对象.test() jdbcTemplate conn sql语句6. SQL没问题就conn.commit(),否则就conn.rollback();}
}
- autocommit = true对应的功能
- 如果数据库由jdbcTemplate连接,autocommit默认为true,则执行完SQL语句之后就会自动提交
- 如果数据库由Spring连接,那么Spring就会把autocommit改成false,则执行完SQL语句之后还不会自动提交
- 所以数据库创建必须由Spring来进行,但是如果由Spring创建数据库,那么Spring一启动就要连接数据库,万一我们不用数据库呢?不用就不用呗,哈哈哈哈!
2. AOP是基于继承原理的
public class UserService{@Transactionprivate void test(){System.out.println("测试数据库回滚事务");}
}执行结果:报错
分析
- 事务的实质是AOP,AOP的实质是继承,所以事务的实质是继承
- 父类private修饰的方法,子类无法访问重写
- 所以父类private修饰的方法,无法进行AOP,也无法进行事务
- 那么@Transaction就会报错
4.2 事务失效场景
什么是事务失效呢?其实就是事务不会正常工作,可以发生如下情况
- 事务没有启动
- 事务启动了,但是无法回滚,或者回滚了很多
- …
4.2.1 访问权限问题private
把某些方法或者变量定义为private,对其开启事务时,会导致事务失效。因为事务基于继承,private修饰的父类方法和变量,子类无法继承。
4.2.2 final修饰
如果某个方法用 final 修饰了,那么在它的代理类中,就无法重写该方法,所以无法添加事务功能
4.2.3 未被Spring管理
如果有一天,你匆匆忙忙地开发了一个 Service 类,但忘了加 @Service 注解,比如:
//@Service
public class UserService {@Transactionalpublic void add(UserModel userModel) {saveData(userModel);updateData(userModel);}
}
从上面的例子,我们可以看到 UserService 类没有加@Service注解,那么该类不会交给 spring 管理,所以它的 add 方法也不会生成事务。
4.2.4 表不支持事务
众所周知,在 mysql5 之前,默认的数据库引擎是myisam。myisam 好用,但有个很致命的问题是:不支持事务。如果只是单表操作还好,不会出现太大的问题。但如果需要跨多张表操作,由于其不支持事务,数据极有可能会出现不完整的情况。此外,myisam 还不支持行锁和外键。所以在实际业务场景中,myisam 使用的并不多。在 mysql5 以后,myisam 已经逐渐退出了历史的舞台,取而代之的是 innodb。
4.2.5 未开启事务
如果你使用的是 springboot 项目,那么你很幸运。因为 springboot 通过DataSourceTransactionManagerAutoConfiguration类,已经默默地帮你开启了事务。
你所要做的事情很简单,只需要配置spring.datasource相关参数即可。
但如果你使用的还是传统的 spring 项目,则需要在 applicationContext.xml 文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。
具体配置信息如下:
<!-- 配置事务管理器 -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/></tx:attributes>
</tx:advice>
<!-- 用切点把事务切进去 -->
<aop:config> <aop:pointcut expression="execution(* com.susan.*.*(..))" id="pointcut"/> <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>
4.2.6 方法内部调用
来看两个示例
@Service
public class OrderServiceImpl implements OrderService {public void update(Order order) {updateOrder(order);}@Transactionalpublic void updateOrder(Order order) {// update order}}
@Service
public class OrderServiceImpl implements OrderService {@Transactionalpublic void update(Order order) {updateOrder(order);}@Transactional(propagation = Propagation.REQUIRES_NEW)public void updateOrder(Order order) {// update order}}
上面两个例子的updateOrder(Order order) 都不会生成事务:因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。
解决办法
@Service
public class ServiceA {@Autowiredprivate ServiceB serviceB;@Transactionalpublic void update(Order order) {updateOrder(order);}
}
@Service
public class ServiceB {@Transactional(propagation = Propagation.REQUIRES_NEW)public void updateOrder(Order order) {// update order}
}
4.2.7 错误的传播特性
我们在使用@Transactional注解时,是可以指定propagation参数的:该参数的作用是指定事务的传播特性,spring 目前支持 7 种传播特性:
- REQUIRED 如果当前上下文中存在事务,则加入该事务,如果不存在事务,则创建一个事务,这是
默认
的传播属性值。- SUPPORTS 如果当前上下文中存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
- MANDATORY 当前上下文中必须存在事务,否则抛出异常。
- REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
- NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
- NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
- NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
目前只有这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED。
如果我们在手动设置 propagation 参数的时候,把传播特性设置错了,比如:
@Service
public class UserService {@Transactional(propagation = Propagation.NEVER)public void add(UserModel userModel) {saveData(userModel);updateData(userModel);}
}
我们可以看到 add 方法的事务传播特性定义成了 Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则会抛异常。
4.2.8 自己吞了异常
@Slf4j
@Service
public class UserService {@Transactionalpublic void add(UserModel userModel) {try {saveData(userModel);updateData(userModel);} catch (Exception e) {log.error(e.getMessage(), e);}}
}
这种情况下 spring 事务当然不会回滚,因为开发者自己捕获catch了异常,又没有手动抛出throw,换句话说就是把异常吞掉了。
如果想要 spring 事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则 spring 认为程序是正常的。
4.2.9 手动抛了别的异常
因为 spring 事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),对于普通的 Exception(非运行时异常),它不会回滚。
@Slf4j
@Service
public class UserService {@Transactionalpublic void add(UserModel userModel) throws Exception {try {saveData(userModel);updateData(userModel);} catch (Exception e) {log.error(e.getMessage(), e);throw new Exception(e);}}
}
4.2.10 事务嵌套回滚多了
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);roleService.doOtherThing();}
}@Service
public class RoleService {@Transactional(propagation = Propagation.NESTED)public void doOtherThing() {System.out.println("保存role表数据");}
}
这种情况使用了嵌套的内部事务,原本是希望调用 roleService.doOtherThing 方法时,如果出现了异常,只回滚 doOtherThing 方法里的内容,不回滚 userMapper.insertUser 里的内容,即回滚保存点。但事实是,insertUser 也回滚了。
why?
因为 doOtherThing 方法出现了异常,没有手动捕获,会继续往上抛,到外层 add 方法的代理方法中捕获了异常。所以,这种情况是直接回滚了整个事务,不只回滚单个保存点。
怎么样才能只回滚保存点呢?可以将内部嵌套事务放在 try/catch 中,并且不继续往上抛异常。这样就能保证,如果内部嵌套事务中出现异常,只回滚内部事务,而不影响外部事务。
@Slf4j
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);try {roleService.doOtherThing();} catch (Exception e) {log.error(e.getMessage(), e);}}
}
4.2.11 大事务
在使用 spring 事务时,有个让人非常头疼的问题,就是大事务问题。
通常情况下,我们会在方法上加@Transactional注解,添加事务功能,比如:
@Service
public class UserService {@Autowired private RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {query1();query2();query3();roleService.save(userModel);update(userModel);}
}
但@Transactional注解,如果被加到方法上,有个缺点就是整个方法都包含在事务当中了。
上面的这个例子中,在 UserService 类中,其实只有这两行才需要事务:
roleService.save(userModel);
update(userModel);
现在的这种写法,会导致所有的 query 方法也被包含在同一个事务当中。
如果 query 方法非常多,调用层级很深,而且有部分查询方法比较耗时的话,会造成整个事务非常耗时,而从造成大事务问题。
4.3 编程式事务
上面聊的这些内容都是基于@Transactional注解的,主要说的是它的事务问题,我们把这种事务叫做:声明式事务。
其实,spring 还提供了另外一种创建事务的方式,即通过手动编写代码实现的事务,我们把这种事务叫做:编程式事务。例如:
@Autowiredprivate TransactionTemplate transactionTemplate;...public void save(final User user) {queryData1();queryData2();transactionTemplate.execute((status) => {addData1();updateData2();return Boolean.TRUE;})}
在 spring 中为了支持编程式事务,专门提供了一个类:TransactionTemplate,在它的 execute 方法中,就实现了事务的功能。
相较于@Transactional注解声明式事务,我更建议大家使用基于TransactionTemplate的编程式事务。主要原因如下:
- 避免由于 spring aop 问题导致事务失效的问题。
- 能够更小粒度地控制事务的范围,更直观。
建议在项目中少使用 @Transactional 注解开启事务。但并不是说一定不能用它,如果项目中有些业务逻辑比较简单,而且不经常变动,使用 @Transactional 注解开启事务也无妨,因为它更简单,开发效率更高,但是千万要小心事务失效的问题。
相关文章:

Spring彻头彻尾的讲解,按照Spring框架启动流程,逐步剖析问题,不再是大杂烩!
文章目录1. 定义Spring Bean篇1.1 定义Spring Bean的几种方式1.1.1 XML文件定义Spring Bean1.1.2 JavaConfig定义Spring Bean1.1.3 Component注解定义SpringBean1.2 装配Spring Bean的四种常用方式1.2.1 手动装配 XML文件1.2.2 自动装配 XML文件1.2.3 手动装配 JavaConfig文…...

[2]MyBatis+Spring+SpringMVC+SSM整合一套通关
二、Spring 1、Spring简介 1.1、Spring概述 官网地址:https://spring.io/ Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Jav…...

Javascript的API基本内容(三)
一、事件流 假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父实际工作都是使用事件冒泡为主 二、页面加载事件 加载外部资源(如图片、外联CS…...

【Python入门第十九天】Python 函数
函数是一种仅在调用时运行的代码块。 可以将数据(称为参数)传递到函数中。 函数可以把数据作为结果返回。 创建函数 在 Python 中,使用 def 关键字定义函数: 实例 def my_function():print("Hello from a function&quo…...

web前端性能优化
3.性能检测 当面对具体的项目实践时,该如何快速提升性能体验呢?或者说如何能够准确地定位到性能瓶颈呢?难道要比对着优化知识点清单,一项一项手动排查或完全凭借经验去处理吗?不,我们需要有一整套清晰科学…...

Telnet 基础实验2: SSH 实验
Telnet 基础实验2: SSH 实验 本实验只能使用 eNSP中 AR 系统的路由器做 拓扑图 SSH : Secure Shell 是一个网络安全协议,基本于 TCP 协议 22 端口传输数据,通过对网络数据的加密,使其能够在一个不安全的网络环境中&a…...

Panda Farm:首个部署在 Arbitrum 上的轻量化 GameFi 游戏
在2月16日,Bitget平台宣布 Launchpad 重新启动,并推出了重启后的首个项目 Panda Farm(BBO),该 Launchpad 启动后得到了较高的关注。 Panda Farm 是部署在 Arbitrum 上的 GameFi应用,这可能首先意味着 Bitge…...

Redis实现分布式锁
1、使用背景几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景。这些场景有一个共同特点就是访问量激增,虽然在系统设计时会通过限流、异步、排队等方式优…...

刷题小抄1-2数之和
时间复杂度和空间复杂度 对于一个算法高效性的评估,分为时间复杂度与空间复杂度两种,在算法优化到极致的情况下,只能舍弃时间来换取空间,或者舍弃空间来换取时间,故而两者可以说是互斥关系 时间复杂度衡量的是算法运行的速度,而空间复杂度衡量的是算法运行所需要的额外内存空…...

axicom的测试文档
目录)SQLpython开放性业务题(二选一)完整代码SQL 问题描述 SQL, 请根据前一周各产品的总GMV将其分成五类:GMV Top 20%、20%-40%,40%-60%,60%-80%以及Bottom 20%的产品组,请计算这五…...

基于vue3异步组件、动态组件、vite批量导入实现路由权限动态管理(非addRoute方案)
开发后台管理系统必备的需求:动态菜单权限管理、或者说路由权限动态管理 原理是通过addRoute实现路由权限控制,一般分为两种: 后端生成当前用户相应的路由后由前端(用 Vue Router 提供的API)addRoutes 动态加载路由前…...

带中转hub的卡车无人机车辆路径问题
本文介绍了两类无人机卡车协同配送问题: 第一类是旅行商问题,也即一辆卡车拉着一架无人机服务给定的节点集合第二类是车辆路径问题,这里强制要求了一架卡车只能搭配一架无人机无人机卡车旅行商问题 符号列表: N N N:表示所有节点集合,含起始和终止节点M M M...

前端食堂技术周刊第 72 期:Signals 是前端框架的未来、Chrome Headless、ts-reset、magic-regexp、Bun 新文档
美味值:🌟🌟🌟🌟🌟 口味:草莓番茄 食堂技术周刊仓库地址:https://github.com/Geekhyt/weekly 本期摘要 Signals 是前端框架的未来Chrome Headless 进化成完全体Next.js 13.2Deno…...

mysql中用逗号隔开的字段作查询用(find_in_set的使用)
mysql中用逗号隔开的字段作查询用(find_in_set的使用) 场景说明 在工作中,经常会遇到一对多的关系。想要在mysql中保存这种关系,一般有两种方式,一种是建立一张中间表,这样一条id就会存在多条记录。或者采用第二种方式ÿ…...

Day902.Memory存储引擎 -MySQL实战
Memory存储引擎 Hi,我是阿昌,今天学习记录的是关于Memory存储引擎的内容。 两个 group by 语句都用了 order by null,为什么使用内存临时表得到的语句结果里,0 这个值在最后一行; 而使用磁盘临时表得到的结果里&…...

Linux(Centos)安装RabbitMQ+延时插件+开机自启动
安装目录1:前言1.1 系统环境1.2:安装版本1.3 简介2:安装2.1:安装前准备:2.2:安装Erlang2.3:安装RabbitMQ2.3:延迟依赖插件安装1:前言 1.1 系统环境 操作系统版本&#…...

最近是遇到了CKPT(BLOCKED)
半夜被电话吵醒,数据库不可用了,无法交易。 远程登录查看,这个时候就没有所谓的安全不安全了,都可以远程了。 onstat - 数据库处于CKPT(REQ) CKPT:BLOCKED状态 onstat -l 发现所有的逻辑日志都是U------状态ÿ…...

RabbitMQ死信队列
目录 一、概念 二、出现死信的原因 三、实战 (一)代码架构图 (二)消息被拒 (三)消息TTL过期 (四)队列达到最大长度 一、概念 先从概念解释上搞清楚这个定义,死信&…...

Word控件Spire.Doc 【书签】教程(1):在C#/VB.NET:在 Word 中插入书签
Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下,轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具,专注于创建、编辑、转…...

微服务框架-学习笔记
1 微服务架构介绍 1.1 系统架构演变历史 单体架构垂直应用架构:按照业务线垂直划分分布式架构:抽出业务无关的公共模块SOA架构:面向服务微服务架构:彻底的服务化1.2 微服务架构概览 1.3 微服务架构核心要素 服务治理࿱…...

实验心理学笔记01:引论
原视频链接: https://www.bilibili.com/video/BV1Qt41137Kv 目录 一、实验心理学:定义、内容及简要历史回顾 二、实验心理学和普通心理学、认知心理学的区别 三、实验方法与非实验方法 四、实验范式 五、实验中的各种变量 六、The science of psy…...

预备3-如何学习编程
如何学习编程 我说说曾经学习编程踩得坑 纠结字面上的意思 如纠结一个关键词的名称如何来 为什么叫这个名称... 只是一个简单的名称,该名称代表某一想象/行为,就好比你为啥叫张三, 千万别去深究这些...做笔记的时间比敲代码的时间还多 做笔记的原因是,自己总结归纳所学的知识, …...

操作系统权限提升(十七)之绕过UAC提权-Windows令牌概述和令牌窃取攻击
系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权 操作系统权限提升(十四)之绕过UAC提权-基于白名单AutoElevate绕过UAC提权 操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权 操作系…...

【时间之外】系统管人,能行?(冷眼旁观连载之二)
上次写了在用的工具系统和痛点,基本情况都交待清楚了,春节假期很快就过去了。这次继续按照之前观察计划,谈谈对这些工具使用情况的感受,学而时习之,算是抛砖引玉,也算是个人对这项工作的总结和体会。 目录…...

【数据结构必会基础】关于树,你所必须知道的亿些概念
目录 1.什么是树 1.1浅显的理解树 1.2 数据结构中树的概念 2.树的各种结构概念 2.1 节点的度 2.2 根节点/叶节点/分支节点 2.3 父节点/子节点 2.4祖先节点/子孙节点 2.5兄弟节点 2.6树的度 2.7节点的层次 2.8森林 3. 如何用代码表示一棵树 3.1链式结构 3.1.1 树节…...

设计模式的应用(已在大型项目中使用)
说明:开发语言:在本文中,使用的是C# 一、目录 •1 、单例模式 •2 、简单工厂模式 •3 、代理模式 •4 、观察者模式 •5 、外观模式 •6 、享元模式 •7 、命令模式 •8 、状态模式 •9 、发布订阅模式...

Git的相关用法
1.全局设置自己的git提交用户名和邮箱git config --global user.name 张三 git config --global user.email zsgmail.com即所有的提交都会用这个姓名和邮箱。如果不知道自己配置的是什么,可以查询下git config --global user.name git config --global user.email 或…...

Linux服务:Nginx反向代理与负载均衡
目录 一、Nginx反向代理 1、什么是代理 2、实现反向代理实验 ①实验拓扑 ②实验目的 ③实验过程 二、反向代理负载均衡 1、反向代理负载均衡调度算法 ①轮询算法 ②加权轮询算法 ③最小连接数算法 ④ip、url 哈希算法 ⑤响应时间fair算法 2、实现反向代理负载均…...

数据结构与算法——2.算法概述
这篇文章,我们来讲一下算法的概述,大致理解一下什么是算法。 目录 1.定义 2.生活实例 3.算法目标 4.实际案例 4.1案例一 4.2案例二 5.小结 1.定义 官方解释: 算法是指解题方案的准确而完整的描述,是一系列解决问题的清…...

BPMN2.0是什么,BPMN能解决企业流程管理中哪些问题?
一、前言: 在任何行业和企业中,一定存在着各式各样的流程,请假流程、报销流程、入职流程、离职流程、出差流程、合同审批流程、出入库流程等等…… 无论是管理者、技术人员还是业务人员,每天肯定也在使用各种流程,但…...