需求
前端(VUE)页面上有一个可多选的select控件,前端以数组形式存储被选中的数据,但是数据库中不想新建表来存储,希望在主表中用以逗号为分隔符的字符串来存。
于是在存储和读取数据的时候就涉及到数据类型转换的问题:
- 存储时前端的数组需要转换成以逗号分隔的字符串给数据库;
- 读取时从数据库中读取的字符串数据需要转成数组给前端
针对1,可以在前端调用存储接口之前,就先转换成字符串,再作为参数传给存储接口;
针对2,可以在后端从数据库中读出来之后,转换成字符串,再返回给前端。
但是这样做的弊端,主要是1,前端每次调用后端接口的时候,都需要自行拼接一下字符串作为参数,我觉得不太方便,也不便于后期维护。
关于数据类型转换的思考:
- 前端的所有数据,其实都是字符串,大多数时候使用json格式传送。但是传给后台接口的时候,我们是在controller中用一个java对象来接收的;
- 数据库中的数据,是jdbc格式,但是同样,需要与1中的java对象互相转换。
基于这两个初步认知,简单学习了一下springboot和mybatis框架在数据转换中的作用(这就是框架的好处吧,不声不响地为我们做了很多事情)。
基础知识学习
springboot
在Spring Boot项目中,通常会按照一定的分层结构来组织代码和资源文件。
常见的Spring Boot项目目录分层结构:
根目录(Root)
- 包含项目配置文件(如pom.xml、build.gradle等)
- 可以包含一些通用的配置和资源文件
src目录(Source)
- main目录:包含主要的源代码和资源文件
- java目录:包含Java源代码
- com.example.application:包含应用程序的入口点和配置类
- com.example.controller:包含Web层控制器类
- com.example.service:包含业务逻辑和服务类
- com.example.repository:包含数据访问和持久化类
- com.example.entity:包含实体类
- resources目录:包含应用程序的资源文件(如配置文件、静态资源、模板文件等)
- java目录:包含Java源代码
test目录
- java目录:包含测试用例的Java源代码
- resources目录:包含测试用例的资源文件
target目录
- 包含编译后的代码和打包后的应用程序文件
分层的作用
这种分层结构的作用主要是为了将不同的逻辑功能模块划分清楚,有利于代码的维护和扩展。每一层都有其特定的职责和作用,例如:
- 应用程序入口点和配置类(com.example.application):负责初始化Spring Boot应用程序的各个组件,包括配置数据源、扫描并注册Bean、启动Web服务器等。
- Web层控制器类(com.example.controller):负责处理HTTP请求,调用业务逻辑和服务类,并返回响应结果给客户端。
- 业务逻辑和服务类(com.example.service):负责处理具体的业务逻辑,包括数据处理、业务规则验证、事务管理等。
- 实体类(com.example.entity):用于表示业务领域的对象,与数据库中的表结构对应。
通过合理的分层结构可以使代码结构更加清晰,便于团队开发和后期维护,同时也符合软件设计的开闭原则和单一职责原则。
前端数据与springboot java数据类型转换
从上面可见,在Spring Boot中,前端数据通常以JSON格式发送到后端,后端Web层控制器类(com.example.controller)中,根据使用的注解(比如@RequestBody)确定传参方式,并调用对应的内置类型转换器,来将前端传来的参数转换成相应的对象来接收这些数据(据说有124个内置转换器,在在 GenericConverter 接口的内部类 ConvertiblePair 中),但是,其中很重要的一点是,当我们使用我们自定义的实体类对象来接收的时候,会调用实体类中的set/get函数。
mybatis
前端数据转换成java对象,进行符合业务逻辑的处理之后,需要访问数据库进行长久化存储。同样,也需要从数据库中读取,传给前端进行展示。那么,与数据库交互部分,使用的就是mybatis,它同样需要使用前面实体类中的set/get来实现数据库jdbc格式的数据与java对象之间的转换。
思路整理
基于以上基本概念的了解,思路整理如下:
所以,焦点就在实体类Module中
1、定义一个与前端交互的的对象devIdList
2、定义一个与数据库交互的对象devIds
3、接收前端数据时,同时将值赋给devIds,以便传给数据库做存储
4、从数据库读取数据后,转成List格式并存储在devIdList,以便传给前端
实现
1、前端参数
form:{
docName:"",
devIdList:[]
}
2、controller中:使用实体类接收参数
@RestController
@PostMapping(value = "/add")
public R<String> add(@RequestBody Module module){
moduleService.AddModule(module);
return R.ok("success");
}
3、数据库中字段:
dev_ids | varchar(200) |
4、实体类定义(关键)
public class Module {
private List<String> devIdList; //前端数组字段
private String devIds; //数据库中字段
/*与前端交互时使用的get/set*/
public List<String> getDevIdList() {
return devIdList;
}
public void setDevIdList(List<String> devIdList) {
this.devIds = String.join(",",devIdList);
this.devIdList = devIdList;
System.out.println("接收前端数据");
}
/*与前端交互时使用的get/set*/
/*与数据库交互时使用的get/set*/
public String getDevIds() {
return devIds;
}
public void setDevIds(String devIds) {
this.devIds = devIds;
this.devIdList = Arrays.asList(devIds.split(","));
System.out.println("从数据库中读取数据");
}
/*与数据库交互时使用的get/set*/
}
总结
以上,从前端接收数据时,因前端字段名称为devIdList,会自动调用 setDevIdList,将接收到的数组转成字符串并赋值给Java变量devIds,这样在与数据库交互时,因数据库字段名为dev_ids,会自动调用getDevIds,获取到字符串变量devIds的值,并存储。
从数据库读取数据时,根据字段名称,调用setDevIds,将jdbc格式的数据转换成Java的数组变量devIdList,转换成给前端的响应报文时,将根据前端的变量名devIdList,调用getDevIdList,获取到数组变量的值。从而可以使数据库的字符串变量正确地转换成前端需要的数组,来进行业务逻辑处理。
经过这一些列的转换,前端的数组和后端的逗号分隔的字符串,就在读取和存储间自动完成了。前端在调用存取接口的时候,就不需要再额外地考虑类型转换的问题了。
这只是一个例子,通过这个例子,对springboot中的实体类有了更进一步的了解。