当前位置 : 主页 > 编程语言 > java >

Java新人易错:当修改遇到唯一

来源:互联网 收集:自由互联 发布时间:2023-03-22
写在前面 现在是Java培训机构林立的时代,更有360行,行行转java的谣传。但是,我们很多人都一味地追求知识面的广度,孜孜不倦地学习各种新技术。 网上大量地博客都打着xxx管理系统

写在前面

现在是Java培训机构林立的时代,更有360行,行行转java的谣传。但是,我们很多人都一味地追求知识面的广度,孜孜不倦地学习各种新技术。

网上大量地博客都打着xxx管理系统的亮眼标题,哇,免费的可以白嫖,赶紧下载,编译,部署。搭出来一个博客页面,有类型,文章的增删改查,各种新技术,boot,cloud,vue,感觉自己碉堡了。

但是,我们往往却疏忽了业务逻辑的锻炼,那些项目再好,页面再华丽,也无非就是最最简单的增删改查,为一大群crud boy提供了肥沃的土壤。

于是,又有人说,我要跳出舒适圈,去卷算法。

算法对应高阶的程序来说是有意义的,但是我们更多时候,工作还是写业务代码为主。

不要求你的代码有多么牛逼,但是要求你的代码质量过硬,不要频繁出现各种BUG,引起生产事故。

试金石

不要觉得这是一件很简单的事情哦,往往一个很小的功能,就会埋藏了数不清的坑。

甚至你都会觉得愕然,我TM一个妥妥的crud小能手,怎么遇到这么简单的需求,一不小心就跳进了坑里。

这是一个思维的问题,业务思维无法用无数的crud来升级。

要我说,看一个初入java行业的小白来说,最好的试金石就是这个修改唯一数据的问题。

准备下环境

先把环境搞一搞,为了方便起见,我直接使用了之前写的 ​​《清新版 Springboot日记本系统》​​的代码,添加好test依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId></dependency>

直接搞起。

先来一个Customer类:

@Data@Builderpublic class Customer { @TableId(type = IdType.AUTO) private Integer id; private String name; private String idno; private String isDel;}

对应表结构:

Java新人易错:当修改遇到唯一_Customer

测试添加数据

@Slf4j@SpringBootTest(classes = {DiaryApplication.class})@RunWith(SpringJUnit4ClassRunner.class)public class MyTest { @Resource CustomerMapper customerMapper; @Test public void tt(){ customerMapper.insert(Customer.builder().name("小火龙").idno("1").build()); customerMapper.insert(Customer.builder().name("杰尼龟").idno("2").build()); customerMapper.insert(Customer.builder().name("妙蛙种子").idno("3").build()); }}

得到

Java新人易错:当修改遇到唯一_数据_02

至此,环境准备完毕。

身份证号唯一

需求很简单,要把身份证号设置唯一。

我们知道,id是自动递增的,这个没问题。身份证号唯一,无非就算新增和修改的时候判断一下呗。

新增判断唯一

这个简单,无非就是新增的时候判断一下idno有没有呗。 你很快就写出了代码,并且为自己封装了​​checkIdno​​方法而沾沾自喜:

