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

全国十大装饰公司排行网站优化公司哪家效果好

全国十大装饰公司排行,网站优化公司哪家效果好,呼叫中心系统解决方案,云南城市建设官方网站前言 尽管 Transactional 注解在 Spring 中提供了方便的事务管理功能,我们在使用过程中却常常面临其失效的问题。事务失效可能导致意想不到的数据状态和错误,影响应用的稳定性和可靠性。本文将探讨一些常见的 Transactional 失效场景,包括异常…

前言


尽管 @Transactional 注解在 Spring 中提供了方便的事务管理功能,我们在使用过程中却常常面临其失效的问题。事务失效可能导致意想不到的数据状态和错误,影响应用的稳定性和可靠性。本文将探讨一些常见的 @Transactional 失效场景,包括异常处理不当、事务传播行为设置错误,以及在非 Spring 管理的类中使用注解等情况。通过深入分析这些场景,我们将揭示如何避免这些常见陷阱,以确保事务管理的有效性。


一、@Transactional失效场景

1、代理角度

1.1同类内部方法间调用

  • 非@Tranactional注解方法调用@Transactional注解方法
@Transactional(rollbackFor = Exception.class)
public void createUser(UserInfo user) throws UnknownHostException {userDao.save(user);
}public void createUser2(UserInfo user) throws UnknownHostException {createUser(user);
}

createUser2 方法直接调用 createUser 方法时,Spring 的 AOP 机制无法拦截这个方法调用。createUser上的@Tranactional失效。

IDE也会给出相应警告:

image-20241104141055525

  • @Tranactional注解方法调用@Transactional注解方法
@Transactional(rollbackFor = Exception.class)
public void createUser(UserInfo user) throws UnknownHostException {userDao.save(user);
}@Transactional(rollbackFor = Exception.class)
public void createUser2(UserInfo user) throws UnknownHostException {createUser(user);
}

createUser2 方法直接调用 createUser 方法时,Spring 的 AOP 机制无法拦截这个方法调用。createUser上的@Tranactional失效。

createUser中DML以非事务形式执行。

1.2如果@Transactional标注到接口上

注解标注到接口上,如果使用CGLIB对目标类进行代理,将无法解析到@Transactional

  • 定义接口和实现类
public interface UserService {void createUser(UserInfo user);
}public class UserServiceImpl implements UserService {@Override@Transactional(rollbackFor = Exception.class)  // 注解在接口实现上public void createUser(UserInfo user) {// DML 操作System.out.println("User created: " + user.getUserId());}
}
  • 配置Spring和使用CGLIB代理
@Configuration
@EnableTransactionManagement
public class AppConfig {@Beanpublic UserService userService() {return new UserServiceImpl();  // CGLIB 代理}@Beanpublic DataSource dataSource() {// 配置数据源}
}
  • 测试类
public class TransactionTest {@Autowiredprivate UserService userService;public void testCreateUser() {UserInfo user = new UserInfo();user.setUserId(1);userService.createUser(user);}
}

在这个案例中,UserService 接口的 createUser 方法被 @Transactional 注解标记,而 UserServiceImpl 实现类也是正确实现了这个接口。

由于 Spring 在这里使用 CGLIB 代理(当代理目标类没有实现接口时,默认使用 CGLIB),它将创建一个 UserServiceImpl 的代理对象。

当通过接口调用 userService.createUser(user) 时,代理对象并不会解析接口上的 @Transactional 注解,导致事务管理失效。

1.3final、static修饰的类或者方法上使用注解

1.3.1 final 修饰的类
  • 无法被代理:如果一个类被声明为 final,Spring 无法对其进行子类化,因此无法生成代理。由于 Spring 的事务管理依赖于代理机制,@Transactional 注解将不会生效,相关的事务管理功能将被忽略。
1.3.2static 修饰的方法
  • 无法代理static 方法属于类本身,而不是类的实例,因此 Spring 事务管理也无法对其进行代理。这意味着,即使在 static 方法上使用 @Transactional,事务管理也不会生效。

1.4没有被Spring管理的bean

