Spring(1) Spring的7种事务传播行为

文章介绍了Spring中的7种事务传播行为,如REQUIRED、REQUIRES_NEW等,并通过示例详细解释了如何使用@Transaction注解来配置这些行为。特别强调了REQUIRES_NEW的作用,即创建新事务并暂停当前事务,适合于确保方法执行独立于调用它的事务的场景。文中还给出了TUserAService和TUserBService的代码示例,展示了如何在服务层方法中应用不同的事务策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.7种事务传播行为

Spring 中定义了七种事务传播行为,分别是:(propagation:n.传播,宣传,培养)

  1. REQUIRED:如果当前存在事务,则加入该事务,否则新建一个事务。这是最常见的传播行为,也是默认的传播行为。
  2. SUPPORTS:支持当前事务,如果当前不存在事务,则以非事务方式执行。
  3. MANDATORY:强制要求当前存在事务,如果不存在事务,则抛出异常。(mandatory:adj.强制的)
  4. REQUIRES_NEW:重新开启一个新的事务,如果当前存在事务,则挂起该事务。(注意:默认的事务隔离级别——可重复读中 ,开启的新事务读不到之前挂起事务的操作,而且如果操作相同的表会导致锁表,一定要谨慎使用!
  5. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则挂起该事务。
  6. NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。
  7. NESTED:如果当前存在事务,则嵌套事务中执行。嵌套事务是相对于外部事务而言的,它可以独立提交或回滚,但是嵌套事务的提交或回滚并不会对外部事务产生影响。如果外部事务不存在,那么 NESTEDREQUIRED 的效果是一样的。该传播行为只有在使用 JDBC 事务时才有效。

事务传播行为决定了一个方法执行时如何参与到已有的事务中,或者如何创建新的事务。需要根据实际的业务场景和要求来选择合适的事务传播行为。

2.事务使用示例

使用事务传播行为需要在方法上加上 @Transactional 注解,并指定传播行为,例如:

@Transactional(propagation=Propagation.REQUIRED)
public void doSomething() {
    // ...
}

这样就将 doSomething() 方法设置为在当前存在事务时,加入该事务,如果不存在事务,则创建一个新的事务。如果需要设置其他的传播行为,只需要将 Propagation.REQUIRED 替换成其他传播行为即可。同时,需要保证 @Transactional 注解在方法所在的类上被正确地启用了事务管理。

3.REQUIRES_NEW 事务传播行为使用示例

REQUIRES_NEW 是 Spring 中的一个事务传播行为。

  • 作用: 创建一个新的事务,并暂停当前事务(如果存在)。
  • 使用场景: 可以使用这个传播行为来确保一个方法执行时独立于调用它的方法所在的事务。
  • 场景示例: 日志记录场景。PostgreSQL 同一事务中,如果事务中间有报错,后面的所有SQL都会执行失败。这时我们就非常需要 PRAOPAGATION_REQUIRES_NEW 这种事务传播行为。

3.1 事务传播图

下面是一个使用 REQUIRES_NEW 事务传播行为的示例:

在这里插入图片描述

3.2 TUserAServiceImpl.java

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.module.entity.TUser;
import com.demo.module.mapper.TUserMapper;
import com.demo.module.service.TUserAService;
import com.demo.module.service.TUserBService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author ACGkaka
 * @since 2021-04-25
 */
@Slf4j
@Service
public class TUserAServiceImpl extends ServiceImpl<TUserMapper, TUser> implements TUserAService {

    @Autowired
    private TUserBService tUserBService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUserA() {
        // 添加用户A
        TUser user = new TUser(null, "username_aaa", "pwd_aaa", LocalDateTime.now(), LocalDateTime.now());
        this.save(user);
        // 调用方法B
        try {
            tUserBService.addUserB();
        } catch (Exception e) {
            log.error("addUserA filed. reason: {}", e.getMessage());
        }
    }
}

3.3 TUserBServiceImpl.java

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.module.entity.TUser;
import com.demo.module.mapper.TUserMapper;
import com.demo.module.service.TUserBService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author ACGkaka
 * @since 2021-04-25
 */
@Slf4j
@Service
public class TUserBServiceImpl extends ServiceImpl<TUserMapper, TUser> implements TUserBService {

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void addUserB() {
        // 添加用户B
        TUser user = new TUser(null, "username_bbb", "pwd_bbb", LocalDateTime.now(), LocalDateTime.now());
        this.save(user);
        throw new RuntimeException("抛出异常");
    }
}

整理完毕,完结撒花~

### 微信小程序中左侧导航栏与右侧内容联动的实现方式 在微信小程序开发中,要实现左侧导航栏与右侧内容区域的联动功能,可以按照以下方法完成: #### 1. 页面结构设计 通过 `WXML` 定义页面的整体布局。通常分为两个部分:左侧导航栏和右侧内容展示区。 ```html <view class="container"> <!-- 左侧导航 --> <scroll-view scroll-y class="left_nav" bindscroll="onLeftScroll"> <block wx:for="{{nav_list}}" wx:key="id"> <view class="menu-item {{index === activeIndex ? 'active' : ''}}" data-index="{{index}}" bindtap="switchCategory" > {{item.text}} </view> </block> </scroll-view> <!-- 右侧内容 --> <scroll-view scroll-y class="right_content" bindscroll="onRightScroll"> <block wx:for="{{content_list}}" wx:key="id"> <view id="{{item.id}}" class="section"> <text>{{item.title}}</text> <view wx:for="{{item.items}}"> <text>{{item.name}}</text> </view> </view> </block> </scroll-view> </view> ``` 上述代码实现了基本的页面结构,其中左侧导航栏是一个垂直滚动视图 (`scroll-view`),而右侧的内容也是一个独立的滚动视图[^3]。 --- #### 2. 样式设置 为了使左侧导航栏固定在屏幕一侧,可以通过 `WXSS` 设置其样式。 ```css /* 整体容 */ .container { display: flex; } /* 左侧导航栏 */ .left_nav { position: relative; width: 200rpx; background-color: #f8f8f8; border-right: 1px solid #ddd; } .menu-item { padding: 20rpx; text-align: center; font-size: 28rpx; } .menu-item.active { color: red; background-color: white; } /* 右侧内容区域 */ .right_content { flex: 1; padding: 20rpx; } ``` 这里设置了 `.left_nav` 的宽度为固定的 `200rpx` 并将其背景颜色与其他区域区分,同时利用 `flex` 布局让右侧内容占据剩余空间[^2]。 --- #### 3. 数据绑定与交互逻辑 在 `JS` 中处理数据绑定以及点击事件和滚动监听。 ##### (1) 初始化数据 ```javascript Page({ data: { nav_list: [ { id: 1, text: "水果" }, { id: 2, text: "蔬菜" }, { id: 3, text: "饮料" } ], content_list: [ { id: 1, title: "水果", items: [{ name: "苹果" }, { name: "香蕉" }] }, { id: 2, title: "蔬菜", items: [{ name: "白菜" }, { name: "胡萝卜" }] }, { id: 3, title: "饮料", items: [{ name: "可乐" }, { name: "雪碧" }] } ], activeIndex: 0, scrollTopMap: {} }, onLoad() { this.initScrollTopMap(); }, initScrollTopMap() { const query = wx.createSelectorQuery().in(this); let scrollTopMap = {}; this.data.content_list.forEach((item, index) => { query.select(`#${item.id}`).boundingClientRect(rect => { scrollTopMap[item.id] = rect.top; if (Object.keys(scrollTopMap).length === this.data.content_list.length) { this.setData({ scrollTopMap }); } }).exec(); }); } }); ``` 初始化时计算每个右侧内容区块的顶部位置,并存储到 `scrollTopMap` 对象中以便后续使用[^1]。 ##### (2) 处理左侧菜单点击事件 ```javascript switchCategory(e) { const index = e.currentTarget.dataset.index; this.setData({ activeIndex: index, scrollTop: this.data.scrollTopMap[this.data.nav_list[index].id] }); this.selectComponent('.right_content').scrollTo({ scrollTop: this.data.scrollTopMap[this.data.nav_list[index].id], duration: 300 }); } ``` 当用户点击某个左侧菜单项时,更新当前激活索引并触发右侧内容滚动至相应位置。 ##### (3) 监听右侧内容滚动事件 ```javascript onRightScroll(e) { const scrollTop = e.detail.scrollTop; Object.keys(this.data.scrollTopMap).forEach(key => { const top = this.data.scrollTopMap[key]; if (scrollTop >= top && scrollTop < top + 300) { // 判断范围可根据实际需求调整 this.setData({ activeIndex: parseInt(key) - 1 || 0 }); } }); } ``` 此函数用于检测右侧内容滚动过程中哪个区块处于可视范围内,并同步更新左侧导航栏的状态。 --- #### 总结 以上方案涵盖了从页面结构、样式设定到核心业务逻辑的具体实现细节。它不仅能够满足基础的需求,还具备良好的扩展性和用户体验优化潜力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿放下技术的小赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值