/** * 测试新增 */@Testpublic void insert(){ //模拟前台传过来的Req Customer customerDto = Customer.builder().name("火球鼠").idno("1").build(); //先检查身份证号是否存在于数据库中 checkIdno(customerDto.getIdno()); //正常入库 customerMapper.insert(customerDto);}private void checkIdno(String idno) { LambdaQueryWrapper<Customer> lambdaQueryWrapper = new LambdaQueryWrapper<>(); Customer one = customerMapper.selectOne(lambdaQueryWrapper.eq(Customer::getIdno, idno)); //不是空,说明有这个idno的数据 if(one != null){ throw new WrongArgumentException("身份证重复!"); }}

测试,报错了,业务正确。

Java新人易错:当修改遇到唯一_数据_03

编辑判断唯一

新增已经开了一个好头,接下来是编辑,作为一个刚毕业后进入公司的萌新,此时正兴致勃勃地敲起了键盘。

嗯,修改的时候正好可以复用刚刚封装的​​checkIdno​​方法。

/** * 测试修改 */@Testpublic void edit(){ //模拟前台传过来的Req Customer customerReq = Customer.builder().id(1).idno("2").build(); //先检查身份证号是否存在于数据库中 checkIdno(customerReq.getIdno()); //根据id查询数据库的数据 Customer customerDto = getById(customerReq.getId()); //更新idno customerDto.setIdno(customerReq.getIdno()); //入库 customerMapper.updateById(customerDto);}private Customer getById(Integer id) { return customerMapper.selectById(id);}

封装性,封装性,这是培训班老师教的,能封装的就封装。 嗯,我又封装了一个getById方法,这是荣誉的勋章,也是我技术实力的证明!

上面代码测试修改小火龙,把身份证从1改成2,与杰尼龟冲突。

来吧,测试。

Java新人易错:当修改遇到唯一_数据_04

( •̀ ω •́ )yeah!

打完收工,注释完美,代码完整,我还封装了呢!

接下来就是升职加薪,迎娶白富美,从此走上人生巅峰了吧。

假如没改idno呢?

人生总是起起落落,酸甜苦辣都有,不可能只有甜。

直到有一天,测试小姐姐找到你,为什么我修改个姓名,也报错了呢?

你直接懵逼了,马上打开postman自测,结果发现还真有问题。

@Testpublic void edit(){ //模拟前台传过来的Req Customer customerReq = Customer.builder().id(1).name("喷火龙").idno("1").build(); //先检查身份证号是否存在于数据库中 checkIdno(customerReq.getIdno()); //根据id查询数据库的数据 Customer customerDto = getById(customerReq.getId()); //更新name customerDto.setName(customerReq.getName()); //入库 customerMapper.updateById(customerDto);}

idno还是1,但是送过来了,小火龙本来的idno就是1,但是因为有了验重逻辑,导致报错了。

你顿时慌张起来,怎么就这么简单的一个小需求,还有这么多弯弯绕呢?

解决方案

一个最简单的思路就是,你编辑的时候,是不是肯定有个id传过来,毕竟我要根据这个id去更新嘛。

那么,我也可以通过这个id,去查询对应的idno,和这次送进来的idno是不是一样的。

如果跟原来的idno不一致,说明这次你改了idno,我就要进行校验,看你修改后的idno有没有和别的数据对应的idno重复。

方法就是拿着修改后的idno去查,查出来就说明有重复,否则就是唯一的,允许修改。

为了测试方便,给edit方法添加参数。

public void edit(Customer customerReq){ //根据id查询数据库是否有这条数据 Customer customer = customerMapper.selectById(customerReq.getId()); //如果不存在或者跟原来的idno不一致,才进行校验 Boolean isNeedCheck = true; if(customer != null && customer.getIdno().equals(customerReq.getIdno())){ isNeedCheck = false; } //检查身份证号是否存在于数据库中 if(isNeedCheck) { checkIdno(customerReq.getIdno()); } //根据id查询数据库的数据 Customer customerDto = getById(customerReq.getId()); //更新name customerDto.setName(customerReq.getName()); //更新idno customerDto.setIdno(customerReq.getIdno()); //入库 customerMapper.updateById(customerDto);}

然后是测试用例:

//测试编辑功能,修改其他字段但idno不变@Testpublic void testEdit01(){ //模拟前台传过来的Req => 只改姓名不改身份证 Customer customerReq = Customer.builder().id(1).name("喷火龙").idno("1").build(); edit(customerReq);}//测试编辑功能,修改idno但是和其他数据的idno一样@Testpublic void testEdit02(){ //模拟前台传过来的Req => 只改姓名不改身份证 Customer customerReq = Customer.builder().id(1).name("喷火龙").idno("2").build(); edit(customerReq);}//测试编辑功能,修改idno但是和其他数据的idno都不一样@Testpublic void testEdit03(){ Customer customerReq = Customer.builder().id(1).name("喷火龙").idno("8").build(); edit(customerReq);}

第一个测试是正常的,因为没有改idno。第二个测试报错了,也符合预期。第三个是正例,结果依然没问题。

方案2

上面的逻辑是没问题的,代码可读性也很强,但是不够简洁。在实际开发中,我也看到有人是这么处理的。

直接上代码,注释很详细了。

public void edit2(Customer customerReq){ /** * 直接根据idno查询,无非有两种情况 * 1.查出来为null,说明没有这个idno,安全 * 2.查出来有一条数据,又分两种情况 * 2.1 这条数据就是要编辑的原数据:说明我们修改这条记录并且没改idno,安全 * 2.2 这条数据不是要编辑的原数据:说明我们修改这条记录并且改了idno,还和别的记录的idno重复了,不安全 * 总结:如果查出来有数据并且id和要编辑数据的id不同,才不安全 */ LambdaQueryWrapper<Customer> lambdaQueryWrapper = new LambdaQueryWrapper<>(); Customer one = customerMapper.selectOne(lambdaQueryWrapper.eq(Customer::getIdno, customerReq.getIdno())); if(Objects.nonNull(one) && !Objects.equals(one.getId(),customerReq.getId())){ throw new WrongArgumentException("身份证重复!"); } //根据id查询数据库的数据 Customer customerDto = getById(customerReq.getId()); //更新name customerDto.setName(customerReq.getName()); //更新idno customerDto.setIdno(customerReq.getIdno()); //入库 customerMapper.updateById(customerDto);}

思路就是直接通过idno去查,无非这四种情况。

直接根据idno查询,无非有两种情况:

1.查出来为null,说明没有这个idno,安全。

2.查出来有一条数据,又分两种情况。

2.1 这条数据就是要编辑的原数据:说明我们修改这条记录并且没改idno,安全。

2.2 这条数据不是要编辑的原数据:说明我们修改这条记录并且改了idno,还和别的记录的idno重复了,不安全。

总结:如果查出来有数据并且id和要编辑数据的id不同,才不安全。

乍一看似乎不好理解,但是只要我们把所有的情况都理清楚了,还是很清晰的。

这个方案确实很简洁,巧妙就巧妙在,不知不觉间它把新增的情况也包含了。

如果是新增,那么customerReq是没有id的,那么getId就是null,必然与one的getId不符合。也能走进这个逻辑:

LambdaQueryWrapper<Customer> lambdaQueryWrapper = new LambdaQueryWrapper<>();Customer one = customerMapper.selectOne(lambdaQueryWrapper.eq(Customer::getIdno, customerReq.getIdno()));if(Objects.nonNull(one) && !Objects.equals(one.getId(),customerReq.getId())){ throw new WrongArgumentException("身份证重复!");}

我们不妨把这个逻辑抽取出来封装成方法。

private void checkIdnoNew(Customer customerReq) { /** * 直接根据idno查询,无非有两种情况 * 1.查出来为null,说明没有这个idno,安全 * 2.查出来有一条数据,又分两种情况 * 2.1 这条数据就是要编辑的原数据:说明我们修改这条记录并且没改idno,安全 * 2.2 这条数据不是要编辑的元数据:说明我们修改这条记录并且改了idno,还和别的记录的idno重复了,不安全 * 总结:如果查出来有数据并且id和要编辑数据的id不同,才不安全 */ LambdaQueryWrapper<Customer> lambdaQueryWrapper = new LambdaQueryWrapper<>(); Customer one = customerMapper.selectOne(lambdaQueryWrapper.eq(Customer::getIdno, customerReq.getIdno())); if(Objects.nonNull(one) && !Objects.equals(one.getId(),customerReq.getId())){ throw new WrongArgumentException("身份证重复!"); }}

编辑方法就变成了这样:

public void edit2(Customer customerReq){ checkIdnoNew(customerReq); //根据id查询数据库的数据 Customer customerDto = getById(customerReq.getId()); //更新name customerDto.setName(customerReq.getName()); //更新idno customerDto.setIdno(customerReq.getIdno()); //入库 customerMapper.updateById(customerDto);}

新增方法也修改下:

@Testpublic void insert(){ //模拟前台传过来的Req Customer customerReq = Customer.builder().name("火球鼠").idno("6").build(); //先检查身份证号是否存在于数据库中 checkIdnoNew(customerReq); //正常入库 customerMapper.insert(customerReq);}

对比之前的方法:

//根据id查询数据库是否有这条数据Customer customer = customerMapper.selectById(customerReq.getId());//如果不存在或者跟原来的idno不一致,才进行校验Boolean isNeedCheck = true;if(customer != null && customer.getIdno().equals(customerReq.getIdno())){ isNeedCheck = false;}//检查身份证号是否存在于数据库中if(isNeedCheck) { checkIdno(customerReq.getIdno());}

是不是要简洁好多呢,这就是业务思维的魅力。

本文写成真的挺不容易,应该是现在网上少有的手把手教人理业务逻辑的博客了。看似一个小功能,却又这么多弯弯绕在里面,哈哈,没想到吧。

现在很多教程,包括很多视频课,都是一大堆技术给你安排上,动不动就商城啊,什么redis啊,ES啊,都给你整上。学了半天,看似学了很多,可往往都不精,成就了全栈HelloWolrd小能手。

很多人都跟我抱怨,为什么我学了那么多,乱七八糟的课程报了一大堆,商城系统也做了一堆,怎么去面试还是要被面试官diao呢,进了公司熬不过试用期就被开了?

那是因为,你还是没有养成真正的业务思维,思维还停留在学生时代,要别人把嚼碎了喂给你,缺少主动去钻研的精神。说白了,项目经验太少,不知道厂里到底要我干些什么?不要觉得会用MybatisPlus生成个Crud代码,会用redis简单的get/set,就能说精通了,现在行业内卷的时代,哪有这么简单。

可是你也许会说,不让我入职,我怎么积累项目经验?

看吧,又是死锁问题。

这些问题暴露出来,可能有网络上众多贩卖焦虑的因素在作怪,也有大环境的原因。

可是不论怎么样,我们都应该保持一颗平常心,多看,多练,不要一味地贪多,没必要达成各种helloworld成就。

看看现在视频网站上面各种脚手架的漂亮博客系统,收藏量和点赞量都高的吓人。

可是我个人觉得,这些对于真正提升自己的代码能力,帮助并不大。

一句话,真的,别被带歪了。

以上都属于我个人吐槽,仅代表我本人观点,欢迎一起交流讨论。本文也主要针对新入行的萌新小白,大佬轻喷。

Java新人易错:当修改遇到唯一_Test_05

网友评论