最近在写一个小项目练手,需要自己同时搞前后端。此项目里前端采用了Vue3+Element Plus+JS(TS实在是驾驭不来),后端采用了Spring Boot,在此记录一下遇到的各种坑和解决办法,以备再遇到时可以有参考,省得满世界找资料看代码👿
前端部分
el-checkbox绑定值的问题
我在数据库里对实体对象的状态(status)定义是一个tinyint,其有三个值:
- 0:已禁用,但未删除
- 1:已启用
- 2:已禁用,且已删除
在使用el-checkbox来映射状态时,一开始单纯使用
<el-checkbox :checked="item.status === 1" />
结果就导致了大问题,在使用前端接口向后端接口传递数据的时候,本来应该是0和1的状态值,变成了false和true,直接导致后端报错。
后来查文档看到,el-checkbox有两个属性值,分别是true-lable和false-lable,可以定义复选框对应的值。把原来的代码改成:
<el-checkbox :checked="item.status === 1" true-lable=1 false-lable=0 />
这样就可以保证后端传来的状态数据可以被正常渲染,而前端的勾选状态传递给后端的时候也是正确的数值类型,而不是布尔类型。
<el-input>的数据类型问题
<el-input>
很坑的一点在于,无论为type属性设置任何值(number之类的),它在最终提交的时候都是会提交string类型,这就导致了前后端数据类型的不一致。
虽然理论上不会有影响,因为后端在接收过程中如果接收到的是String类型的数据,是会自动强转为实体类中定义的数据类型(比如我定义的price变量是一个Double对象,而前端给我的却是一个String类型数据“1”,SpringBoot是会帮我自动将其强转为Double),但是这确实破坏了前后端的一致性。
因此,可以在<el-input>
的v-model
属性值后面加上.number
声明这里的数据是number类型,如:
<el-input v-model.number="newObj.price" placeholder="这里的数据是number类型"/>
不是什么大问题,记一下,以后再看到前端传过来奇怪的数据类型知道什么情况就行
后端部分
Controller接收MultipartFile的变量名问题
文件上传的前端接口采用了multipart/form-data类型,因此后端的其中一个Contoller如下
@PostMapping("/image") public Result upload(MultipartFile file /**这里是重点!!!*/) throws IOException { log.info("接收到了image文件,文件名:{}", file.getOriginalFilename()); String url = aliyunOSS.uploadImage(file); log.info("文件上传到了:{}", url); return Result.success(url); }
在PostMan中向接口上传文件没有任何报错,后端能正常处理并返回文件存储的url,但是前端无论如何都无法上传。经过排查,发现前端在<el-upload>
标签内为上传文件指定了name
属性为image
。
因此,前端发送过来的载荷实际如下:
------WebKitFormBoundaryhfyAqEe0VyjiZvQv Content-Disposition: form-data; name="image";/** 重点就是这里 */ filename="Snipaste_2023-04-02_19-17-32.png" Content-Type: image/png ------WebKitFormBoundaryhfyAqEe0VyjiZvQv--
在传送载荷的时候,把<el-upload>
的属性名作为了图片文件的key,而我在后端习惯性地定义为了“file”,这就导致后续的处理报出空指针异常。因此,只需要让前端传送文件时携带的key和后端接收的变量名保持一致即可。
接口在设计之初就是要各司其职,不同的接口处理不同的文件类型,因此我选择把后端代码中的MultipartFile file
改为MultipartFile image
,问题解决。
SQL语句中的内连接和左连接
在我的数据库结构里有三张表,分别是商品表(commodity),图片Hash表(image_hash)以及用于记录商品图片的中间表(commodity_image)
这样就可以实现一样商品同时对应多张图片了。
起初我使用的是内连接查询,可以实现查询商品基本信息的同时把商品对应的图片封装进实体类,sql语句如下:
<select id="getByIds" resultMap="commodity_images"> select c.*,ih.url as images from commodity c join commodity_image ci on c.id = ci.commodityId join image_hash ih on ci.imageId = ih.id where d.id in <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </select>
起初测试的时候没有任何问题,因为我测试的商品都是有添加图片的,后端可以正常返回封装了图片数组的商品类回来。但是当我打开一个没有添加图片的商品准备测试其他前端功能时突然报错:“没有查询到相关内容”。
这时候我意识到,没有添加图片的商品是查询不到的。然后就开始了“转脑子模式”:我是不是要用if-else
语句对select
后面的所有sql语句进行限制啊?当中间表里没有该商品ID记录的时候就不选取ih.url
了?这样的话就得开启一个新事务,在事务里blah blah……
写到一半突然脑子灵光了:对啊,我用的是内连接啊,换成左连接不就行了?何必费劲吧啦的?被自己蠢哭了要。之前的全部代码都不用大改,只需要把内连接[inner] join
换成left join
就OK了。左连接的意义就在于右边(后边)的表没有查询到结果并不会影响左边(前边)的表正常输出内容啊。。。
<select id="getByIds" resultMap="commodity_images"> select c.*,ih.url as images from commodity c left join commodity_image ci on c.id = ci.commodityId left join image_hash ih on ci.imageId = ih.id where d.id in <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </select>
搞定,即便是商品没有关联图片,商品的基本信息也可以正常输出。犯蠢+1
发表回复