项目
博客
文档
归档
资源链接
关于我
项目
博客
文档
归档
资源链接
关于我
22| 事务传播级别和隔离属性
2024-08-09
·
·
原创
·
·
本文共 566个字,预计阅读需要 2分钟。
### 事务特性&事务管理方式 事务:多个操作,要么同时成功,要么失败后⼀起回滚 具备ACID四种特性: - Atomic(原子性) - Consistency(⼀致性) - Isolation(隔离性) - Durability(持久性) 常见的Spring事务管理方式: - 编程式事务管理: 代码中调用`beginTransaction()、commit()、rollback()`等事务管理相关的方法,通过`TransactionTempalte`手动管理事务(用的少) - 声明式事务管理: 通过AOP实现,可`配置⽂件⽅式或者注解⽅式`实现事务的管理控制(用的多) 声明式事务管理本质: - 本质是`对⽅法前后进⾏拦截`,底层是建立在 AOP 的基础之上 - 在`⽬标⽅法开始之前创建或者加⼊⼀个事务`,在执行完目标方法`之后根据执⾏情况提交或者回滚事务`。 ### Spring事务的传播属性和隔离级别 事物`传播⾏为`介绍: 如果在开始当前`事务之前`,`⼀个事务上下⽂已经存在`,此时有若⼲选项可以指定⼀个事务性⽅法的执⾏⾏为。 - `@Transactional(propagation=Propagation.REQUIRED`) 如果有事务, 那么加⼊事务, 没有的话新建⼀个(`默认`情况下) - `@Transactional(propagation=Propagation.NOT_SUPPORTED`) 不为这个⽅法开启事务 - `@Transactional(propagation=Propagation.REQUIRES_NEW`) 不管是否存在事务,都`创建⼀个新`的事务,原来的挂起,新的执⾏完毕,继续执⾏⽼的事务 - `@Transactional(propagation=Propagation.MANDATORY`) 必须在⼀个已有的事务中执⾏,否则抛出异常 - `@Transactional(propagation=Propagation.NEVER`)必须在⼀个没有的事务中执⾏,否则抛出异常(与Propagation.MANDATORY相反) - `@Transactional(propagation=Propagation.SUPPORTS`) 如果其他bean调⽤这个⽅法,在其他bean中声明事务,那就⽤事务.如果其他bean没有声明事务,那就不⽤事务. - `@Transactional(propagation=Propagation.NESTED`) 如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏; 如果当前没有事务,则该取值等价于`Propagation.REQUIRED`。 事务`隔离级别`: 是指若⼲个并发的事务之间的隔离程度。 - `@Transactional(isolation =Isolation.READ_UNCOMMITTED`) 读取未提交数据(会出现脏读, 不可重复读) 基本不使⽤ - `@Transactional(isolation =Isolation.READ_COMMITTED`) 读取已提交数据(会出现不可重复读和幻读) - `@Transactional(isolation =Isolation.REPEATABLE_READ`) 可重复读(会出现幻读) - `@Transactional(isolation =Isolation.SERIALIZABLE`) 串⾏化 MYSQL: 默认为`REPEATABLE_READ`级别 ### 领劵接口微服务本地事务配置 - 启动类增加注解`@EnableTransactionManagement` - ⽅法增加注解`@Transactional(rollbackFor=Exception.class,propagation=Propagation.REQUIRED)` - 指定⽅法需要事务再添加,`不能全局都使⽤` 分布式锁测试锁续期的时候: 不能使⽤debug模式,不然看到不到⾃动续期。 ### 个人领券记录分页接口 controller层 ```java @Api(tags = "优惠券领劵记录控制器") @RestController @RequestMapping("/cop/couponRecord/v1") public class CouponRecordController { @Resource private CouponRecordService couponRecordService; @ApiOperation("分页查询个人优惠券") @GetMapping("page") public JsonData page( @ApiParam(value = "当前页") @RequestParam(value = "page", defaultValue = "1") int page, @ApiParam(value = "每页显示多少条")@RequestParam(value = "size", defaultValue = "10") int size) { return JsonData.buildSuccess(couponRecordService.page(page, size)); } } ``` vo层: ```java @Data public class CouponRecordVO{ private Long id; /** * 优惠券id */ @JsonProperty("coupon_id") private Long couponId; /** * 使用状态 可用 NEW,已使用USED,过期 EXPIRED; */ @JsonProperty("use_state") private String useState; /** * 用户id */ @JsonProperty("user_id") private Long userId; /** * 用户昵称 */ @JsonProperty("user_name") private String userName; /** * 优惠券标题 */ @JsonProperty("coupon_title") private String couponTitle; /** * 开始时间 */ @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",locale = "zh",timezone = "GMT+8") @JsonProperty("start_time") private Date startTime; /** * 结束时间 */ @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",locale = "zh",timezone = "GMT+8") @JsonProperty("end_time") private Date endTime; /** * 订单id */ @JsonProperty("order_id") private Long orderId; /** * 抵扣价格 */ private BigDecimal price; /** * 满多少才可以使用 */ @JsonProperty("condition_price") private BigDecimal conditionPrice; } ``` service实现 ```java @Override public Map
page(int page, int size) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); //封装分页信息 Page
pageInfo = new Page<>(page, size); IPage
recordPage = baseMapper.selectPage(pageInfo, Wrappers.
lambdaQuery().eq(CouponRecordEntity::getUserId, loginUser.getId()) .orderByDesc(CouponRecordEntity::getCreateTime)); Map
pageMap = new HashMap<>(3); pageMap.put("total_record", recordPage.getTotal()); pageMap.put("total_page", recordPage.getPages()); pageMap.put("current_data", recordPage.getRecords().stream().map(this::beanProcess).collect(Collectors.toList())); return pageMap; } private CouponRecordVO beanProcess(CouponRecordEntity couponRecordDO) { CouponRecordVO couponRecordVO = new CouponRecordVO(); BeanUtils.copyProperties(couponRecordDO, couponRecordVO); return couponRecordVO; } ``` ### 查询优惠券记录详情 防止水平权限攻击: 水平权限攻击:也叫作访问控制攻击,Web应用程序接收到用户请求,修改某条数据时,没有判断数据的所属⼈,或者在判断数据所属⼈时从用户提交的表单参数中获取了userid。 导致攻击者可以自行修改userid修改不属于自己的数据。 controller层: ```java @ApiOperation("查询优惠券记录详情") @GetMapping("detail/{record_id}") public JsonData getCouponRecordDetail(@ApiParam(value = "记录id") @PathVariable("record_id") long recordId){ CouponRecordVO couponRecordVO = couponRecordService.findById(recordId); return couponRecordVO == null ? JsonData.buildResult(BizCodeEnum.COUPON_NO_EXITS):JsonData.buildSuccess(couponRecordVO); } ``` service实现 ```java @Override public CouponRecordVO findById(long recordId) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); CouponRecordEntity couponRecord = baseMapper.selectOne( Wrappers.
lambdaQuery(). eq(CouponRecordEntity::getUserId, loginUser.getId()) .eq(CouponRecordEntity::getId, recordId)); if (couponRecord == null) { return null; } CouponRecordVO result = new CouponRecordVO(); BeanUtils.copyProperties(couponRecord, result); return result; } ```