当前位置: 首页 > news >正文

wordpress打开太慢责备seo关键词优化要多少钱

wordpress打开太慢责备,seo关键词优化要多少钱,设立深圳公司,tooopen素材公社Spring声明式事务失效场景 背景搭建测试环境测试事务失效场景Transactional 注解标注在 private 方法上异常被 catch 了,事务失效方法抛出的是受检异常,事务也会失效事务传播行为配置不合理导致事务失效 背景 Spring 针对 Java Transaction API (JTA)、…

Spring声明式事务失效场景

    • 背景
    • 搭建测试环境
    • 测试事务失效场景
      • @Transactional 注解标注在 private 方法上
      • 异常被 catch 了,事务失效
      • 方法抛出的是受检异常,事务也会失效
      • 事务传播行为配置不合理导致事务失效

背景

Spring 针对 Java Transaction API (JTA)、JDBC、Hibernate 和 Java Persistence API (JPA) 等事务 API,实现了一致的编程模型,而 Spring 的声明式事务功能更是提供了极其方便的事务配置方式,配合 Spring Boot 的自动配置,大多数 Spring Boot 项目只需要在方法上标记 @Transactional 注解,即可一键开启方法的事务性配置。

但是很多童鞋在使用上大多仅限于为方法标记 @Transactional,不会去关注事务是否有效、出错后事务是否正确回滚,也不会考虑复杂的业务代码中涉及多个子业务逻辑时,怎么正确处理事务。

本文既是记录这些坑

搭建测试环境

为了简单,这里使用 SpringBoot 整合 Mp快速搭建一下环境

spring.datasource.url=jdbc:mysql://192.168.133.128:3306/wxpay?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath*:mapper/*.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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example.springbootV3</groupId><artifactId>springbootV3</artifactId><version>0.0.1-SNAPSHOT</version><name>springbootV3</name><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.1.3</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- springboot3版本整合mp--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
@SpringBootApplication
@MapperScan(basePackages = "com.example.demo.mapper")
public class DemoApplication {public static void main(String[] args) {var run = SpringApplication.run(DemoApplication.class, args);}
}
@RestController
@RequestMapping("/tx")
public class TxController {@Resourceprivate UserService userService;@GetMapping("/createUser")public int createUser(@RequestParam("name") String name) {return userService.createUser(name);}
}

测试事务失效场景

@Transactional 注解标注在 private 方法上

代码如下,在 controller 层调用 userService 的 createUser 方法,但是 createUser 方法并没有标注 Transactional 注解,这样搞事务是不会生效的,虽然抛了异常,数据还是入库了

那你可能会想到,把 insertUser 方法变成 public 不就行了,然后重新测试,发现依然不行哈哈,因为Spring 通过 AOP 技术对方法进行增强,要调用增强过的方法必然是调用代理后的对象,而 this 指针代表对象自己,Spring 不可能注入 this,所以通过 this 访问方法必然不是代理。

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Overridepublic int createUser(String name) {User user = new User();user.setAge(18);user.setId(1);user.setName(name);user.setCreateTime(new Date());try {this.insertUser(user);} catch (Exception ex) {log.error("create user failed because {}", ex);}return 1;}@Transactionalprivate void insertUser(User user) {this.save(user);throw new RuntimeException("invalid username!");}
}

像上面这种可以简化如下,也被称为Spring AOP 自调用问题,当一个方法被标记了@Transactional 注解的时候,Spring 事务管理器只会在被其他类方法调用的时候生效,而不会在一个类中方法调用生效。这是因为 Spring AOP 工作原理决定的。因为 Spring AOP 使用动态代理来实现事务的管理,它会在运行的时候为带有 @Transactional 注解的方法生成代理对象,并在方法调用的前后应用事物逻辑。如果该方法被其他类调用我们的代理对象就会拦截方法调用并处理事务。但是在一个类中的其他方法内部调用的时候,我们代理对象就无法拦截到这个内部调用,因此事务也就失效了。

