增删改 - 01 - 增删改流程

增删改事务 - 01 - 增删改流程

1. 流程概述

增删改作为事务相关的处理,需要格外注意入参的正确性,不正确的参数可能会导致异常

增删改操作时,用 DTO 来封装待修改数据,对DTO进行入参校验,校验完毕后,传入 service 层,将 DTO 转为 entity,存储到数据库

四个词就能概括下图:入参,校验,转换,入库

DTO增删改流程

2. 流程细化

在这里举个简单的流程栗子:

2.1 入参 + 校验

2.1.1 构建DTO入参类,并做基础校验
/** DTO,非空校验添加的地方 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WorkOperatorDTO {
    
    /** 主键ID */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /** 作业单位ID */
    @NotNull(message = "作业单位ID不能为空")
    private Long workCompId;

    /** 电子工牌 - 非必填,可以设为 Null 或者 空字符串,修改 MP 的更新策略,使其不会跳过 NULL 的列 */
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private String eleWorkCard;

    /** 身份证号 */
    @NotBlank(message = "身份证号不能为空")
    @Size(max = 18, message = "身份证号长度不能超过18位")
    private String idCardNumber;

    /** 关联的 ids */
    @NotEmpty(message = "关联的 ids 不能为空")
    List<Long> ids;
    
    /** 入职日期 */
    @NotBlank(message = "入职日期不能为空,格式yyyy-MM-dd")
    @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$", message = "入职日期格式错误(yyyy-MM-dd)")
    private String comeInDate;
}

2.1.2 在 controller 里面做复杂校验

注: ValidationUtils 是自己写的一个小工具类,主要判断字符串是否匹配正则表达式

/** Controller层 */
import javax.annotation.Resource;

@RestController
@RequestMapping("/work-operator")
public class WorkOperatorController {

    @Resource
    private IWorkOperatorService workOperatorService;

    /**
     * 入参校验,能够统一处理,则放到统一校验类里面,否则放到自己的类里处理
     * @param dto 增改时的入参类
     * @return
     */
    private R checkDTO(WorkOperatorDTO dto) {
        //  简单校验:身份证号、身份证号和性别匹配
        if (dto.getIdCardNumber().length() != ValidationUtils.CHINA_ID_MAX_LENGTH) {
            return R.fail(ConstantMessage.NOT_CHINA_ID_MAX_LENGTH);
        }
        if (!ValidationUtils.matchTargetRegex(dto.getIdCardNumber(), ValidationUtils.ID_CARD_REGEX)) {
            return R.fail(ConstantMessage.NOT_CHINA_ID);
        }
        //  校验性别
        int sexRes = Integer.parseInt(dto.getIdCardNumber().substring(16, 17)) % 2 == 0 ? 2 : 1;
        if (sexRes != dto.getSex()) {
            return R.fail(ConstantMessage.SEX_NOT_MATCH_CHINA_ID);
        }
        //  校验手机号
        if (!ValidationUtils.matchTargetRegex(dto.getPhone(), ValidationUtils.PHONE_REGEX)) {
            return R.fail(ConstantMessage.PHONE_NOT_MATCH_REGEX);
        }
        //  校验电子工牌,不为空时,需要判断是否匹配正则表达式
        if (StringUtils.isNotEmpty(dto.getEleWorkCard())) {
            if (!ValidationUtils.matchTargetRegex(dto.getEleWorkCard(), ValidationUtils.NUM_ENG_LIMIT_30)) {
                return R.fail(ConstantMessage.PLEASE_TRY_AGAIN + ConstantMessage.LENGTH_FORMAT_ERR);
            } else {
                //  判断电子工牌是否存在
                boolean exist = workOperatorService.lambdaQuery().eq(WorkOperator::getEleWorkCard, dto.getEleWorkCard()).ne(dto.getId() != null, WorkOperator::getEleWorkCard, dto.getEleWorkCard()).exists();
                if (exist) {
                    return R.fail(ConstantMessage.PRE_FIX + ConstantMessage.ELE_CARD_EXIST + ConstantMessage.PLEASE_TRY_AGAIN);
                }
            }
        }

        //  校验,中文30字符以内
        if (!ValidationUtils.matchTargetRegex(dto.getName(), ValidationUtils.ZH_REGEX_LIMIT_30)) {
            return R.fail(ConstantMessage.PRE_FIX + ConstantMessage.NAME_ERROR + ConstantMessage.PLEASE_TRY_AGAIN);
        }

        //  手机号已存在时,
        if (workOperatorService.lambdaQuery().eq(WorkOperator::getPhone, dto.getPhone()).ne(dto.getId() != null, WorkOperator::getId, dto.getId()).exists()) {
            return R.fail(ConstantMessage.PRE_FIX + ConstantMessage.PHONE_EXIST + ConstantMessage.PLEASE_TRY_AGAIN);
        }

        //  同理,可校验其他的 entity 属性
        return null;
    }

}

