1. Описание
Официальный сайт Mybatis-Plus:mp.baomidou.com/
Mybatis-Plus - это расширение расширения для фреймворка Mybatis.Согласно официальному описанию,MP только расширяет и не изменяет.Внедрение его не повлияет на существующие проекты,и оно гладкое как шелк.И его можно быстро выполняется с простой настройкой.Операции CRUD, что экономит много времени.Генерация кода, пейджинг, анализ производительности и другие функции доступны.Последняя версия была обновлена до 3.1.1.Серия 3.X поддерживает лямбда-синтаксис , что заставляет меня писать условные конструкции намного меньше.«Волшебное значение» , более лаконично из структуры кода.
2. Окружающая среда проекта
Версия MyBatis-Plus: 3.2.0
Версия SpringBoot: 2.2.7
Версия JDK: 1.8
Зависимости Maven следующие:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatisPlus 核心库 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 引入阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
Конфигурация выглядит следующим образом:
# 配置端口
server:
port: 8081
spring:
# 配置数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mp_student?useUnicode=true&characterEncoding=utf-8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
# mybatis-plus相关配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: auto
#字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"
field-strategy: NOT_EMPTY
#数据库类型
db-type: MYSQL
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 返回map时true:当查询数据为空时字段返回为null,false:不加这个查询数据为空时,字段将被隐藏
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Структура таблицы:
CREATE TABLE `user_info` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(32) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`skill` varchar(32) DEFAULT NULL COMMENT '技能',
`evaluate` varchar(64) DEFAULT NULL COMMENT '评价',
`fraction` bigint(11) DEFAULT NULL COMMENT '分数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';
CREATE TABLE `dept_info` (
`dept_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '部门ID',
`dept_name` varchar(64) NOT NULL COMMENT '部门名称',
`create_id` varchar(20) DEFAULT NULL COMMENT '创建者ID',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_id` varchar(20) DEFAULT NULL COMMENT '更新者ID',
`update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除 0:正常 1:删除',
PRIMARY KEY (`dept_id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4;
Данные таблицы:
INSERT INTO `user_info` VALUES (1, '小明', 20, '画画', '该学生在画画方面有一定天赋', 89);
INSERT INTO `user_info` VALUES (2, '小兰', 19, '游戏', '近期该学生由于游戏的原因导致分数降低了', 64);
INSERT INTO `user_info` VALUES (3, '张张', 18, '英语', '近期该学生参加英语比赛获得二等奖', 90);
INSERT INTO `user_info` VALUES (4, '大黄', 20, '体育', '该学生近期由于参加篮球比赛,导致脚伤', 76);
INSERT INTO `user_info` VALUES (5, '大白', 17, '绘画', '该学生参加美术大赛获得三等奖', 77);
INSERT INTO `user_info` VALUES (7, '小龙', 18, 'JAVA', '该学生是一个在改BUG的码农', 59);
INSERT INTO `user_info` VALUES (9, 'Sans', 18, '睡觉', 'Sans是一个爱睡觉,并且身材较矮骨骼巨大的骷髅小胖子', 60);
INSERT INTO `user_info` VALUES (10, 'papyrus', 18, 'JAVA', 'Papyrus是一个讲话大声、个性张扬的骷髅,给人自信、有魅力的骷髅小瘦子', 58);
INSERT INTO `user_info` VALUES (11, '删除数据1', 3, '画肖像', NULL, 61);
INSERT INTO `user_info` VALUES (12, '删除数据2', 3, NULL, NULL, 61);
INSERT INTO `user_info` VALUES (13, '删除数据3', 3, NULL, NULL, 61);
INSERT INTO `user_info` VALUES (14, '删除数据4', 5, '删除', NULL, 10);
INSERT INTO `user_info` VALUES (15, '删除数据5', 6, '删除', NULL, 10);
2. Напишите базовый класс
Добавьте аннотацию для сканирования DAO в классе запуска.
@SpringBootApplication
@MapperScan(basePackages = {"com.mp.demo.dao"}) //扫描DAO
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Написать класс конфигурации Config
/**
* @Description MybatisPlus配置类
* @Author Sans
* @CreateTime 2019/5/26 17:20
*/
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
Напишите класс Entity
/**
* @Description 学生信息实体类
* @Author Sans
* @CreateTime 2019/5/26 21:41
*/
@Data
@TableName("user_info")//@TableName中的值对应着表名
public class UserInfoEntity {
/**
* 主键
* @TableId中可以决定主键的类型,不写会采取默认值,默认值可以在yml中配置
* AUTO: 数据库ID自增
* INPUT: 用户输入ID
* ID_WORKER: 全局唯一ID,Long类型的主键
* ID_WORKER_STR: 字符串全局唯一ID
* UUID: 全局唯一ID,UUID类型的主键
* NONE: 该类型为未设置主键类型
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 技能
*/
private String skill;
/**
* 评价
*/
private String evaluate;
/**
* 分数
*/
private Long fraction;
}
Напишите класс Дао
/**
* @Description 用户信息DAO
* @Author Sans
* @CreateTime 2019/6/8 16:24
*/
public interface UserInfoDao extends BaseMapper<UserInfoEntity> {
}
Напишите класс обслуживания
/**
* @Description 用户业务接口
* @Author Sans
* @CreateTime 2019/6/8 16:26
*/
public interface UserInfoService extends IService<UserInfoEntity> {
}
Напишите класс ServiceImpl
/**
* @Description 用户业务实现
* @Author Sans
* @CreateTime 2019/6/8 16:26
*/
@Service
@Transactional
public class UserInfoSerivceImpl extends ServiceImpl<UserInfoDao, UserInfoEntity> implements UserInfoService {
}
3. Базовая демонстрация MyBatis-Plus
Здесь мы видим, что мы не писали никаких методов в сервисе, MyBatis-Plus официально инкапсулирует многие базовые методы CRUD, которые можно использовать напрямую, чтобы сэкономить много времени. , ServiceImpl и BaseMapper, а операция записи находится в ServiceImpl. Существуют привязки транзакций, здесь мы показываем некоторые общие методы для демонстрации.
/**
* @Description UserInfoController
* @Author Sans
* @CreateTime 2019/6/8 16:27
*/
@RestController
@RequestMapping("/userInfo")
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
/**
* 根据ID获取用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:34
* @Param userId 用户ID
* @Return UserInfoEntity 用户实体
*/
@RequestMapping("/getInfo")
public UserInfoEntity getInfo(String userId){
UserInfoEntity userInfoEntity = userInfoService.getById(userId);
return userInfoEntity;
}
/**
* 查询全部信息
* @Author Sans
* @CreateTime 2019/6/8 16:35
* @Param userId 用户ID
* @Return List<UserInfoEntity> 用户实体集合
*/
@RequestMapping("/getList")
public List<UserInfoEntity> getList(){
List<UserInfoEntity> userInfoEntityList = userInfoService.list();
return userInfoEntityList;
}
/**
* 分页查询全部数据
* @Author Sans
* @CreateTime 2019/6/8 16:37
* @Return IPage<UserInfoEntity> 分页数据
*/
@RequestMapping("/getInfoListPage")
public IPage<UserInfoEntity> getInfoListPage(){
//需要在Config配置类中配置分页插件
IPage<UserInfoEntity> page = new Page<>();
page.setCurrent(5); //当前页
page.setSize(1); //每页条数
page = userInfoService.page(page);
return page;
}
/**
* 根据指定字段查询用户信息集合
* @Author Sans
* @CreateTime 2019/6/8 16:39
* @Return Collection<UserInfoEntity> 用户实体集合
*/
@RequestMapping("/getListMap")
public Collection<UserInfoEntity> getListMap(){
Map<String,Object> map = new HashMap<>();
//kay是字段名 value是字段值
map.put("age",20);
Collection<UserInfoEntity> userInfoEntityList = userInfoService.listByMap(map);
return userInfoEntityList;
}
/**
* 新增用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:40
*/
@RequestMapping("/saveInfo")
public void saveInfo(){
UserInfoEntity userInfoEntity = new UserInfoEntity();
userInfoEntity.setName("小龙");
userInfoEntity.setSkill("JAVA");
userInfoEntity.setAge(18);
userInfoEntity.setFraction(59L);
userInfoEntity.setEvaluate("该学生是一个在改BUG的码农");
userInfoService.save(userInfoEntity);
}
/**
* 批量新增用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:42
*/
@RequestMapping("/saveInfoList")
public void saveInfoList(){
//创建对象
UserInfoEntity sans = new UserInfoEntity();
sans.setName("Sans");
sans.setSkill("睡觉");
sans.setAge(18);
sans.setFraction(60L);
sans.setEvaluate("Sans是一个爱睡觉,并且身材较矮骨骼巨大的骷髅小胖子");
UserInfoEntity papyrus = new UserInfoEntity();
papyrus.setName("papyrus");
papyrus.setSkill("JAVA");
papyrus.setAge(18);
papyrus.setFraction(58L);
papyrus.setEvaluate("Papyrus是一个讲话大声、个性张扬的骷髅,给人自信、有魅力的骷髅小瘦子");
//批量保存
List<UserInfoEntity> list =new ArrayList<>();
list.add(sans);
list.add(papyrus);
userInfoService.saveBatch(list);
}
/**
* 更新用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:47
*/
@RequestMapping("/updateInfo")
public void updateInfo(){
//根据实体中的ID去更新,其他字段如果值为null则不会更新该字段,参考yml配置文件
UserInfoEntity userInfoEntity = new UserInfoEntity();
userInfoEntity.setId(1L);
userInfoEntity.setAge(19);
userInfoService.updateById(userInfoEntity);
}
/**
* 新增或者更新用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:50
*/
@RequestMapping("/saveOrUpdateInfo")
public void saveOrUpdate(){
//传入的实体类userInfoEntity中ID为null就会新增(ID自增)
//实体类ID值存在,如果数据库存在ID就会更新,如果不存在就会新增
UserInfoEntity userInfoEntity = new UserInfoEntity();
userInfoEntity.setId(1L);
userInfoEntity.setAge(20);
userInfoService.saveOrUpdate(userInfoEntity);
}
/**
* 根据ID删除用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:52
*/
@RequestMapping("/deleteInfo")
public void deleteInfo(String userId){
userInfoService.removeById(userId);
}
/**
* 根据ID批量删除用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:55
*/
@RequestMapping("/deleteInfoList")
public void deleteInfoList(){
List<String> userIdlist = new ArrayList<>();
userIdlist.add("12");
userIdlist.add("13");
userInfoService.removeByIds(userIdlist);
}
/**
* 根据指定字段删除用户信息
* @Author Sans
* @CreateTime 2019/6/8 16:57
*/
@RequestMapping("/deleteInfoMap")
public void deleteInfoMap(){
//kay是字段名 value是字段值
Map<String,Object> map = new HashMap<>();
map.put("skill","删除");
map.put("fraction",10L);
userInfoService.removeByMap(map);
}
}
4. Условный конструктор QueryWrapper MyBatis-Plus
Когда условия запроса сложны, мы можем использовать конструктор условий MP, пожалуйста, обратитесь к следующему описанию параметра условия QueryWrapper.
режим запроса | Описание метода |
---|---|
setSqlSelect | Установить поля запроса SELECT |
where | Оператор WHERE, конкатенация + условие WHERE |
and | Оператор И, конкатенация + поле И = значение |
or | Оператор ИЛИ, конкатенация + поле ИЛИ=значение |
eq | равно = |
allEq | На основе содержимого карты равно = |
ne | не равно |
gt | больше чем> |
ge | Больше или равно >= |
lt | менее |
le | Меньше или равно |
like | Нечеткий запрос НРАВИТСЯ |
notLike | Нечеткий запрос НЕ НРАВИТСЯ |
in | IN запрос |
notIn | НЕ В запросе |
isNull | Запрос значения NULL |
isNotNull | IS NOT NULL |
groupBy | ГРУППА ПО |
having | НАЛИЧИЕ ключевых слов |
orderBy | СОРТИРОВАТЬ ПО |
orderByAsc | Сортировать по ASC ORDER BY |
orderByDesc | DESC сортировать ORDER BY |
exists | СУЩЕСТВУЕТ условное выражение |
notExists | НЕ СУЩЕСТВУЕТ условное выражение |
between | МЕЖДУ условным оператором |
notBetween | НЕ МЕЖДУ условным оператором |
addFilter | Бесплатный сплайсинг SQL |
last | Вставить в конце, например: last("LIMIT 1") |
Вот несколько распространенных примеров
/**
* @Description UserInfoPlusController
* @Author Sans
* @CreateTime 2019/6/9 14:52
*/
@RestController
@RequestMapping("/userInfoPlus")
public class UserInfoPlusController {
@Autowired
private UserInfoService userInfoService;
/**
* MP扩展演示
* @Author Sans
* @CreateTime 2019/6/8 16:37
* @Return Map<String,Object> 返回数据
*/
@RequestMapping("/getInfoListPlus")
public Map<String,Object> getInfoListPage(){
//初始化返回类
Map<String,Object> result = new HashMap<>();
//查询年龄等于18岁的学生
//等价SQL: SELECT id,name,age,skill,evaluate,fraction FROM user_info WHERE age = 18
QueryWrapper<UserInfoEntity> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.lambda().eq(UserInfoEntity::getAge,18);
List<UserInfoEntity> userInfoEntityList1 = userInfoService.list(queryWrapper1);
result.put("studentAge18",userInfoEntityList1);
//查询年龄大于5岁的学生且小于等于18岁的学生
//等价SQL: SELECT id,name,age,skill,evaluate,fraction FROM user_info WHERE age > 5 AND age <= 18
QueryWrapper<UserInfoEntity> queryWrapper2 = new QueryWrapper<>();
queryWrapper2.lambda().gt(UserInfoEntity::getAge,5);
queryWrapper2.lambda().le(UserInfoEntity::getAge,18);
List<UserInfoEntity> userInfoEntityList2 = userInfoService.list(queryWrapper2);
result.put("studentAge5",userInfoEntityList2);
//模糊查询技能字段带有"画"的数据,并按照年龄降序
//等价SQL: SELECT id,name,age,skill,evaluate,fraction FROM user_info WHERE skill LIKE '%画%' ORDER BY age DESC
QueryWrapper<UserInfoEntity> queryWrapper3 = new QueryWrapper<>();
queryWrapper3.lambda().like(UserInfoEntity::getSkill,"画");
queryWrapper3.lambda().orderByDesc(UserInfoEntity::getAge);
List<UserInfoEntity> userInfoEntityList3 = userInfoService.list(queryWrapper3);
result.put("studentAgeSkill",userInfoEntityList3);
//模糊查询名字带有"小"或者年龄大于18的学生
//等价SQL: SELECT id,name,age,skill,evaluate,fraction FROM user_info WHERE name LIKE '%小%' OR age > 18
QueryWrapper<UserInfoEntity> queryWrapper4 = new QueryWrapper<>();
queryWrapper4.lambda().like(UserInfoEntity::getName,"小");
queryWrapper4.lambda().or().gt(UserInfoEntity::getAge,18);
List<UserInfoEntity> userInfoEntityList4 = userInfoService.list(queryWrapper4);
result.put("studentOr",userInfoEntityList4);
//查询评价不为null的学生,并且分页
//等价SQL: SELECT id,name,age,skill,evaluate,fraction FROM user_info WHERE evaluate IS NOT NULL LIMIT 0,5
IPage<UserInfoEntity> page = new Page<>();
page.setCurrent(1);
page.setSize(5);
QueryWrapper<UserInfoEntity> queryWrapper5 = new QueryWrapper<>();
queryWrapper5.lambda().isNotNull(UserInfoEntity::getEvaluate);
page = userInfoService.page(page,queryWrapper5);
result.put("studentPage",page);
return result;
}
}
5. Пользовательский SQL
Внедрение Mybatis-Plus никак не повлияет на существующую архитектуру проекта Mybatis, а Mybatis-Plus поддерживает все собственные функции Mybatis, что является одной из причин, по которым мне нравится его использовать.Из-за сложности некоторых предприятий , нам, возможно, придется сделать это самим.Напишите несколько более сложных операторов SQL, мы приводим простой пример для демонстрации пользовательского SQL.
Пример: Запрос учащихся, чьи баллы выше установленного балла (баллы вводятся динамически и имеют разбиение на страницы)
Написать файл Mapper.xml
<mapper namespace="com.mp.demo.dao.UserInfoDao">
<!-- Sans 2019/6/9 14:35 -->
<select id="selectUserInfoByGtFraction" resultType="com.mp.demo.entity.UserInfoEntity" parameterType="long">
SELECT * FROM user_info WHERE fraction > #{fraction}
</select>
</mapper>
Добавить метод в DAO
/**
* 查询大于该分数的学生
* @Author Sans
* @CreateTime 2019/6/9 14:28
* @Param page 分页参数
* @Param fraction 分数
* @Return IPage<UserInfoEntity> 分页数据
*/
IPage<UserInfoEntity> selectUserInfoByGtFraction(IPage<UserInfoEntity> page, Long fraction);
Добавить метод в сервис
/**
* 查询大于该分数的学生
* @Author Sans
* @CreateTime 2019/6/9 14:27
* @Param page 分页参数
* @Param fraction 分数
* @Return IPage<UserInfoEntity> 分页数据
*/
IPage<UserInfoEntity> selectUserInfoByGtFraction(IPage<UserInfoEntity> page,Long fraction);
Добавить метод в serviceImpl
/**
* 查询大于该分数的学生
* @Author Sans
* @CreateTime 2019/6/9 14:27
* @Param page 分页参数
* @Param fraction 分数
* @Return IPage<UserInfoEntity> 分页数据
*/
@Override
public IPage<UserInfoEntity> selectUserInfoByGtFraction(IPage<UserInfoEntity> page, Long fraction) {
return this.baseMapper.selectUserInfoByGtFraction(page,fraction);
}
Тест в контроллере
/**
* MP自定义SQL
* @Author Sans
* @CreateTime 2019/6/9 14:37
* @Return IPage<UserInfoEntity> 分页数据
*/
@RequestMapping("/getInfoListSQL")
public IPage<UserInfoEntity> getInfoListSQL(){
//查询大于60分以上的学生,并且分页
IPage<UserInfoEntity> page = new Page<>();
page.setCurrent(1);
page.setSize(5);
page = userInfoService.selectUserInfoByGtFraction(page,60L);
return page;
}
6. Автозаполнение и надгробие
Плагины расширения MyBaits-plus обладают высокой функциональностью, такой как автоматическое заполнение, логическое удаление и оптимистическая блокировка и т. д., что позволяет уменьшить объем кода и улучшить читаемость во время разработки.
- Тестовый пример сущности
/**
* 部门信息实体类
* @Author Sans
* @CreateTime 2020/8/15 20:03
*/
@Data
@TableName("dept_info")
public class DeptInfoEntity {
/**
* ID主键
*/
@TableId(type = IdType.AUTO)
private Long deptId;
/**
* 部门名称
*/
private String deptName;
/**
* 自动填充属性
* FieldFill.INSERT 为插入时填充
* FieldFill.INSERT_UPDATE 插入和更新填充字段
* FieldFill.UPDATE 更新填充字段
* FieldFill.DEFAULT 默认不处理
*/
/**
* 创建者ID
*/
@TableField(fill = FieldFill.INSERT)
private String createId;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者ID
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateId;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 逻辑删除
* TableLogic为逻辑删除注解,如果在yml配置了全局逻辑删除字段,这个注解可以不加
* 注意: insert 官方推荐字段在数据库定义默认值
*/
@TableLogic
private Integer deleted;
}
- Конфигурация автозаполнения
/**
* MP自动填充Handler
* @Author Sans
* @CreateTime 2020/8/15 21:49
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入操作填充字段方法
*/
@Override
public void insertFill(MetaObject metaObject) {
/**
* 填充操作者/更新者
* DEMO中仅仅是举例,实际项目中这里其实可以根据业务进行判断
* 例如从 Shrio中的 SecurityUtils.getSubject().getPrincipal()获取当前用户信息填充
*/
this.strictInsertFill(metaObject, "createId", String.class,"ADMIN");
this.strictInsertFill(metaObject, "updateId", String.class, "ADMIN");
// 填充时间
Date date = new Date();
this.strictInsertFill(metaObject, "createTime", Date.class, date);
this.strictInsertFill(metaObject, "updateTime", Date.class, date);
}
/**
* 更新操作填充字段方法
*/
@Override
public void updateFill(MetaObject metaObject) {
Date date = new Date();
this.strictUpdateFill(metaObject, "updateId", String.class, "ADMIN");
this.strictUpdateFill(metaObject, "updateTime", Date.class, date);
}
}
7. Исходный код проекта
Исходный код проекта:git ee.com/Maroon Lotte/ это…
Лично мне очень нравится использовать MyBatis-Plus, это не только экономит время, но и код лаконичный и чистый, это дает мне ощущение перехода с SSM на SpringBoot в то время.
Ммм, это вкусно~
Спасибо за прочтение. Если вам понравилось, пожалуйста, поставьте лайк. Если статьи недостаточно, пожалуйста, дайте свои ценные комментарии.