  • Spring 的事务管理依赖于代理机制来处理方法调用。当你在一个类上使用 @Transactional 注解时,Spring 会创建一个代理对象,并在调用被注解的方法时应用事务逻辑。
  • 如果一个 bean 没有被 Spring 管理(例如,它不是 Spring 上下文中的一个 bean),Spring 就不会为它创建代理,因此 @Transactional 注解将不起作用。

2、框架及底层角度

2.1非public修饰的方法

/*** 创建代理时,会把方法和事务属性放到缓存对象attributeCache中* 计算事务属性时,会判断方式是否public修饰*/// AbstractFallbackTransactionAttributeSource
private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// 不允许非public修饰的方法if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}
}

2.2多线程

@Service
public class UserService {private final UserDao userDao;private final UserLoginService userLoginService;public UserService(UserDao userDao, UserLoginService userLoginService) {this.userDao = userDao;this.userLoginService = userLoginService;}@Transactional(rollbackFor = Exception.class)public void createUser(UserInfo user) {userDao.save(user);UserLoginInfo userLoginInfo = new UserLoginInfo();userLoginInfo.setId(user.getUserId());userLoginInfo.setUserId(user.getUserId());userLoginService.saveUserLoginInfo(userLoginInfo);}
}
@Service
public class UserLoginService {private final UserLoginDao userLoginDao;public UserLoginService(UserLoginDao userLoginDao) {this.userLoginDao = userLoginDao;}@Transactional(rollbackFor = Exception.class)@Asyncpublic void saveUserLoginInfo(UserLoginInfo userLoginInfo) {userLoginDao.save(userLoginInfo);}
}

理论上createUser和saveUserLoginInfo都有@Transactional注解时,两个方法中的DML处于同一个事务。

当在saveUserLoginInfo上加上@Async时,saveUserLoginInfo会被代理,新开一个线程执行。

由于事务时绑定在线程上的,这就意味着saveUserLoginInfo会新建一个事务。

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, String joinpointIdentification,@Nullable TransactionStatus status) {TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);if (txAttr != null) {if (logger.isTraceEnabled()) {logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");}txInfo.newTransactionStatus(status);} else {if (logger.isTraceEnabled()) {logger.trace("No need to create transaction for [" + joinpointIdentification +"]: This method is not transactional.");}}// 即使没有创建新的事务,也总是绑定事务信息到线程上txInfo.bindToThread();return txInfo;
}

2.3数据库本身不支持事务

MySQL如果使用MyISAM存储引擎,则不支持事务,自然@Transactional也没有效果

2.4未开启事务

在某些MVC项目中需要在配置文件中,手动配置事务相关参数

3、使用角度

3.1rollbackFor 属性设置错误而导致 @Transactional 注解失效的情况

  1. 定义用户服务类
@Service
public class UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate UserLoginService userLoginService;@Transactional(rollbackFor = NullPointerException.class) // 错误设置public void createUser(UserInfo user) {userDao.save(user); // 可能会抛出 Exception// 假设下面这行会抛出一个 RuntimeExceptionUserLoginInfo userLoginInfo = null; // 这里故意设置为 nulluserLoginInfo.setUserId(user.getUserId()); // 这里会抛出 NullPointerExceptionuserLoginService.saveUserLoginInfo(userLoginInfo);}
}
  1. 测试类
public class TransactionTest {@Autowiredprivate UserService userService;public void testCreateUser() {UserInfo user = new UserInfo();user.setUserId(1);userService.createUser(user);}
}
  • 如果 userDao.save(user) 方法抛出一个非 NullPointerException 类型的异常(例如 SQLException),则由于 rollbackFor 属性不包含该异常类型,事务不会回滚。
  • 即使后续代码中发生了 NullPointerException,由于前面的操作已经导致事务处于失败状态,整个事务也不会被正确处理。

3.2异常被内部 catch 块捕获而导致 @Transactional 注解失效的情况

  1. 定义用户服务类
@Service
public class UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate UserLoginService userLoginService;@Transactional(rollbackFor = Exception.class)public void createUser(UserInfo user) {try {userDao.save(user); // 假设这可能抛出 SQLException} catch (SQLException e) {// 捕获异常,事务不会回滚System.out.println("Caught SQLException: " + e.getMessage());// 不重新抛出异常,导致事务管理失效}// 下面的代码仍然会被执行UserLoginInfo userLoginInfo = new UserLoginInfo();userLoginInfo.setUserId(user.getUserId());userLoginService.saveUserLoginInfo(userLoginInfo);}
}
  1. 测试类
public class TransactionTest {@Autowiredprivate UserService userService;public void testCreateUser() {UserInfo user = new UserInfo();user.setUserId(1);userService.createUser(user);}
}
  • userDao.save(user) 抛出 SQLException 时,该异常被 catch 块捕获并处理。