对手机号和电子工牌唯一性校验的解释:
ne(dto.getId() != null, WorkOperator::getId, dto.getId()) 这段用来查询与当前 id 不同的数据

构建DTO有两种情况,一是新增,二是修改
新增时,没有 id 字段,ne(dto.getId() != null, WorkOperator::getId, dto.getId()) 这段不会执行

修改时,因为id存在,ne(dto.getId() != null, WorkOperator::getId, dto.getId()) 这段查询代码会执行,用来判断当前值除去自身外是否唯一,避免了修改时,与原值相同而无法入库的情况

这段代码可以使用MP的写法封装一下,后续封装好后,过来更新

2.3 在Controller里让两个校验生效
import javax.validation.Valid;

@RestController
@RequestMapping("/work-operator")
public class Controller {

    @Resource
    private IWorkOperatorService workOperatorService;

    /**
     * 入参校验,能够统一处理,则放到统一校验类里面,否则放到自己的类里处理
     * @param dto 增改时的入参类
     * @return
     */
    private R checkDTO(WorkOperatorDTO dto) {
    	//	上面的入参复杂校验
    }

    /** 新增 */
    @PostMapping("/create")
    public R<Object> create(@Valid @RequestBody WorkOperatorDTO saveDTO, @User UserBO userBO) {
        //  判断入参是否正确
        if (checkDTO(saveDTO) != null) {
            return checkDTO(saveDTO);
        }
        //	入参正确后进入 service 层
        return workOperatorService.createWorkOperator(saveDTO) ?
                R.ok(ConstantMessage.CREATE_SUCCESS) : R.fail(ConstantMessage.CREATE_FAIL);
    }

    /** 修改 */
    @PostMapping("/update")
    public R<Object> update(@Valid @RequestBody WorkOperatorDTO updateDTO, @User UserBO userBO) {
    	//  判断入参是否正确
        if (checkDTO(updateDTO) != null) {
            return checkDTO(updateDTO);
        }
		//	入参正确后进入 service 层
        return workOperatorService.updateWorkOperator(updateDTO) ?
                R.ok(ConstantMessage.UPDATE_SUCCESS) : R.fail(ConstantMessage.UPDATE_FAIL);
    }

}

注1:@Valid 必须添加后,才能达到第一步简单校验的效果
注2:第二步的复杂校验可直接放在 controller 里处理


2.2 转换

往期文章里写了转换类的方法,使用同样的方法,将 DTO -> entity

详情跳转到:下面文章中,1.2 类的调整方法

传送门

给个实体类 - entity 的栗子:

/** entity */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("work_operator")
public class WorkOperator implements Serializable {

    private static final long serialVersionUID = 1L;

    /** 主键ID */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /** 作业单位ID */
    private Long workCompId;

    /** 电子工牌 - 非必填,可以设为 Null 或者 空字符串,修改 MP 的更新策略,使其不会跳过 NULL 的列 */
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private String eleWorkCard;

    /** 身份证号 */
    private String idCardNumber;

    /** 关联的 ids */
    @TableField(typeHandler = JacksonTypeHandler.class)
    List<Long> ids;

}

Service 层的栗子:

public class Service {
    @Resource
    private WorkOperatorDAO workOperatorDAO;

    /** 新增方法 */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createWorkOperator(WorkOperatorDTO saveDTO) {
        //  DTO -> entity
        //  需要额外查询 片区、片区id、作业单位名称、年龄
        WorkOperator saveOne = transDTO2Entity(saveDTO);
        saveOne.setId(null);
        //  保存
        return workOperatorDAO.insertWorkOperator(saveOne);
    }

