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

一起做网站吧网店运营具体做什么

一起做网站吧,网店运营具体做什么,做斗图网站,网站设置受信任使用springboot时,只要引入spring-jdbc/jpa相关的依赖后,在想要启用事务的方法上加上Transactional注解就能开启事务,碰到异常就能自动回滚。大大的提高了编码的便捷性性,同时也不侵入代码,保持了代码的简洁性。 默认情…

使用springboot时,只要引入spring-jdbc/jpa相关的依赖后,在想要启用事务的方法上加上@Transactional注解就能开启事务,碰到异常就能自动回滚。大大的提高了编码的便捷性性,同时也不侵入代码,保持了代码的简洁性。

默认情况下,Spring时使用的Spring AOP (mode=Mode.Proxy, proxyTargetClass=false)方式启动数据库事务拦截。只有了解清楚了具体背景,才能清除知道事务为什么在碰到异常时没有能够正确回滚。下面是一些常用场景分析:

场景1、未正确配置TransactionManager

使用springboot开发时,引入以下依赖后通常会自动启用TransactionManager。

  • spring-boot-starter-jdbc 是 Spring Boot 提供的用于简化 JDBC(Java Database Connectivity)开发的启动器,引入该依赖后,Spring Boot 会自动配置 DataSourceTransactionManager

  • spring-boot-starter-data-jpa 是 Spring Boot 提供的用于简化 JPA(Java Persistence API)开发的启动器,它集成了 Hibernate 等 JPA 实现框架,方便开发者进行数据库操作。引入该依赖后,Spring Boot 会自动配置 JpaTransactionManager

通过下面代码中的printTransactionManager(TransactionManager transactionManager) 方法可以检查是否配置正常。

@SpringBootApplication
public class MybatisApplication {public static void main(String[] args) {org.springframework.boot.SpringApplication.run(MybatisApplication.class, args);}@BeanObject printTransactionManager(TransactionManager transactionManager) {System.out.println("transactionManager: " + transactionManager);return null;}
}

transactionManager: org.springframework.jdbc.support.JdbcTransactionManager@590765c4 

通过打印语句,可以看到spring中的TransactionManager是否正确配置。

场景2、@Transaction注解不在public方法上

默认情况下,事务是在proxy模式下(即Spring AOP负责拦截事务),proxyTargetClass=false ,有接口的时候使用JDK动态代理实现。没有接口时使用CGLIB进行代理。

JDK代理接口时,都是public方法。

CGLIB代理时,在public方法上能生效。在Spring 6.0 以后,除public方法外,可以代理protected, package-visable修饰的方法。

当@Transactional注解位于private/final修饰的方法上时,事务碰到异常不能正常回滚。

详情参考文档:

Method visibility and @Transactional in proxy mode

The @Transactional annotation is typically used on methods with public visibility. As of 6.0, protected or package-visible methods can also be made transactional for class-based proxies by default. Note that transactional methods in interface-based proxies must always be public and defined in the proxied interface. For both kinds of proxies, only external method calls coming in through the proxy are intercepted.

Using @Transactional :: Spring Framework

示例代码:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalprivate void createUser(User user) {// 插入用户数据userRepository.save(user);// 可能会抛出异常if (user.getName().equals("error")) {throw new RuntimeException("创建用户失败");}}
}

因为方法的可见性,private修饰的方法不能被代理拦截

场景3、调用内部方法

@Transactional 注解使用 AOP 实现事务管理,而 AOP 是基于代理模式的。当在同一个类内部一个没有事务注解的方法调用有 @Transactional 注解的方法时,事务注解会失效,因为这种调用没有经过代理对象。

示例代码如下:

// 定义 UserService 类,处理用户相关业务逻辑
@Service
class UserService {@Autowiredprivate UserRepository userRepository;// 无事务注解的方法,内部调用有事务注解的方法public void outerMethod() {try {innerMethod();} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());}}// 有事务注解的方法@Transactionalpublic void innerMethod() {User user = new User("John");userRepository.save(user);// 模拟抛出异常throw new RuntimeException("Simulated exception");}
}

示例代码中,虽然Spring AOP代理了innerMethod方法,但是原始事务不是通过代理的innerMethod进入,而是通过原始类的outerMethod进入,这样就调用的是原始类的innerMethod方法,导致不能进入代理类的innerMethod方法,事务拦截不能生效。

场景4、方法内部Catch了异常

在 Spring 中使用 @Transactional 注解时,如果在方法内部捕获了异常且没有重新抛出,会导致事务无法正常回滚,从而使 @Transactional 注解失效。

@Transactional 注解的事务管理是基于 AOP 实现的,它会在目标方法抛出异常时进行事务回滚。默认情况下,@Transactional 注解只对未被捕获的 RuntimeException 及其子类异常进行回滚操作。如果在方法内部捕获了异常,Spring 就无法感知到异常的抛出,从而不会触发事务回滚逻辑,导致事务继续提交,@Transactional 注解的功能失效。

示例代码

