为了简化开发、提高效率,YouAsk 项目采用了 MyBatis-Plus 作为数据层的持久层框架,提供了主键自动生成、内置代码生成器、分页插件等强大功能。
项目地址:https://gitee.com/obsidian15/youask/tree/v0.2/
Tag:v2.0
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
YouAsk使用MyBatis Plus作为项目数据层的持久层框架,相比于MyBatis提供很多增强功能,比较有用的增强功能包括
- 支持主键自动生成
- 内置代码生成器
- 内置分页插件
- 内置全局拦截插件
这些功能在后续项目中都会用带,我也会逐一介绍如何开发和使用。
MyBaits-Plus 引用
- 父模块pom中增加mybatis-plus的引用
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.12</version>
</dependency>
- youask-admin-api项目的pom中增加mybatis-plus的引用,但是不需要指定version
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
- application.yaml文件中增加数据库连接配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/youask?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: p@ssword!
# mybatis-plus配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- YouAskAdminApiApplication.java启动类上增加MapperScan注释
package com.obsidian.youask;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.obsidian.youask.mapper")
public class YouAskAdminApiApplication {
public static void main(String[] args) {
SpringApplication.run(YouAskAdminApiApplication.class, args);
}
}
开发User数据库访问层和服务层
- 在介绍具体代码开发前,我先介绍一下API的层次结构
项目从上至下分为6层,分别为Web层、Controller层、Service层、Mapper层和数据库
- Web层是页面展示层
- Controller层提供对外的API访问请求,请求主要来自Web、App或者小程序
- Service是服务层,主要包含业务处理逻辑
- Mapper是数据持久层,主要生产SQL语句实现数据库操作
- Database是数据库,用于存储数据

