1、以下代码存在并发问题,原因是@Transactional开启事务后,执行完createOrder()方法后已经释放锁了,但是事务还没提交,此时另外一个线程获取到锁开始执行createOrder方法导致的
public class ChaoMaiConcurrencyService {
public static final int purchaseProductId = 1;
public static final int purchaseProductNum =1;
@Autowired
private ProductMapper productMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Transactional(rollbackFor = Exception.class)
public synchronized Integer createOrder() throws Exception {
Product product = productMapper.selectByPrimaryKey(purchaseProductId);
if (product == null) {
throw new Exception("购买商品:"+purchaseProductId+"不存在");
Integer currentCount = product.getCount();
if (purchaseProductNum > currentCount) {
throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
productMapper.updateProductCount(purchaseProductNum,product.getId());
Order order = new Order();
order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
order.setOrderStatus(1);
order.setReceiverName("xxx");
orderMapper.insertSelective(order);
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setProductId(product.getId());
orderItem.setPurchasePrice(product.getPrice());
orderItem.setPurchaseNum(purchaseProductNum);
orderItemMapper.insertSelective(orderItem);
return order.getId();
测试类代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ChaoMaiConcurrencyTest {
@Autowired
private ChaoMaiConcurrencyService chaoMaiConcurrencyService;
@Test
public void testChaoMai() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(5);
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i=0;i<5;i++) {
es.execute(()->{
try {
cyclicBarrier.await();
Integer orderId = chaoMaiConcurrencyService.createOrder();
System.out.println("订单id:"+orderId);
} catch (Exception e) {
e.printStackTrace();
} finally {
cdl.countDown();
});
2、解决方案:不使用@Transactional注解,注入PlatformTransactionManager和TransactionDefinition来手动提交事务
代码如下:
public class ChaoMaiConcurrencyService {
public static final int purchaseProductId = 1;
public static final int purchaseProductNum =1;
@Autowired
private ProductMapper productMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
public synchronized Integer createOrder() throws Exception {
TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
Product product = productMapper.selectByPrimaryKey(purchaseProductId);
if (product == null) {
platformTransactionManager.rollback(transaction);
throw new Exception("购买商品:"+purchaseProductId+"不存在");
Integer currentCount = product.getCount();
if (purchaseProductNum > currentCount) {
platformTransactionManager.rollback(transaction);
throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
productMapper.updateProductCount(purchaseProductNum,product.getId());
Order order = new Order();
order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
order.setOrderStatus(1);
order.setReceiverName("xxx");
orderMapper.insertSelective(order);
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setProductId(product.getId());
orderItem.setPurchasePrice(product.getPrice());
orderItem.setPurchaseNum(purchaseProductNum);
orderItemMapper.insertSelective(orderItem);
platformTransactionManager.commit(transaction);
return order.getId();
管理事务:
开启事务: setAutoCommit( boolean autoCommit) : 调用该方法设置参数为false,即开启事务
提交事务: commit()
回滚事务: rollback()
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
Savepoint savepoint = null;
try {
最近项目当中有个场景问题,需要在数据更新完成之后,发送mq消息通知周边系统进行操作。但是周边系统需要用到当前事务更新完成的数据。经常出现mq消息处理过程中拿不到更新之后的数据。经过排查日志,查看sql执行情况,代码逻辑。还跟组内成员反复查看代码执行逻辑。就是没发现问题点在哪里!
然后只能跟踪debug代码执行过程,终于发现在事务执行过程中,没看到commit的时候,mq就把消息发出去了。
然后查看源码,发现在事务提交后,提供了afterCommit的方法,需要我们自己来实现。
解决方案:
1、把mq发消息放
在Java中,事务(Transaction)是指作为单个逻辑工作单元执行的一系列操作。当涉及到数据库操作时,事务确保了数据的完整性和一致性,即使在出现故障的情况下也能保证数据的安全。事务的处理通常在数据库管理系统(DBMS)层面进行,但在Java环境中,事务的管理也可以通过编程框架如Spring或Java Transaction API (JTA)来控制。以上即为SpringBoot的事务开启、配置与使用方式!更多资源也可关注微信公众号获取!
一个程序中不可能没有事务,而 Spring 中,事务的实现方式分为两种:编程式事务和声明式事务,又因为编程式事务实现相对麻烦,而声明式事务实现极其简单,所以在日常项目中,我们都会使用声明式事务 @Transactional 来实现事务。
当我们开启一个事务时,通常需要用commit或rollback结束事务。但是有些操作会自动提交事务,比如DDL语句。这种自动提交事务的操作就是隐式事务。如果存在事务A,那么将事务A挂起,并开启一个新事务。那么当标志有REQUIRES_NEW的内部事务发生异常回滚时,外部事务也会一起回滚。当第3步发生异常时,1、2都不会回滚。3、一个事务没有结束,又开启了新的事务,前一个事务就会被自动提交。2、创建、更新或者删除用户的操作也会导致事务隐式提交。拿我遇到的场景举例:在第3步的方法上添加事务注解。
文章目录1 背景2 问题分析2.1 mysql隔离级别2.2 问题原因分析3 解决问题方案3.1 方式一3.2 方式二
业务处理过程,发现了以下问题,代码一是原代码能正常执行,代码二是经过迭代一次非正常执行代码
代码一:以下代码开启线程后,代码正常执行
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, Tim...
我在使用Spring(3.0.5),Hibernate(3.6.0)和Wicket(1.4.14)开发应用程序时遇到了奇怪的问题.问题是:我无法将任何对象保存或修改为数据库.通过’不能’,我的意思是对象的所有更改或对EntityManager.persist(foo)的调用都被简单地,默默地忽略.选择工作.示例案例很简单 – 在一些wicket页面上我尝试将对象保存到数据库中,如下所示public...
点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析...
除非特殊配置(比如使用 AspectJ 静态织入实现 AOP),否则只有定义在 public 方法上的 @Transactional 才能生效。因为Spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。
必须通过代理过的类从外部调用目标方法才能生效。Spring 通过 AOP 技术对方法进行增强,要调用增强过的方法必然是调用代理后的对象。因为this 指针代表对象自己,Spring 不可能注入 this,所..
这个手动开启事务,需要每个方法都需要实现,这个也是比较繁琐,这种方式可以抽象出一个公共类,统一来实现事务的处理。方法一和方法二的效率都比较低,另一种方式可以把,可以把需要 把需要并发控制的业务,单独抽离出来,进行事务控制操作。由上图可以看出,当线程1 释放了锁,还未提交事务之前,线程2 已经获取锁并提前提交了事务,从而导致了并发的问题。不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。注解的方法 添加 AOP切面来时先事务管理的。最大范围也就是方法级别的。