本文共 7130 字,大约阅读时间需要 23 分钟。
欢迎关注公众号:java4all
6.tcc业务逻辑编写
在使用tcc框架处理分布式事务时,需要我们自己来编写tcc业务代码。这里演示一个简单的加钱的操作。
一个tcc操作,分为try,confirm,cancel三个操作。
根据创建的company表,可以看到公司表有一个money金额字段,还有个frozen字段,在业务简单时,我们可以借助这个字段来实现tcc;如果业务复杂,修改多个字段时,我们可以不要这个字段,tcc的每一步,都直接操作目标字段。
6.1mappper.xml编写
业务的目标操作是:给money加钱。
这里分为三条sql,分别是:
1.try操作
这里先添加在frozen字段,暂不操作money字段。(如果业务对操作资源需要做判断,比如是不是够减,那就需要操作money字段)
2.confirm操作
如果try操作成功了,且各链路服务都没有问题,框架认为走confirm分支时,那我们就认为网络和服务等是没有问题的,本地事务就执行这个confirm操作,操作money字段,同时,把fronzen字段清零。
3.cancel操作
如果链路上有环节出现异常,那么就会进入cancel分支,执行cancel逻辑。
UPDATE company SET frozen = frozen + #{money} WHERE id = #{id} UPDATE company SET money = money + #{money},frozen = frozen - #{money} WHERE id = #{id} UPDATE company SET frozen = frozen - #{money} WHERE id = #{id}
6.2dao编写
package com.java4all.dao;import java.math.BigDecimal;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;/** * description: * * @author wangzhongxiang * @date 2019/2/14 14:39 */@Repositorypublic interface CompanyDao { int increaseMoney(@Param("id") Integer id,@Param("money") BigDecimal money); int confirmIncreaseMoney(@Param("id") Integer id,@Param("money") BigDecimal money); int cancelIncreaseMoney(@Param("id") Integer id,@Param("money") BigDecimal money);}
6.3service编写
接口里就一个方法,加钱的方法。
package com.java4all.service;import java.math.BigDecimal;/** * description: * * @author wangzhongxiang * @date 2019/2/14 14:38 */public interface CompanyService { int increaseMoney(Integer id, BigDecimal money);}
6.4serviceImpl编写
接口的加钱方法,对应过来是有3个实现方法的,confirm和cancel的逻辑分别写在对应的实现类中,try逻辑,我们可以直接写在controller中,也可以单独的写在一个实现类中,我这里写在实现类中,这样会更加清晰,实际开发中也推荐这样写,功能非常清晰,controller不应该承载业务逻辑,业务都在service接口的实现类中。
1.CompanyServiceImpl
CompanyServiceImpl 中对应的是try逻辑。
package com.java4all.service.impl;import com.java4all.dao.CompanyDao;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * description: * tcc try逻辑 * 1.@Service("companyServiceImpl")这里必须指定此bean名称,tcc过程依靠此名称区分执行tcc中哪个逻辑 * 2.参与tcc的方法必须添加@Transactional注解 * 3.建议tcc每个步骤,方法执行后添加日志,方便问题排查 * * @author wangzhongxiang * @date 2019/2/14 15:06 */@Slf4j@Service("companyServiceImpl")public class CompanyServiceImpl implements CompanyService{ @Autowired private CompanyDao companyDao; @Override @Transactional public int increaseMoney(Integer id, BigDecimal money) { int line = companyDao.increaseMoney(id, money); log.info("【try】 increaseMoney: id = "+id+",money ="+money); return line; }}
2.CompanyServiceConfirm
CompanyServiceConfirm中对应的是confirm逻辑。
package com.java4all.service.impl;import com.java4all.dao.CompanyDao;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * description: * tcc confirm逻辑 * 1.@Service("companyServiceConfirm")这里必须指定此bean名称,tcc过程依靠此名称区分执行tcc中哪个逻辑 * 2.参与tcc的方法必须添加@Transactional注解 * 3.建议tcc每个步骤,方法执行后添加日志,方便问题排查 * * @author wangzhongxiang * @date 2019/2/14 15:06 */@Slf4j@Service("companyServiceConfirm")public class CompanyServiceConfirm implements CompanyService{ @Autowired private CompanyDao companyDao; @Override @Transactional public int increaseMoney(Integer id, BigDecimal money) { int line = companyDao.confirmIncreaseMoney(id, money); log.info("【confirm】 increaseMoney: id = "+id+",money ="+money); return line; }}
CompanyServiceCancel
CompanyServiceCancel中是cancel的逻辑。
package com.java4all.service.impl;import com.java4all.dao.CompanyDao;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * description: * tcc cancel逻辑 * 1.@Service("companyServiceCancel")这里必须指定此bean名称,tcc过程依靠此名称区分执行tcc中哪个逻辑 * 2.参与tcc的方法必须添加@Transactional注解 * 3.建议tcc每个步骤,方法执行后添加日志,方便问题排查 * * @author wangzhongxiang * @date 2019/2/14 15:06 */@Slf4j@Service("companyServiceCancel")public class CompanyServiceCancel implements CompanyService{ @Autowired private CompanyDao companyDao; @Override @Transactional public int increaseMoney(Integer id, BigDecimal money){ int line = companyDao.cancelIncreaseMoney(id, money); log.info("【cancel】 increaseMoney: id = "+id+",money ="+money); return line; }}
6.5controller编写
controller中有一个需要注意的点,就是tcc的核心实现@Compensable注解。@Compensable注解有3三参数,这里引用下作者提供的文档中对此的说明:
通过@Compensable注解定义的service为可补偿型service。@Compensable注解需要定义三个参数:
1)interfaceClass,必需。该值用于指定confirm/cancel针对的业务接口,该接口同时被用于校验confirm/cancel实现类。confirm/cancel实现类如果没有实现该业务接口则会被认为无效;
2)confirmableKey,可选。该值用于指定confirm实现类在容器中的beanId,若没有confirm逻辑则不必指定;
3)cancellableKey,可选。该值用于指定cancel实现类在容器中的beanId,若没有cancel逻辑则不必指定;注意:若try阶段执行了写操作则必须有相应的取消逻辑;
4)服务提供方Controler必须添加@Compensable注解;
5)针对一个特定的可补偿型服务接口,业务系统提供的Try、Confirm、Cancel三个实现类,其Try实现类必须定义@Compensable注解,而Confirm、Cancel实现类则不能定义Compensable注解;
文档地址:
package com.java4all.controller;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.bytesoft.compensable.Compensable;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.transaction.annotation.Transactional;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author wangzhongxiang * @date 2019/2/14 14:38 */@Compensable( interfaceClass = CompanyService.class, confirmableKey = "companyServiceConfirm", cancellableKey = "companyServiceCancel")@Slf4j@RestController@RequestMapping("company")public class CompanyController { /** * 在调用此接口时,首先执行的是tcc中的try, * 因此,我们需要明确指定引入的究竟是此接口的哪一个实现类, * 首先执行try,try逻辑写在companyServiceImpl类中,我们就需要明确指定, * @Autowired private CompanyService companyService * 上述这种写法是不可以的,无法识别究竟是哪一个实现类,因此实现类,需要明确指定bean名称 * @Compensable注解扫描时,在哪一种状态执行哪个实现类方法,是 * */ @Autowired private CompanyService companyServiceImpl; @PostMapping("increaseMoney") @Transactional public void increaseMoney(Integer id,BigDecimal money){ int line = companyServiceImpl.increaseMoney(id, money); log.info("修改行数为:"+line); }}
- description:
- try逻辑有2种写法:
- 1.直接写在controller中
- 在controller中直接引入dao层,调用dao层方法,
- 此时,@Compensable添加在controller中;
- 2.写在接口的实现类中
- 此时,在controller中引入实现类时,需要明确指定bean名称。
- @Compensable注解,应该仅仅添加在controller中
- 如果仅仅添加在try逻辑的实现类上,那么仅仅会执行try逻辑,cc逻辑不会执行;
- 如果try逻辑的实现类和controller都添加,那么cc逻辑会执行两遍。
- 在官方文档中,明确指出了:必须在try的实现类添加@Compensable,而controller建议添加。
- 此说法经过验证有问题。可能是之前版本遗留问题,但是文档没有及时更新。
框架会根据每一步操作的结果来控制事务走向,选择是confirm还是cancel逻辑。
转载地址:https://it4all.blog.csdn.net/article/details/87878458 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!