SpringBoot_MybatisPlus_02


SpringBoot_MybatisPlus_02

承接第一篇的文章,我们一般在实际的开发中,都会从前端拿数据,而 SpringBoot 和前端做业务交互的时候,一般的数据格式为 Json。

那么接下来涉及到的:增,删,改,查的业务,都是在模拟前端的数据交互。

新增数据

使用框架底层新增方法,对应 controller 层代码。

需要注意的点:

  1. 主键的生成策略自增。@TableId(type = AUTO )
  2. 实体类和数据库表关系的映射。@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 测试情况

image-20211030213115926

删除数据

删除数据采用的是逻辑删除,并不是正真意义上的删除。

第一步

  • 新增数据库的字段,并赋值给一个默认值
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

需要注意的地方

image-20211024120110627

更新数据

更新数据操作,有两个点需要注意:

  1. 自动填充字段
  2. 数据库乐观锁的版本号

自动填充字段

第一步

  • 在数据库里添加两个时间字段
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 是直接通过地址传过来的。对应的测试情况为:

image-20211031112839734

需要注意的地方

自动填充字段

image-20211024115945302

版本号

image-20211024120026246

另外就是:

  1. 版本号的更新仅支持 updateById(id) 与 update(entity, wrapper) 方法。
  2. 在 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 的测试

image-20211031115003448

除了这种,还有一种需要场景也是很常见的,那就是分页数据的查询。

分页查询数据_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;
}

第六步

  • 测试结果

image-20211031121452145

需要注意的地方

  1. 方法的返回值是 IPage<User> 其中 User 是对应的实体类。
  2. 在 Mapper 层传参的时候,需要指定参数在数据库里对应的类型,例如下面这样的一种情况:img
  3. 在使用 Page 这个方法的时候,controller 层直接使用即可。img

分页查询数据_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
    }
}

文章作者: L Q
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 L Q !
  目录