    /** 更新方法 */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWorkOperator(WorkOperatorDTO updateDTO) {
        //  DTO -> entity
        WorkOperator updateOne = transDTO2Entity(updateDTO);
        workOperatorDAO.updateWorkOperator(updateOne);
        return true;
    }

    /** DTO -> 实体类 */
    private WorkOperator transDTO2Entity(WorkOperatorDTO saveDTO) {
        WorkOperator one = new WorkOperator(saveDTO);

        one.setAge(IdCardUtil.getAgeByIdNumber(saveDTO.getIdCardNumber()));
        return one;
    }

}

2.3 入库

  • 入库操作放在了 DAO 层,举个栗子
import org.apache.commons.lang3.StringUtils;

/** DAO层 */
@Repository
public class DAO {
    /** 更新 */
    public void updateWorkOperator(WorkOperator workOperator) {
    	/** 如果前端传的是空字符串,会影响mysql 的 uniqueKey,故处理为 null */
        if (StringUtils.isEmpty(workOperator.getEleWorkCard())) {
            workOperator.setEleWorkCard(null);
        }
        try {
            workOperatorMapper.updateById(workOperator);
        } catch (Exception e) {
            checkException(e);
        }
    }

    /** 新增 */
    public boolean insertWorkOperator(WorkOperator workOperator) {
        try {
            return workOperatorMapper.insert(workOperator) > 0;
        } catch (Exception e) {
            checkException(e);
        }
        return true;
    }

    /** 处理异常,部分通用异常可放到统一异常处理器里面,特殊异常可放到自己的类里处理 */
    private void checkException(Exception e) {
        if (StringUtils.containsIgnoreCase(e.getMessage(), "duplicate entry") && StringUtils.containsIgnoreCase(e.getMessage(), "work_operator.un_id_card_number")) {
            throw new IcpBizException("该身份证号已有职务,请重新确认!");
        }
        if (StringUtils.containsIgnoreCase(e.getMessage(), "duplicate entry") && StringUtils.containsIgnoreCase(e.getMessage(), "work_operator.un_ele_card")) {
            throw new IcpBizException("该电子工牌已被使用,请重新确认!");
        }
        if (StringUtils.containsIgnoreCase(e.getMessage(), "duplicate entry") && StringUtils.containsIgnoreCase(e.getMessage(), "work_operator.un_phone")) {
            throw new IcpBizException("该手机号已被使用,请重新确认!");
        }
    }
}
  • 注1:IcpBizException 是个自定义的类,设置了全局异常捕获,后续会讲到
  • 注2:异常里的三个字段均设置了 mysql 的 uniqueKey
  • 注3:记得使用 @Repository 注解,暴露 class 给 spring 容器
  • 注4:StringUtils.containsIgnoreCase(str A, str B) 这段代码用于判断字符串A中是否包含字符串B,同时忽略大小写。
  • 24
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Node.js环境下,使用Mongoose可以方便地进行对MongoDB的增删操作。首先,需要导入Mongoose和数据库连接配置文件,在该配置文件中,使用`mongoose.connect`方法连接到MongoDB数据库。然后,定义一个模型(Model),模型是根据数据结构定义的类,用于操作数据库中的集合。模型需要定义一个Schema来规定数据的结构。在Schema中,可以定义字段的类型、默认值等。最后,通过模型的方法,可以对数据库进行增删操作。 增加数据: 要在数据库中增加一条数据,首先需要创建一个模型实例,并设置数据的属性,然后调用`save`方法进行保存。 删除数据: 要删除数据库中的数据,可以使用模型的`deleteOne`或`deleteMany`方法。可以根据条件指定要删除的数据。 修数据: 要修数据库中的数据,首先要根据条件查询到对应的数据,然后修数据的属性,最后调用`save`方法进行保存。 以上是Node.js中使用Mongoose进行数据库的增删操作的基本流程。具体的代码可以参考示例代码和Mongoose的官方文档进行更详细的了解和实践。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Node.js 高级篇(三):Mongoose 增删改查](https://blog.csdn.net/qq_41887214/article/details/123947394)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [mongoose 入门(一) mongoose 实现数据 的增、删、、查、默认参数 、模块化](https://blog.csdn.net/qq_41880073/article/details/122895427)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值