SpringBoot_MybatisPlus_02
承接第一篇的文章,我们一般在实际的开发中,都会从前端拿数据,而 SpringBoot 和前端做业务交互的时候,一般的数据格式为 Json。
那么接下来涉及到的:增,删,改,查的业务,都是在模拟前端的数据交互。
新增数据
使用框架底层新增方法,对应 controller
层代码。
需要注意的点:
- 主键的生成策略自增。
@TableId(type = AUTO )
- 实体类和数据库表关系的映射。
@TableName(value = "user")
可以参考的文章:链接地址
- 代码
@Controller
public class UserController {
@Autowired
private UserServerImpl userServer;
@PostMapping("/add")
// 如果不加这个注解,那么返回的就不是对应后端方法接受的数据
@ResponseBody
// 这里模拟前端传过来的数据,默认数据不只一条,那么就放在 LList<Map<String , Object>> listMap 里接受
// 前端传过来的参数使用 @RequestBody 接受一下
public void addUser(@RequestBody List<Map<String , Object>> listMap) {
// 先判断前端传过来的数据是不是空
if (listMap == null && listMap.isEmpty()) {
return;
}
// 因为数据可能不只一条,所以就要遍历循环
for (Map<String, Object> map: listMap) {
// 把数据转成 Json 格式
JSONObject jsonMap = new JSONObject(map);
// 把 Json 格式的数据封装成实体类型
User user = jsonMap.toJavaObject(User.class);
// 直接调用原生的方法保存
userServer.save(user);
}
}
}
Postman
测试情况
删除数据
删除数据采用的是逻辑删除,并不是正真意义上的删除。
第一步
- 新增数据库的字段,并赋值给一个默认值
ALTER TABLE `user` ADD deleted int(10) DEFAULT 0;
第二步
- 类中添加字段,并添加对应的注解
// 表示逻辑删除的注解
@TableLogic
private Integer deleted;
第三步
- 配置项中添加对应的配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
第四步
controller
层书写对应的代码
@DeleteMapping("/delete/{id}")
@ResponseBody
public void deleteUser(@PathVariable int id) {
userServer.removeById(id);
}
对应的请求 URL
格式为:http://localhost:8080/delete/1
,注意请求的方式为:delete
。
@DeleteMapping("/delete/id")
@ResponseBody
public void deleteUserByName(@RequestParam("id") int id) {
userServer.removeById(id);
}
对应的请求 URL
格式为:http://localhost:8080/delete/id?id=2
,注意请求的方式为:delete
。
需要注意的地方
更新数据
更新数据操作,有两个点需要注意:
- 自动填充字段
- 数据库乐观锁的版本号
自动填充字段
第一步
- 在数据库里添加两个时间字段
ALTER TABLE `user` ADD COLUMN `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ;
ALTER TABLE `user` ADD COLUMN `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间' ;
第二步
- 在实体类中添加对应的字段
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
第三步
- 自定义实现类
MyMetaObjectHandler
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("insert into start ...");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("update into start ......");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
数据库乐观锁的版本号
第一步
- 数据库里添加对应的字段
ALTER TABLE `user` ADD version int(10) DEFAULT 0;
第二步
- 实体类中添加对应的字段
@Version
private Integer version;
第三步
- 自定义
MybatisPlusConfig
@Configuration
// 这里扫描注解可以放在配置项中
@MapperScan("com.example.springboot_mybatisplus.mapper")
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
controller
层设计方法
这里没有使用框架提供的更新方法,而是根据实际的业务需要自定义了更新方法。
service
层接口层方法的声明
@Component
public interface UserServer extends IService<User> {
public void updateUserByIdAndObject(long id, User user);
}
service
层方法的实现
@Service
public class UserServerImpl extends ServiceImpl<UserMapper, User> implements UserServer {
@Autowired
private UserMapper userMapper;
@Override
public void updateUserByIdAndObject(long id, User user) {
if (user.getId() == null) {
user.setId(id);
}
userMapper.updateById(user);
}
}
controller
层方法的编写
@PutMapping("/update/{id}")
@ResponseBody
// 更新方法,根据传进来的 Id 和数据进行匹配更新
public void updateUser(@PathVariable long id, @RequestBody Map<String, Object> map) {
if (map == null && map.isEmpty()) {
return;
}
// 把数据转成 Json 格式
JSONObject jsonMap = new JSONObject(map);
// 把 Json 格式的数据封装成实体类型
User user = jsonMap.toJavaObject(User.class);
// 调用自定义的更新方法,但这个更新,对应版本号的值不会改变
// 版本号的更新仅支持 updateById(id) 与 update(entity, wrapper) 方法
// 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
userServer.updateUserByIdAndObject(id, user);
}
这里要更新的数据 Id 是直接通过地址传过来的。对应的测试情况为:
需要注意的地方
自动填充字段
版本号
另外就是:
- 版本号的更新仅支持 updateById(id) 与 update(entity, wrapper) 方法。
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
查询数据
如果说直接根据对应的 Id 查询到单条数据,那么直接调用框架提供的方法就可以了,而实际的业务场景中,往往需要根据某一个属性去查询对应的信息,而这个属性还不一定是 Id,并且查询的数据往往不止一条,那么这个时候,框架提供的方法就不能满足了。
自定义属性查询
第一步
- 在 Mapper 层自定义对应的方法
@Select("select * from user where name = #{name}")
public List<User> getUserByName(String name);
第二步
- 在 service 层接口中声明这个方法
public List<User> getUserByName(String name);
第三步
- 在 service 层实现这个方法
@Override
public List<User> getUserByName(String name) {
return userMapper.getUserByName(name);
}
第四步
- 在 controller 层实现调用
@GetMapping("/get/name")
@ResponseBody
// 采用 @RequestParam 接收参赛
// 由于考虑到查询到的数据可能不止一条,数据接收使用 List
public List<User> getUserByName(@RequestParam("name") String name) {
// 直接调用对应的方法即可
List<User> user = userServer.getUserByName(name);
return user;
}
第五步
- PostMan 的测试
除了这种,还有一种需要场景也是很常见的,那就是分页数据的查询。
分页查询数据_IPage
第一步
- 在 config 层的
MybatisPlusConfig
实现分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
第二步
- 在 mapper 层定义方法 (注意这里的方法返回值)
@Select("select * from user where name = #{name}")
public IPage<User> getUserByNamePage(Page<?> page, @Param("name") String name);
第三步
- 在 service 的接口中声明方法
public IPage<User> getUserByNamePage(Page<?> page, String name);
第四步
- 在 service 层中实现方法
@Override
public IPage<User> getUserByNamePage(Page<?> page, String name) {
return userMapper.getUserByNamePage(page, name);
}
第五步
- 在 controller 层中实现调用
@GetMapping("/get/page")
@ResponseBody
// 注意这里方法的返回值,是采用 IPage<User> 接收的,并指明了类型
public IPage<User> getUserByNamePage(@RequestParam("name") String name) {
// 直接调用对应的方法
IPage<User> userPage = userServer.getUserByNamePage(new Page<>(1, 4), name);
return userPage;
}
第六步
- 测试结果
需要注意的地方
- 方法的返回值是
IPage<User>
其中User
是对应的实体类。- 在 Mapper 层传参的时候,需要指定参数在数据库里对应的类型,例如下面这样的一种情况:
- 在使用
Page
这个方法的时候,controller
层直接使用即可。
分页查询数据_Pagehelper
第一步
- 在pom.xml 文件里引入对应的架包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
第二步
- 在 mapper 层定义方法 (注意这里的方法返回值)
List<DataArea> getAllPage(DataArea dataArea, @Param("page") PageRequestDataVO<DataArea> page);
第三步
- 在对应的 xml 层书写对应的 sql
SELECT
a.*,
CASE
WHEN ( a.id IN ( SELECT b.area_id FROM data_topic b WHERE b.deleted = 0 ) ) THEN
'1' ELSE '0'
END AS 'relation'
FROM
data_area a
WHERE
a.deleted = 0
<if test="dataArea.name != null and dataArea.name != '' ">
AND (a.area_mark LIKE concat('%', #{dataArea.name}, '%') or a.name LIKE concat('%', #{dataArea.name}, '%'))
</if>
<if test="page.startTime != null and page.startTime !='' and page.endTime != null and page.endTime !='' ">
and ( a.update_time BETWEEN #{page.startTime} AND #{page.endTime} )
</if>
ORDER BY
a.create_time DESC
第四步
- 在 service 的接口中声明方法
PageInfo<DataArea> getAllPage(PageRequestDataVO<DataArea> pageRequestDataVO);
第五步
- 在 service 层中实现方法
@Override
public PageInfo<DataArea> getAllPage(PageRequestDataVO<DataArea> pageRequestDataVO) {
// 注意这里是以对应实体类接收对应的参数
DataArea dataArea = pageRequestDataVO.getObj();
if (ObjectUtils.isEmpty(dataArea)) {
dataArea = new DataArea();
}
// 这里封装对应的查询页面参数
PageHelper.startPage(pageRequestDataVO.getPageNum(), pageRequestDataVO.getPageSize());
List<DataArea> allPage = topicAreaMapper.getAllPage(dataArea, pageRequestDataVO);
// 这里把返回的查询结果,封装成页面数据格式
return new PageInfo<>(allPage);
}
第六步
- 在 controller 层中实现调用
@PostMapping("/get")
public ResponseVoWithData getAllPage(@RequestBody PageRequestDataVO<DataArea> pageRequestDataVO) {
// 这里注意接收对应查询结果的实体类
PageInfo<DataArea> allPage = dataAreaService.getAllPage(pageRequestDataVO);
return ResponseVoWithData.success(allPage);
}
第七步
查询结果
- 请求参数
{ "pageNum": 1, "pageSize": 10, "obj": { "name": "监控" }, "startTime": "2021-12-22", "endTime": "2022-12-25" }
{
"result": 1,
"msg": "success",
"data": {
"total": 1,
"list": [
{
"id": 758,
"areaMark": "monitor",
"name": "监控",
"remark": null,
"operator": "admin",
"updateTime": "2022-01-14 11:33:56",
"relation": "1"
}
],
"pageNum": 1,
"pageSize": 10,
"size": 1,
"startRow": 1,
"endRow": 1,
"pages": 1,
"prePage": 0,
"nextPage": 0,
"isFirstPage": true,
"isLastPage": true,
"hasPreviousPage": false,
"hasNextPage": false,
"navigatePages": 8,
"navigatepageNums": [
1
],
"navigateFirstPage": 1,
"navigateLastPage": 1
}
}