历史文章(文章累计490+)
《国内最全的Spring Boot系列之一》
《国内最全的Spring Boot系列之二》
《国内最全的Spring Boot系列之三》
《国内最全的Spring Boot系列之四》
《国内最全的Spring Boot系列之五》
《国内最全的Spring Boot系列之六》
SpringBoot集成MyBatis-Plus + MyBatis-Plus代码生成器[MP系列] - 第490篇
MyBatis-Plus主键生成策略[MyBatis-Plus系列] - 第491篇
MyBatis-Plus实现逻辑删除[MyBatis-Plus系列] - 492篇
悟纤:师傅,你觉得生命的本质是什么?
师傅:生命的本质是能量
师傅:喜怒哀乐都是能量
师傅:话语是能量的载体
师傅:平和的语气,合适的词语,都会安抚心灵。
师傅:如果话语太锋利了,就会影响对方的情绪和心情,也就是能量紊乱。
悟纤:那两个人相处之道,不就是需要注意使用平和的语气和用合适的词语来表达了?
师傅:平和的语气能够让人听着很舒服,情绪不容易上头。
师傅:不同的词语虽然都能够表达清楚意思,但用合适的词语,听起来让人觉得舒服的词语进行表达,能够让人在心里上更加愿意去接受。
悟纤:听师傅一席话,胜读十年书。
导读
Hi,大家好,我是悟纤。过着爱谁谁的生活,活出不设限的人生。
通常在开发中,有这样的需求:枚举类型存入数据库存的是编码code,然而返回给前端的时候是名称name,我们每次入库的时候都要getCode()以及返回给前端的时候要getName(),很繁琐,并且字段属于那种枚举类型的可读性也不高
基于以上问题:我们会尝试着定制一些逻辑专门去处理,一般是自定义枚举转换器实现,然而mybatis-plus提供了优雅的实现方式。
一、枚举的使用场景和好处
在实际的使用当中,当某个对象或者某个属性,需要有多个可供选择的状态或者描述,例如人的性别,支付的状态,错误的类型等等,都可以使用枚举。
好处:
(1)可读性高, 易理解。
(2)统一参数类型,避免传参错误。
(3)线程安全,全局唯一,无法修改。
二、版本区别
Mybatis-Plus 不同的版本,通用枚举配置是不一样的,稍早一些的需要实现 IEnum 接口,并且需要在配置文件中配置 typeEnumsPackage 或者编写配置类,这难免有些复杂。
而 Mybatis-Plus 从 3.5.2 版本开始只需使用 @EnumValue 注解枚举属性,简单来说就是一个注解解决了一系列配置,本文也将讲解 @EnumValue 注解枚举属性这种方式!
三、通用枚举实战
接下来用具体的例子看一下mybatis-plus通用枚举的使用。
3.1定义枚举
3.1.1方式1:@EnumValue标注入库映射字段
使用注解@EnumValue定义存储到数据库的值:
package com.kfit.user.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* author:悟纤「公众号SpringBoot」
* date:2023/9/15
*/
public enum GradeEnum {
PRIMARY(1, "小学"),
SECONDORY(2, "中学"),
HIGH(3, "高中");
@EnumValue//标记数据库存的值是code
private final int code;
@JsonValue //标注该字段要开启自定义序列化返回值
private final String desc;
GradeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}
说明:注解@JsonValue注解是开启序列化返回的值。
3.1.2方式2:枚举属性实现 IEnum 接口
实现接口IEnum定义存储到数据库的值:
package com.kfit.user.enums;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* author:悟纤「公众号SpringBoot」
* date:2023/9/15
*/
public enum AgeEnum implements IEnum<Integer> {
ONE(1, "一岁"),
TWO(2, "二岁"),
THREE(3, "三岁");
private int value;
@JsonValue //标注该字段要开启自定义序列化返回值
private String desc;
AgeEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
// @Override
// public String toString() {
// return this.desc;
// }
}
说明:上面两种方式定义的枚举都是可以的,使用注解@EnumValue在使用起来会更简单一些。
3.2 在实体类中使用枚举
在需要的实体类中使用上面定义的枚举,这里重新创建一个实体类:
package com.kfit.user.model;
import com.kfit.user.enums.AgeEnum;
import com.kfit.user.enums.GradeEnum;
import lombok.Data;
/**
* author:悟纤「公众号SpringBoot」
* date:2023/9/15
*/
@Data
public class Student {
private Long id;
private String name;
/**
* 年龄,IEnum接口的枚举处理
* 数据库字段:age INT(3)
*/
private AgeEnum age;
/**
* 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
* 数据库字段:grade INT(2)
*/
private GradeEnum grade;
}
3.3 定义Mapper
由于实体类是新的,定义个Mapper进行数据库的操作,如果是在原实体添加的忽略这一个步骤:
package com.kfit.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kfit.user.model.Student;
/**
* author:悟纤「公众号SpringBoot」
* date:2023/9/15
*/
public interface StudentMapper extends BaseMapper<Student> {
}
3.4 在表中添加对应的列
在表中添加对应的列,这里实体类是新的,需要创建一个表:
CREATE TABLE student (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NULL,
`age` int(3) NULL,
`grade` int(2) NULL,
PRIMARY KEY (`id`)
);
3.5 后端测试
接下来进行简单的后端测试。
3.5.1 保存测试
先来看下保存数据的测试:
@Autowired
private StudentMapper studentMapper;
@Test
public void testInsert(){
Student student = new Student();
student.setName("张三");
student.setAge(AgeEnum.ONE);
student.setGrade(GradeEnum.HIGH);
studentMapper.insert(student);
}
执行结果:
3.5.2 修改测试
看下修改:
@Test
public void testUpdate(){
Student student = new Student();
student.setId(1L);
student.setName("李四");
student.setAge(AgeEnum.TWO);
student.setGrade(GradeEnum.SECONDORY);
studentMapper.updateById(student);
}
运行结果:
3.5.3 查询测试
看下返回的数据情况:
@Test
public void testSelctById(){
Student student = studentMapper.selectById(1);
System.out.println(student);
}
运行结果:
这里显示的希望是中文的描述的话,那么需要重写AgeEnum和GradeEnum的toString()方法:
@Override
public String toString() {
return this.desc;
}
这时候在运行一下:
3.6 前端测试
最后在进行一下前端测试,就是从前端请求到控制层,然后进行操作。
3.6.1 定义一个controller
首先定义controller:
package com.kfit.user.controller;
import com.kfit.user.mapper.StudentMapper;
import com.kfit.user.model.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* author:悟纤「公众号SpringBoot」
* date:2023/9/15
*/
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentMapper studentMapper;
}
3.6.2 查询测试
进行查询测试:
@RequestMapping("/select")
public Student selectStudent(){
return studentMapper.selectById(1);
}
请求地址:
http://127.0.0.1:8080/student/select
请求结果:
结果显示很正常,如果显示的不是中文的话,那么看看有没有在属性上添加了@JsonValue的注解,如果使用的是其它的JSON框架的话,那么对应的是什么注解。
3.6.3 保存测试
编写保存测试代码:
@RequestMapping("/save")
public int save(@RequestBody Student student){
return studentMapper.insert(student);
}
请求地址:
http://127.0.0.1:8080/student/save
请求体:
{
"name": "wuqian",
"age": "二岁",
"grade": "高中"
}
请求结果:
除了 "age": "二岁","age": 1 也能达到相同的效果,需要注意的是:"age": 1 对枚举类的要求苛刻,需要保证枚举数字从0开始并按顺序排列,因为它是按顺序取枚举的。
也就是说:
设置age=0,那么对应的是ONE(1, "一岁");
设置age=1,那么对应的是TWO(2, "二岁");
如果要使用这种方式,最好是code从0开始,并且是顺序排列的,不然可能会出现莫名其妙的错误。
3.6.4 保存测试2
上面保存是使用的JSON的方式,如果使用get请求地址这样的请求呢?
@RequestMapping("/save1")
public int save1(Student student){
return studentMapper.insert(student);
}
请求地址:
http://127.0.0.1:8080/student/save1?name=wuqian&age=二岁
请求报错:
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'student' on field 'age': rejected value [二岁]; codes [typeMismatch.student.age,typeMismatch.age,typeMismatch.com.kfit.user.enums.AgeEnum,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.age,age]; arguments []; default message [age]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.kfit.user.enums.AgeEnum' for property 'age'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [com.kfit.user.enums.AgeEnum] for value [二岁]; nested exception is java.lang.IllegalArgumentException: No enum constant com.kfit.user.enums.AgeEnum.二岁]]
所以这种方式不能够这样子请求,可以这样子请求:
http://127.0.0.1:8080/student/save1?name=wuqian&age=ONE
请求结果:
那能不能传递value值呢 ?这个就需要重写StringToEnumConverterFactory的,可以自行去了解一下。
小结
本节介绍了MP的通用枚举功能,对于本文的知识重点总结一下:
(1)通用枚举定义的两种方式:其一使用注解@EnumValue;其二实现接口IEnum。
(2)后端测试想要返回对应的描述,可以重写toString()方法。
(3)前端测试想要返回对应的描述,可以添加注解@JsonValue。
(4)如果请求方式是json的方式,那么可以直接进行转换。
(5)如果请求方式是x-www-form-urlencoded,那么要使用name的方式,否则要重写类StringToEnumConverterFactory。
「1000道互联网Java工程师面试题」
包括了:MyBatis、ZK、Dubbo、EL、Redis、MySQL、并发编程、Java面试、Spring、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题(共 485 页,32W字)
领取方式:关注公众号「SpringBoot」,回复[面试资料]
👍 点赞、转发、评论,伸出你的双手666…
🐜i 你就是你,不一样的小蚂蚁!