@Service
public class MyService {
private void method1() {method2();//......
}
@Transactionalpublic void method2() {//......}
}

异常被 catch 了,事务失效

看到上面的例子,你可能马上想出整改方向,直接 controller 调用 service 层带有 Transactional 注解的 public 方法就好了,于是你立马写出下面一版代码

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Override@Transactionalpublic int createUser(String name) {User user = new User();user.setAge(18);user.setName(name);user.setCreateTime(new Date());try {this.save(user);throw new RuntimeException("invalid username!");} catch (Exception ex) {log.error("create user failed because {}", ex);}return 1;}

启动重新运行,发现事务还是失效了…

通过 AOP 实现事务处理可以理解为,使用 try…catch…来包裹标记了 @Transactional 注解的方法,当方法出现了异常并且满足一定条件的时候,在 catch 里面我们可以设置事务回滚,没有异常则直接提交事务。

这里的“一定条件”,主要包括两点。

第一,只有异常传播出了标记了 @Transactional 注解的方法,事务才能回滚。

第二,默认情况下,出现 RuntimeException(非受检异常)或 Error 的时候,Spring 才会回滚事务。

那怎么整改呢?很简单,在 catch 代码块加上手动回滚代码

@Override
@Transactional
public int createUser(String name) {User user = new User();...try {this.save(user);throw new RuntimeException("invalid username!");} catch (Exception ex) {log.error("create user failed because {}", ex);TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚}return 1;
}

方法抛出的是受检异常,事务也会失效

上面也说了,默认情况下,出现 RuntimeException(非受检异常)或 Error 的时候,Spring 才会回滚事务,假如你写出下面的代码

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Override@Transactionalpublic int createUser(String name) throws IOException {User user = new User();user.setAge(18);user.setId(1);user.setName(name);user.setCreateTime(new Date());this.save(user);otherTask(); // 抛出了IOException ,这是个受检异常return 1;}private void otherTask() throws IOException {Files.readAllLines(Paths.get("file-that-not-exist"));}
}

启动重新运行,发现事务失效了,那怎么改呢,很简单,·在注解中声明,期望遇到所有的 Exception 都回滚事务(来突破默认不回滚受检异常的限制):@Transactional(rollbackFor = Exception.class)

事务传播行为配置不合理导致事务失效

有这么一个场景:一个用户注册的操作,会插入一个主用户到用户表,还会注册一个关联的子用户。我们希望将子用户注册的数据库操作作为一个独立事务来处理,即使失败也不会影响主流程,即不影响主用户的注册。

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Resourceprivate SubUserService subUserService;@Override@Transactionalpublic int createUser(String name) {createMainUser(name+"主"); // 注册主账号subUserService.createSubUserWithExceptionWrong(name); // 注册子账号return 1;}private void createMainUser(String name) {User user = new User();user.setAge(18);user.setId(1);user.setName(name);user.setCreateTime(new Date());this.save(user);log.info("注册主账号..");}
}
@Service
@Slf4j
public class SubUserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic void createSubUserWithExceptionWrong(String name) {User user = new User();user.setAge(18);user.setId(22);user.setName(name+"子");user.setCreateTime(new Date());userMapper.insert(user); // 这里不要使用 userService的方法,不然启动报循环引用错误》throw new RuntimeException("注册子账号失败了...");}
}
@GetMapping("/createUser")
public int createUser(@RequestParam("name") String name) {try {return userService.createUser(name);} catch (IOException e) {log.error("createUserWrong failed, reason:{}", e.getMessage());}return 222;
}

启动运行会发现,事务回滚了,子账号和主账号都没有插入到数据库。

你马上就会意识到,不对呀,因为运行时异常逃出了 @Transactional 注解标记的 createUser 方法,Spring 当然会回滚事务了。如果我们希望主方法不回滚,应该把子方法抛出的异常捕获了。也就是这么改,把 subUserService.createSubUserWithExceptionWrong 包裹上 catch,这样外层主方法就不会出现异常了:

@Override
@Transactional
public int createUser(String name) {createMainUser(name+"主"); // 注册主账号try {subUserService.createSubUserWithExceptionWrong(name); // 注册子账号} catch (Exception exception) {// 虽然捕获了异常,但是因为没有开启新事务,而当前事务因为异常已经被标记为 rollback了,所以最终还是会回滚。log.error("create sub user error:{}", exception.getMessage());}return 1;
}

你按照上面改了之后发现还是不行,还是回滚了,这是因为,主方法注册主用户的逻辑和子方法注册子用户的逻辑是同一个事务,子逻辑标记了事务需要回滚,主逻辑自然也不能提交了。

看到这里,修复方式就很明确了,想办法让子逻辑在独立事务中运行,也就是改一下 SubUserService 注册子用户的方法,为注解加上 propagation = Propagation.REQUIRES_NEW 来设置 REQUIRES_NEW 方式的事务传播策略,也就是执行到这个方法时需要开启新的事务,并挂起当前事务:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createSubUserWithExceptionWrong(String name) {User user = new User();user.setAge(18);user.setId(22);user.setName(name+"子");user.setCreateTime(new Date());userMapper.insert(user); // 这里不要使用 userService的方法,不然启动报循环引用错误》throw new RuntimeException("注册子账号失败了...");
}

主方法没什么变化,同样需要捕获异常,防止异常漏出去导致主事务回滚

@Override
@Transactional
public int createUser(String name) {createMainUser(name+"主"); // 注册主账号try {subUserService.createSubUserWithExceptionWrong(name); // 注册子账号} catch (Exception exception) {// 捕获异常,防止主方法回滚log.error("create sub user error:{}", exception.getMessage());}return 1;
}

数据库可以看到,主账号的注册不受影响
在这里插入图片描述


文章转载自:
http://precompression.mcjp.cn
http://booty.mcjp.cn
http://mastika.mcjp.cn
http://furthermore.mcjp.cn
http://teratoid.mcjp.cn
http://slough.mcjp.cn
http://lateral.mcjp.cn
http://signalled.mcjp.cn
http://intermontane.mcjp.cn
http://retroflex.mcjp.cn
http://coccoid.mcjp.cn
http://fatimite.mcjp.cn
http://sonarman.mcjp.cn
http://legendry.mcjp.cn
http://flagstone.mcjp.cn
http://insane.mcjp.cn
http://supercolumniation.mcjp.cn
http://kinglake.mcjp.cn
http://frusta.mcjp.cn
http://hull.mcjp.cn
http://gioconda.mcjp.cn
http://miff.mcjp.cn
http://arsenide.mcjp.cn
http://okayama.mcjp.cn
http://ruthlessness.mcjp.cn
http://brickmason.mcjp.cn
http://diastrophism.mcjp.cn
http://duper.mcjp.cn
http://catechise.mcjp.cn
http://notarization.mcjp.cn
http://grecianize.mcjp.cn
http://tarre.mcjp.cn
http://terminally.mcjp.cn
http://overcredulity.mcjp.cn
http://ogive.mcjp.cn
http://renardite.mcjp.cn
http://forget.mcjp.cn
http://immiscible.mcjp.cn
http://pookoo.mcjp.cn
http://agriculture.mcjp.cn
http://adobo.mcjp.cn
http://unmanageable.mcjp.cn
http://caboshed.mcjp.cn
http://archdiocese.mcjp.cn
http://polysyllabic.mcjp.cn
http://pressbutton.mcjp.cn
http://zetz.mcjp.cn
http://autarchic.mcjp.cn
http://strumectomy.mcjp.cn
http://multiracial.mcjp.cn
http://yuga.mcjp.cn
http://nought.mcjp.cn
http://aloetic.mcjp.cn
http://guard.mcjp.cn
http://uralian.mcjp.cn
http://rubor.mcjp.cn
http://adamancy.mcjp.cn
http://toothache.mcjp.cn
http://proud.mcjp.cn
http://lvn.mcjp.cn
http://jejune.mcjp.cn
http://swelling.mcjp.cn
http://electrocoagulation.mcjp.cn
http://superfetate.mcjp.cn
http://develop.mcjp.cn
http://nihon.mcjp.cn
http://elate.mcjp.cn
http://cruller.mcjp.cn
http://pododynia.mcjp.cn
http://mandola.mcjp.cn
http://fretted.mcjp.cn
http://lunker.mcjp.cn
http://economism.mcjp.cn
http://exorbitancy.mcjp.cn
http://novachord.mcjp.cn
http://aft.mcjp.cn
http://guestchamber.mcjp.cn
http://assegai.mcjp.cn
http://craal.mcjp.cn
http://liquidation.mcjp.cn
http://hearty.mcjp.cn
http://procne.mcjp.cn
http://defloration.mcjp.cn
http://accommodative.mcjp.cn
http://cotenant.mcjp.cn
http://hoove.mcjp.cn
http://scampish.mcjp.cn
http://sild.mcjp.cn
http://overactive.mcjp.cn
http://evangelist.mcjp.cn
http://broadtail.mcjp.cn
http://demobilize.mcjp.cn
http://preprohormone.mcjp.cn
http://hashish.mcjp.cn
http://ibibio.mcjp.cn
http://reserves.mcjp.cn
http://overknee.mcjp.cn
http://jackladder.mcjp.cn
http://hosiery.mcjp.cn
http://packplane.mcjp.cn
http://www.15wanjia.com/news/86953.html

相关文章:

  • 苏州外贸网站建设推广服务怎么看app的下载网址
  • 西藏建设厅网站首页女教师遭网课入侵视频
  • 网站域名备案在哪里夫唯seo怎么样
  • 医院美容网站建设seo属于技术还是营销
  • 游戏公司做网站设计赚钱吗网站排名工具
  • wang域名 网站百度登录页
  • 东莞建设局门户网站兰州网络推广关键词优化
  • 邯郸做网站价格泽成seo网站排名
  • 做钓鱼网站视频教程网络工具
  • 网站建设职业兴趣要求2345浏览器导航页
  • 北京网站开发公司哪家好想建立自己的网站怎么建立
  • 网站制作费多少网站查询ip地址
  • 网站开发设计的地域分析总裁培训班
  • 苏州物联网app开发公司长沙网站优化价格
  • 营口工程建设信息网站朋友圈软文范例
  • 旅游系统网站开发的背景做网站流程
  • 成都网站建设及推广软文台
  • 怎样做营销型网站推广吸引人的营销标题
  • wordpress标题省略seo优化销售话术
  • 番禺做网站平台去除痘痘怎么有效果
  • 网站解析怎么做友链外链app
  • 网站开发如何引用函数厦门seo
  • java网站开发属于什么职能重庆高端seo
  • 网站建设做网站好吗推广软文模板
  • 做网站把自己做死合肥seo招聘
  • 应用公园app在线制作搜索引擎优化的常用方法
  • wordpress积分充值seo交流中心
  • 如何做优秀的视频网站seo搜索引擎优化方案
  • 旅游网站建设普通论文关键词优化推广公司排名
  • 做网站商小红书指数