// 定义 UserService 类,处理用户相关业务逻辑
@Service
class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser() {User user = new User("John");userRepository.save(user);try {// 模拟抛出异常throw new RuntimeException("Simulated exception");} catch (Exception e) {// 捕获异常但未重新抛出System.out.println("Exception caught: " + e.getMessage());}}
}

createUser() 方法:使用 @Transactional 注解标记,在方法内部保存用户信息后抛出一个 RuntimeException 异常,并在 catch 块中捕获该异常,但没有重新抛出。

解决办法:重新抛出异常

在 catch 块中重新抛出异常,让 Spring 能够感知到异常的发生,从而触发事务回滚逻辑。

@Transactional
public void createUser() {User user = new User("John");userRepository.save(user);try {throw new RuntimeException("Simulated exception");} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());// 重新抛出异常throw e;}
}

场景5、 rollbackFor/rollbackForClassName属性未正确配置

  • 默认回滚规则:@Transactional注解默认只对RuntimeException及其子类和Error进行回滚。若抛出的是受检查异常(如IOExceptionSQLException),默认不会触发回滚。

@Service
class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser() throws IOException {User user = new User("John");userRepository.save(user);// 抛出受检查异常throw new IOException("Simulated IOException");}
}

IOException是一个CheckedException的子类,不在默认的回滚体系内,所以不能自动回滚。需要使用rollbackFor属性显式指定才能生效。

  • rollbackFor=BaseException.class, 针对BaseException和它的子类回滚。若抛出的异常不在继承体系内,则不能自动回滚。
    @Service
    public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackFor = BaseException.class)public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will rollback// throw new SubException("出现未知错误");// will not rollbackthrow new OtherException("出现未知错误");}
    }class BaseException extends Exception {public BaseException(String message) {super(message);}
    }class SubException extends BaseException {public SubException(String message) {super(message);}
    }class OtherException extends Exception {public OtherException(String message) {super(message);}
    }

    rollbackForClassName=exceptionPattern, exceptionPattern可以包含异常名字全部或者部分字符。注意这里不是正则表达式,而是基于String.contains(exceptionPattern)来判断的。

    匹配规则的源码如下:

private int getDepth(Class<?> exceptionType, int depth) {if (this.exceptionType != null) {if (this.exceptionType.equals(exceptionType)) {// Found it!return depth;}}else if (exceptionType.getName().contains(this.exceptionPattern)) {// Found it!return depth;}// If we've gone as far as we can go and haven't found it...if (exceptionType == Throwable.class) {return -1;}return getDepth(exceptionType.getSuperclass(), depth + 1);}

不能匹配的示例代码如下:

@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackForClassName = "Base*Exception")public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will not rollbackthrow new Base1Exception("出现未知错误");}
}

 因为exception.typeName="Base1Exception" contains("Base*Exception") 结果未false,所以不匹配,导致不能回滚。

正确的用法如下:

@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackForClassName = "Base")public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will rollbackthrow new Base1Exception("出现未知错误");}
}

以上是我过去经常碰到的@Transactional碰到异常不能正常回滚的案例总结,若有遗漏欢迎下方留言。

参考文档:

Declarative Transaction Management :: Spring Framework

http://www.dtcms.com/wzjs/186378.html

相关文章:

  • 上海企炬做的网站拉新app推广接单平台
  • 游戏开发与网站开发就业情况站长工具seo综合查询收费吗
  • 昆明建企业网站多少钱广告免费发布信息平台
  • 网站广告条动画 怎么做广州软文推广公司
  • 建行网站查询密码是什么东西b2b外贸平台
  • 网站开发成本主要有哪些常用搜索引擎有哪些
  • wordpress 增加浏览数快速排名seo
  • 怎么做门户网站列表网推广效果怎么样
  • 用maxcms做的网站谷歌广告投放教程
  • 泸州建设网站深圳全网推广
  • 什么样的公司愿意做网站合肥seo优化外包公司
  • 信息发布网站模板下载今日最新闻
  • 网站开发工程师需要会写什么区别百度问答我要提问
  • 做网站哪些好发帖效果好的网站
  • 怎么做跟别人一样的网站吗百度刷搜索词
  • 做网站必须要注册公司么杭州网站外包
  • html网站前台模板官网制作公司
  • 漂亮的手机网站模板下载简述什么是百度竞价排名
  • 西部数码 空间做2个网站安徽网站关键字优化
  • 免费做爰小说网站华为seo诊断及优化分析
  • 母婴类网站 网站建设方案书 备案互联网营销师培训班
  • 阿亮seo技术恩施seo整站优化哪家好
  • 建设一个网站用什么软件合肥seo推广培训班
  • 南京网站优化工具全网推广怎么做
  • 怎么用FTP做网站百度点击优化
  • 网站风格的设计原则临沂森佳木业有限公司
  • 做网站能赚钱吗知乎网上推广产品怎么做
  • 纯静态网站开发汕头网站设计公司
  • 在那些网站做宣传更好教你免费申请个人网站
  • 在银行网站如何做理财风险评测百度网盘客服中心电话