  • 由于异常没有被重新抛出,@Transactional 注解无法检测到异常的发生,因此事务不会回滚。

  • 这意味着即使 userDao.save(user) 失败,后续的 userLoginService.saveUserLoginInfo(userLoginInfo) 仍然会被执行,导致数据的不一致。

3.3事务传播行为不当而导致 @Transactional 注解失效的情况

  1. 定义用户服务类
@Service
public class UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate UserLoginService userLoginService;@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)public void createUser(UserInfo user) {userDao.save(user); // 可能抛出异常// 调用另一个方法,假设这个方法没有@Transactional注解userLoginService.saveUserLoginInfo(user.getUserId());}
}
  1. 用户登录服务类
@Service
public class UserLoginService {@Autowiredprivate UserLoginDao userLoginDao;public void saveUserLoginInfo(Long userId) {// 假设这个方法执行一些 DML 操作// 但这里没有 @Transactional 注解userLoginDao.saveLoginInfo(userId);}
}
  1. 测试类
public class TransactionTest {@Autowiredprivate UserService userService;public void testCreateUser() {UserInfo user = new UserInfo();user.setUserId(1);userService.createUser(user);}
}

createUser 方法的传播行为被设置为 Propagation.REQUIRES_NEW

  • createUser 方法被调用时,会启动一个新的事务。
  • 在该新事务中,如果 userDao.save(user) 抛出异常,整个事务将回滚。
  • 然而,userLoginService.saveUserLoginInfo(user.getUserId()) 方法没有被 @Transactional 注解包裹,且它是在 createUser 方法的同一事务中调用的。
关键问题:
  • 因为 saveUserLoginInfo 方法没有事务管理,所以即使 createUser 方法中的事务因为异常回滚,saveUserLoginInfo 方法中的操作仍然会被提交。
  • 这导致了数据的不一致性,因为在 createUser 方法失败时,saveUserLoginInfo 方法的 DML 操作仍然会被执行。
http://www.hkea.cn/news/473483/

相关文章:

  • 伊犁网站建设评价怎样才能上百度
  • 房地产网站建设方案百度实名认证
  • 做外贸可以在哪些网站注册网络项目免费的资源网
  • 中国建设银行信用卡网站首页青岛关键词优化平台
  • 阿里云网站建设考试题目长沙网站推广服务公司
  • 甘肃建设项目审批权限网站俄罗斯搜索引擎yandex官网入口
  • 网站建设公司新员工培训ppt模板百度热门搜索排行榜
  • 仿魔客吧网站模板网址大全是ie浏览器吗
  • 网站产品后台界面怎么做湖南关键词排名推广
  • 网站数据每隔几秒切换怎么做的湖南百度seo排名点击软件
  • 网站制作先学什么百度新闻下载安装
  • 河南省网站建设哪家好免费观看行情软件网站进入
  • 粘合剂东莞网站建设体育热点新闻
  • 百度网站排名关键词整站优化培训网站建设
  • 网络平台代理seo外包 杭州
  • 东方头条网站源码免费推广软件工具
  • 北京网站建设公司分享网站改版注意事项流程优化四个方法
  • 案例学 网页设计与网站建设手机百度seo快速排名
  • 江门网站建设总部电话产品推广渠道有哪些
  • 网站建设全攻略站长之家ping检测
  • 导航网站 cmsgoogle chrome谷歌浏览器
  • wordpress看其他人博客优化师是做什么的
  • 现在哪个网站还做白拿2021小说排行榜百度风云榜
  • 网站流量seo提升seo排名的方法
  • 做html网站模板下载地址网站页面布局和样式设计
  • 公司网站邮箱费用磁力宅在线搜种子
  • wordpress 缺少临时文件夹刷关键词优化排名
  • 做网站要有什么团队淘宝关键词排名查询工具
  • 开源门户网站源码宁波谷歌seo
  • wordpress+一页一屏seo关键技术有哪些