我们将按照上面的分层,逐层搭建
- resources/mapper文件夹中增加UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.obsidian.youask.mapper.UserMapper">
</mapper>
MyBatis Plus需要编写mapper来实现数据库的操作,mapper由两部分组成,mapper接口定义和mapper的实现。其中mapper的实现可以直接在mapper接口定义的方法上,用注释(annotation)的方式定义,也可以使用mapper.xml的形式,使用xml格式定义。使用下来觉得,使用xml的方式虽然比较啰嗦,但是比较好用,直接写的是SQL语句,更加直观。个人比较推荐的做法是,一些常用的增、删、改和简单参数的查询直接用service定义即可,xml中主要包含复杂的查询,可以直接使用SQL语句实现。
- mapper文件夹中增加UserMapper.java
package com.obsidian.youask.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.obsidian.youask.model.entity.SysUser;
public interface UserMapper extends BaseMapper<SysUser> {
}
- service文件夹中增加IUserService.java接口
package com.obsidian.youask.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.obsidian.youask.model.entity.SysUser;
public interface IUserService extends IService<SysUser> {
}
- service/impl文件夹中增加UserService.java的实现
package com.obsidian.youask.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.obsidian.youask.model.entity.SysUser;
public interface IUserService extends IService<SysUser> {
}
- DBeaver连接MySQL数据,创建sys_user数据表
CREATETABLE`sys_user` (
`user_id`bigintNOT NULLAUTO_INCREMENT COMMENT '用户ID',
`user_name`varchar(20)NOT NULL COMMENT '用户账号',
`password`varchar(32)NOT NULL COMMENT '加密后密码',
`dept_id`bigintNOT NULL COMMENT '部门ID',
`employee_no`varchar(20)CHARACTERSET utf8mb4COLLATE utf8mb4_0900_ai_ciDEFAULT NULL COMMENT '工号',
`position`intDEFAULT NULL COMMENT '职位ID',
`name`varchar(50)DEFAULT NULL COMMENT '姓名',
`nick_name`varchar(50)NOT NULL COMMENT '用户昵称',
`email`varchar(50)DEFAULT NULL COMMENT '用户邮箱地址',
`phone_number`varchar(20)DEFAULT NULL COMMENT '手机号码',
`gender`intDEFAULT NULL COMMENT '性别',
`avatar`varchar(100)DEFAULT NULL COMMENT '用户头像URL',
`last_login_time`datetimeDEFAULT NULL COMMENT '最后登陆时间',
`last_login_ip`varchar(15)DEFAULT NULL COMMENT '最后登陆IP',
`pwd_last_update_time`datetimeDEFAULT NULL COMMENT '密码最后更新时间',
`status`intDEFAULT NULL COMMENT '状态',
`is_deleted`intNOT NULLDEFAULT '0' COMMENT '是否删除',
`updated_by`bigintDEFAULT NULL COMMENT '更新人ID',
`updated_at`datetimeDEFAULT NULL COMMENT '更新时间',
`created_by`bigintNOT NULL COMMENT '创建人ID',
`created_at`datetimeNOT NULLDEFAULTCURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARYKEY (`user_id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci;
- 修改UserController中的getAll方法,实现从sys_user表中获取所有数据
@Autowired
private IUserService userService;
@GetMapping()
public YaResult<List<SysUser>> getAll() {
return YaResult.success(userService.list());
}
@Autowired是 Spring 框架中的一个注解,主要用于实现依赖注入(Dependency Injection, DI)。它的核心作用是自动装配,即让 Spring 容器自动为类中的属性、构造方法或 setter 方法注入所需的依赖对象,无需手动创建和赋值。
简单说就是对象不需要自己new了,SpringBoot帮助我们自动new了个对象,比如这里的IUserService是个接口,具体的实现是UserService,SpringBoot就会自动用UserService类来实例化。如果IUserService这个接口有多个类实现了,可以使用@Primary,@Qualifier来指定具体使用哪个实现。
- 启动项目调试,在数据库里增加几条数据,在OpenAPI页面中打开getAll方法,在调试面板中点击“发送”,正常情况下就能看到从数据库获取到的结果。

这样从API获取请求,到数据库之间的打通了。
实现逻辑删除
从MyBatis输出的日志中可以看到实际生成的查询语句。

生成的SQL语句比较简单,主要是字段,没有包含where语句。从User实际应用来看,至少应该包含2个默认的过滤
- is_deleted=0,is_deleted=1表示记录逻辑删除
- tenant_id租户过滤,这个默认应该获取当前登陆用户所在的租户,并添加where过滤(后续会加)
逻辑删除有2种实现方法,第一种是使用mybatis的QueryWrapper实现,手动增加is_deleted字段不等于0的判断,这样做的问题是所有的查询都需要添加这样语句。第二种实现方法是使用mybatis plus提供的逻辑删除。
逻辑删除是一种优雅的数据管理策略,它通过在数据库中标记记录为“已删除”而非物理删除,来保留数据的历史痕迹,同时确保查询结果的整洁性。MyBatis-Plus 提供了便捷的逻辑删除支持,使得这一策略的实施变得简单高效。
Mybatis Plus实现逻辑删除也很简单,在YaEntity类中增加@TableLogic的注释。0表示记录正常,1表示记录删除。
/**
* 删除标志
* 0:正常
* 1:删除
*/
@Schema(description = "逻辑删除标志")
@TableLogic(value = "0", delval = "1")
private Integer isDeleted;
在OpenAPI上重新发送请求,再看日志,可以发现增加了WHERE is_deleted=0,说明逻辑删除生效了。

实现添加和更新
- UserController中增加add方法用户新增用户
@PostMapping()
public YaResult<SysUser> add(@Valid @RequestBody SysUser user) {
if (userService.save(user)) {
return YaResult.success(user);
}
return YaResult.failed("Add user failed");
}
- 浏览器打开OpenAPI页面,定位到add方法,在body中输入完整的体
{
"status": 0,
"isDeleted": 0,
"createdBy": 0,
"createdAt": "",
"updatedBy": 0,
"updatedAt": "",
"userId": 0,
"userName": "ammey",
"password": "123567",
"deptId": 0,
"employeeNo": "",
"position": 0,
"nickName": "",
"name": "",
"gender": 1,
"email": "",
"phoneNumber": "",
"avatar": "",
"lastLoginTime": "",
"lastLoginIp": "",
"pwdLastUpdateTime": ""
}
查看请求的SQL语句,发现辅助字段,比如status、isDeleted、createdBy、createdAt这些字段,如果再请求时不填写,新建对象是会报错的。我们可以用Mybatis Plus的自动填充字段功能,在insert或者update时,自动填充辅助字段。
- 在YaEntity类中,需要使用
@TableField注解来标记哪些字段需要自动填充,并指定填充的策略。
@Data
public abstract class YaEntity implements Serializable {
/**
* 状态
* 0:停用
* 1:正常
*/
@Schema(description = "状态")
@TableField(fill = FieldFill.INSERT)
private Integer status;
/**
* 删除标志
* 0:正常
* 1:删除
*/
@Schema(description = "逻辑删除标志")
@TableLogic(value = "0", delval = "1")
@TableField(fill = FieldFill.INSERT)
private Integer isDeleted;
/**
* 创建人ID
*/
@Schema(description = "创建人ID")
@TableField(fill = FieldFill.INSERT)
private Integer createdBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdAt;
/**
* 更新人ID
*/
@Schema(description = "更新人ID")
@TableField(fill = FieldFill.UPDATE)
private Integer updatedBy;
/**
* 更新时间
*/
@Schema(description = "更新时间")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updatedAt;
}
- config文件夹中创建EntityMetaObjectHandler.java,主要用来指定每个字段分别在insert和update的时候如何
package com.obsidian.youask.config;
import java.time.LocalDateTime;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
@Component
public class EntityMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "status", Integer.class, 1);
this.strictInsertFill(metaObject, "isDeleted", Integer.class, 0);
this.strictInsertFill(metaObject, "createdBy", Long.class, 1L);
this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "updatedBy", Long.class, 1L);
this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
}
}
- 重新发送请求,删除请求数据中的status、isDeleted、createdBy、createdAt、updatedBy、updatedAt字段,可以看到生成的SQL语句中自动填充了数据。
{
"userId": 0,
"userName": "ammey",
"password": "123567",
"deptId": 0,
"employeeNo": "",
"position": 0,
"nickName": "",
"name": "",
"gender": 1,
"email": "",
"phoneNumber": "",
"avatar": "",
"lastLoginTime": "",
"lastLoginIp": "",
"pwdLastUpdateTime": ""
}

实现queryByCondition方法
之所以把queryByCondition方法单独提出来讲,主要是这个方法相对来说比较复杂,它不包含具体业务(因为这个是通用方法,按照输入的查询条件,生成SQL的WHERE查询条件),但是流程覆盖从controller到service到mapper到database。
queryByCondition方法的作用是根据参数中的condition,自动拼接where条件,实现复杂条件的查询,主要用在列表页面的展示和查询功能上。
- resources/mapper文件夹中创建EntityMapper.xml,对应YaEntity.java对象
EntityMapper中的resultMap和Sql都是作为其他mapper的引用,并不直接执行,这个也是我觉得mapper.xml比较灵活好用的地方。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.obsidian.youask.mapper.EntityMapper">
<resultMap id="EntityMap" type="com.obsidian.youask.model.YaEntity">
<result column="status" property="status" />
<result column="is_deleted" property="isDeleted" />
<result column="created_by" property="createdBy" />
<result column="created_at" property="createdAt" />
<result column="updated_by" property="updatedBy" />
<result column="updated_at" property="updatedAt" />
</resultMap>
<sql id="entityCondtion">
<if test="condition.status != null">
and status = #{condition.status}
</if>
<if test="condition.statuses != null and condition.statuses.size() > 0">
and status in
<foreach collection="condition.statuses" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="condition.createdBy != null">
and created_by = #{condition.createdBy}
</if>
<if test="condition.createUserIds != null and condition.createUserIds.size() > 0">
and created_by in
<foreach collection="condition.createUserIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="condition.startCreateTime != null">
and created_at >= #{condition.startCreateTime}
</if>
<if test="condition.endCreateTime != null">
and created_at < #{condition.endCreateTime}
</if>
<if test="condition.updatedBy != null">
and updated_by = #{condition.updatedBy}
</if>
<if test="condition.updateUserIds != null and condition.updateUserIds.size() > 0">
and updated_by in
<foreach collection="condition.updateUserIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="condition.startUpdateTime != null">
and updated_at >= #{condition.startUpdateTime}
</if>
<if test="condition.endUpdateTime != null">
and updated_at < #{condition.endUpdateTime}
</if>
</sql>
</mapper>
EntityMapper定义了通用的查询条件,包活以下的字段:
- status - 单状态查询
- statuses - 状态列表查询
- createdBy - 单创建人Id查询
- createUserIds - 创建人Id列表查询
- startCreateTime,endCreateTime - 开始、结束创建时间查询
- updatedBy - 单更新人Id查询
- updateUserIds - 更新人Id列表查询
- startUpdateTime,endUpdateTime - 开始、结束更新时间查询
- resources/mapper文件夹中创建UserMapper.xml,对应SysUser.java对象
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.obsidian.youask.mapper.UserMapper">
<resultMap id="UserMap"
extends="com.obsidian.youask.mapper.EntityMapper.EntityMap"
type="com.obsidian.youask.model.entity.SysUser">
<id column="user_id" property="userId"/>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
<result column="dept_id" property="deptId"/>
<result column="employee_no" property="employeeNo"/>
<result column="position" property="position"/>
<result column="nick_name" property="nickName"/>
<result column="name" property="name"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<result column="phone_number" property="phoneNumber"/>
<result column="avatar" property="avatar"/>
<result column="last_login_time" property="lastLoginTime"/>
<result column="last_login_ip" property="lastLoginIp"/>
<result column="pwd_last_update_time" property="pwdLastUpdateTime"/>
</resultMap>
<select id="selectByCondition" resultMap="UserMap" parameterType="java.util.Map">
select * from sys_user where is_deleted = 0
<if test="condition.userId != null">
and user_id = #{condition.userId}
</if>
<if test="condition.userIds != null and condition.userIds.size() > 0">
and user_id in
<foreach collection="condition.userIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="condition.userName != null and condition.userName != ''">
and user_name like concat('%', #{condition.userName}, '%')
</if>
<if test="condition.deptId != null">
and dept_id = #{condition.deptId}
</if>
<if test="condition.deptIds != null and condition.deptIds.size() > 0">
and dept_id in
<foreach collection="condition.deptIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<include refid="com.obsidian.youask.mapper.EntityMapper.EntityCondtion" />
</select>
</mapper>
- UserMapper中的resultMap从EntityMap扩展
- UserMapper中selectByCondition引用了EntityMap的EntityCondtion
- IUserMapper中增加selectByCondition方法的定义
package com.obsidian.youask.mapper;
import java.util.Map;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.obsidian.youask.model.entity.SysUser;
public interface UserMapper extends BaseMapper<SysUser> {
IPage<SysUser> selectByCondition(IPage<SysUser> page, Map<String, Object> condition);
}
IUserMapper定义了selectByCondition方法,输入参数和输出参数都为IPage对象,IPage对象是MyBatis Plus的内部模块,用于在数据库查询时提供分页能力。
MyBatis-Plus 的分页插件
PaginationInnerInterceptor提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。
这里的条件参数使用了Map<String, Object>类型,主要是因为后续mapper希望通过Mybatis Generator自动生成,根据不同表生成Condition对象不是很方便,所以这里选择了直接使用map方式。在很多SpringBoot项目标准中还是不建议使用这种方式的。
- IUserService的改造
package com.obsidian.youask.service;
import java.util.Map;
import com.baomidou.mybatisplus.extension.service.IService;
import com.obsidian.youask.model.YaPageResult;
import com.obsidian.youask.model.entity.SysUser;
public interface IUserService extends IService<SysUser> {
public YaPageResult<SysUser> queryByCondition(Map<String, Object> condition, Integer pageIndex, Integer pageSize);
}
- UserServiceImpl的改造
package com.obsidian.youask.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.obsidian.youask.mapper.UserMapper;
import com.obsidian.youask.model.YaPageResult;
import com.obsidian.youask.model.entity.SysUser;
import com.obsidian.youask.service.IUserService;
import java.util.Map;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, SysUser> implements IUserService {
@Override
public YaPageResult<SysUser> queryByCondition(Map<String, Object> condition, Integer pageIndex, Integer pageSize) {
IPage<SysUser> page = new Page<>(pageIndex, pageSize);
IPage<SysUser> result = baseMapper.selectByCondition(page, condition);
return YaPageResult.success(result.getRecords(), pageIndex, pageSize, result.getTotal());
}
}
- UserController改造
@PostMapping("query")
public YaPageResult<SysUser> queryByCondition(@RequestBody Map<String, Object> condition,
@RequestParam(defaultValue = YaConst.DEFAULT_PAGE_INDEX) Integer pageIndex,
@RequestParam(defaultValue = YaConst.DEFAULT_PAGE_SIZE) Integer pageSize) {
return userService.queryByCondition(condition, pageIndex, pageSize);
}

在父项目pom项目中增加mybatis-plus-jsqlparser的引用
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>3.5.12</version>
</dependency>
在youask-admin-api项目增加mybatis-plus-jsqlparser的引用
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>
config目录中创建MybatisPlusConfig配置文件
package com.obsidian.youask.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
@Configuration
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
- 调试queryByCondition方法
在浏览器中打开OpenAPI页面,定位到queryByCondition方法,在URL中增加pageIndex和pageSize参数
/users/query?pageIndex=1&pageSize=10
设置body参数
{
"userIds": [1,2]
}
点击发送后可以看到查询结果,其中包含了page对象,保存了分页信息

我们到后台看一下生成的SQL语句,可以看到查询条件user_id in 已经包含了,并且包含了LIMIT的限制用于实现分页。

完善UserController的方法
package com.obsidian.youask.admin.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.obsidian.youask.admin.YaConst;
import com.obsidian.youask.admin.service.IUserService;
import com.obsidian.youask.common.model.YaPageResult;
import com.obsidian.youask.common.model.YaResult;
import com.obsidian.youask.common.model.YaResultCode;
import com.obsidian.youask.common.model.entity.SysUser;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ApiSupport(author = YaConst.AUTHOR)
@Tag(name = "UserController", description = "用户服务")
@Slf4j
@RestController
@RequestMapping("users")
public class UserController {
@Autowired
private IUserService userService;
/**
* 获取所有用户
* @return 用户列表
*/
@GetMapping()
public YaResult<List<SysUser>> getAll() {
return YaResult.success(userService.list());
}
/**
* 根据用户ID获取用户详情
* @param userId 用户ID
* @return 用户详情
*/
@GetMapping("{userId}")
public YaResult<SysUser> getById(@PathVariable Long userId) {
return YaResult.success(userService.getById(userId));
}
/**
* 根据条件查询用户列表
* @param condition 查询条件对象
* @return 用户列表
*/
@PostMapping("query")
public YaPageResult<SysUser> queryByCondition(@RequestBody Map<String, Object> condition,
@RequestParam(defaultValue = YaConst.DEFAULT_PAGE_INDEX) Integer pageIndex,
@RequestParam(defaultValue = YaConst.DEFAULT_PAGE_SIZE) Integer pageSize) {
return userService.queryByCondition(condition, pageIndex, pageSize);
}
/**
* 添加用户
* @param user 待添加的用户对象
* @return 添加后的用户对象
*/
@PostMapping()
public YaResult<SysUser> add(@Valid @RequestBody SysUser user) {
if (userService.save(user)) {
return YaResult.success(user);
}
return YaResult.failed(YaConst.MSG_TMPL_ADD_FAILED, "user");
}
/**
* 根据用户ID更新用户信息
* @param userId 用户ID
* @param user 待更新的用户对象
* @return 更新后的用户对象
*/
@PutMapping("{userId}")
public YaResult<SysUser> modifyById(@PathVariable Long userId, @RequestBody SysUser user) {
user.setUserId(userId);
if (userService.updateById(user)) {
return YaResult.success(user);
}
return YaResult.failed(YaConst.MSG_TMPL_UPDATE_FAILED, "user", userId);
}
/**
* 停用用户
* @param userId 用户ID
* @return 更新后的用户对象
*/
@PutMapping("{userId}/deactive")
public YaResult<SysUser> deactivateById(@PathVariable Long userId) {
return updateStatus(userId, YaConst.STATUS_DEACTIVE);
}
/**
* 激活用户
* @param userId 用户ID
* @return 更新后的用户对象
*/
@PutMapping("{userId}/active")
public YaResult<SysUser> activateById(@PathVariable Long userId) {
return updateStatus(userId, YaConst.STATUS_ACTIVE);
}
/**
* 更新用户状态
* @param userId 用户ID
* @param status 状态
* @return 更新后的用户对象
*/
private YaResult<SysUser> updateStatus(Long userId, Integer status) {
SysUser user = userService.getById(userId);
if (user == null) {
return YaResult.failed(YaResultCode.NOT_FOUND, YaConst.MSG_TMPL_NOT_FOUND, "User", userId);
}
user.setStatus(status);
if (userService.updateById(user)) {
return YaResult.success(user);
}
return YaResult.failed(YaConst.MSG_TMPL_UPDATE_FAILED, "user", userId);
}
/**
* 根据用户ID删除用户
* @param userId 用户ID
* @return 删除结果
*/
@DeleteMapping("{userId}")
public YaResult<Boolean> removeById(@PathVariable Long userId) {
if (userService.removeById(userId)) {
return YaResult.success(true);
}
return YaResult.failed(YaConst.MSG_TMPL_DELETE_FAILED, "user", userId);
}
}
一般的Controller中会包含一些通用的API,这些API不包含具体的业务逻辑,是针对数据库中数据的CRUD操作,包括以下方法:
- getAll - 获取所有(非删除)数据
- getById - 根据Id获取数据
- queryByCondition - 根据条件获取数据
- add - 添加数据
- modifyById - 根据Id修改数据
- activateById - 根据Id启用数据
- deactivateById - 根据Id停用数据
- removeById - 根据Id删除数据
这说明一下我自己的一个命名规则:
Controller层、Service层更贴近业务,把业务数据看成是列表的形式,CRUD的命名使用add、remove、modify、get或者query。get表达简单条件,可以包含缓存,获取开销较低;query需要有复杂查询,获取开销较高;
Mapper层贴近数据库,是数据库的抽象层,CRUD的命名使用insert、delete、update和select;
方法命名会包含一些特定的格式,比如:
XXXById - 根据Id(主键)进行操作
XXXByCode - 根据Code进行操作
XXXByCondition - 根据复杂条件进行操作
XXXAsVO - 返回数据转换为VO对象
XXXWithXXX - 返回复杂的嵌套对象
后续工作
考虑到从API到数据库中的层次较多,同时数据表也会很多,每张数据表都需要这样逐层开发将会很费时间,所有我们会使用MyBatis Code Generator来自动生成这些模块话的代码。
YouAsk系列目录:

2477

被折叠的 条评论
为什么被折叠?



