超详细,一篇让你学会SpringBoot集成MyBatisPlus

SpringBoot集成MyBatisPlus
MyBatis的概念
  • MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具库,它简化了 MyBatis 的开发流程,提供了更多的便捷功能和增强特性。MyBatis-Plus 并不是替代 MyBatis,而是在 MyBatis 的基础上进行扩展和增强。
  • 以下是 MyBatis-Plus 提供的一些主要功能和概念:
    • CRUD 操作支持:MyBatis-Plus 提供了丰富的 CRUD 操作方法,可以通过简单的 API 完成数据库的增删改查操作。
    • 条件构造器(Wrapper):MyBatis-Plus 提供了强大的条件构造器,可以通过编程方式构建 SQL 查询条件,包括等于、不等于、模糊查询、排序等。
    • 分页查询:MyBatis-Plus 支持简单的分页查询,可以轻松地进行分页操作。
    • 代码生成器:MyBatis-Plus 提供了一个代码生成器,可以根据数据库表结构自动生成实体类、Mapper 接口和 XML 映射文件,减少手写重复代码的工作量。
    • 通用 Service 层封装:MyBatis-Plus 还提供了通用的 Service 层封装,简化了 Service 的编写和操作。
    • Lambda 表达式支持:MyBatis-Plus 支持使用 Lambda 表达式进行查询条件的构建,使得代码更加简洁和易读。
    • 自动填充:MyBatis-Plus 支持自动填充功能,在插入或更新数据时,可以自动填充指定的字段,如创建时间、更新时间等。
  • 总之,MyBatis-Plus 是一个功能强大、提高开发效率的 MyBatis 增强工具库,通过提供丰富的功能和便捷的 API,简化了数据库操作和查询条件构造的过程。它能够极大地简化开发人员的工作量,并提供更好的开发体验。
相关常用注解
  • @Table:表名注解,标识实体类对应的表
    • 相关属性:
      • value:执行表名
      • schema:用于指定数据库的 schema(模式)名称。Schema 是数据库中的一个概念,它可以用来将数据库对象(如表、视图、存储过程等)进行逻辑上的分类和隔离。不同的数据库系统对 schema 的支持和用法可能会有所不同。
        当 schema 参数被指定时,MyBatis-Plus 会将对应的实体类与指定的 schema 下的数据库表进行映射。这在多个 schema 下存在相同表名的情况下非常有用,可以避免表名冲突的问题。
      • keepGlobalPrefix:是否保持全局的表前缀配置。默认为 false。
      • resultMap:指定该实体类使用的结果映射(ResultMap)的 id。
      • autoResultMap:是否自动生成结果映射(ResultMap)。默认为 true。
        • MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上)
        • 因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。
      • prefix:表前缀,用于生成动态表名。如果设置了 prefix,生成的 SQL 语句中会将表名替换为 ${prefix}your_table_name 的形式。
      • suffix:表后缀,用于生成动态表名。如果设置了 suffix,生成的 SQL 语句中会将表名替换为 your_table_name${suffix} 的形式。
      • comment:该表的注释信息。
  • @TableId:标识一个字段为表的主键,主要用于字段上
    • 相关属性:
      • value:指定主键字段名
      • type:指定主键类型
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • 其他相关注解及用法参考:https://baomidou.com/pages/223848/#orderby
MyBatisPlus中常用的CRUD接口
  • Service层中的CRUD,通用 Service CRUD 封装IService (opens new window)接口
    • Save
      • // 插入一条记录(选择字段,策略插入)
        • boolean save(T entity);
      • // 插入(批量)
        • boolean saveBatch(Collection entityList);
      • // 插入(批量)
        • boolean saveBatch(Collection entityList, int batchSize);
    • SaveOrUpdate
      • // TableId 注解存在更新记录,否插入一条记录
        • boolean saveOrUpdate(T entity);
      • // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
        • boolean saveOrUpdate(T entity, Wrapper updateWrapper);
      • // 批量修改插入
        • boolean saveOrUpdateBatch(Collection entityList);
      • // 批量修改插入
        • boolean saveOrUpdateBatch(Collection entityList, int batchSize);
    • Remove
      • // 根据 queryWrapper 设置的条件,删除记录
        • boolean remove(Wrapper queryWrapper);
      • // 根据 ID 删除
        • boolean removeById(Serializable id);
      • // 根据 columnMap 条件,删除记录
        • boolean removeByMap(Map<String, Object> columnMap);
      • // 删除(根据ID 批量删除)
        • boolean removeByIds(Collection<? extends Serializable> idList);
    • Update
      • // 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
        • boolean update(Wrapper updateWrapper);
      • // 根据 whereWrapper 条件,更新记录
        • boolean update(T updateEntity, Wrapper whereWrapper);
      • // 根据 ID 选择修改
        • boolean updateById(T entity);
      • // 根据ID 批量更新
        • boolean updateBatchById(Collection entityList);
      • // 根据ID 批量更新
        • boolean updateBatchById(Collection entityList, int batchSize);
    • Get
      • // 根据 ID 查询
        • T getById(Serializable id);
      • // 根据 Wrapper—(实体对象封装操作类 QueryWrapper),查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last(“LIMIT 1”)
        • T getOne(Wrapper queryWrapper);
      • // 根据 Wrapper,查询一条记录
        • T getOne(Wrapper queryWrapper, boolean throwEx);
      • // 根据 Wrapper,查询一条记录
        • Map<String, Object> getMap(Wrapper queryWrapper);
      • // 根据 Wrapper,查询一条记录
        • // V getObj(Wrapper queryWrapper, Function<? super Object, V> mapper);
    • List
      • // 查询所有
        • List list();
      • // 查询列表
        • List list(Wrapper queryWrapper);
      • // 查询(根据ID 批量查询)
        • Collection listByIds(Collection<? extends Serializable> idList);
      • // 查询(根据 columnMap 条件)
        • Collection listByMap(Map<String, Object> columnMap);
      • // 查询所有列表
        • List<Map<String, Object>> listMaps();
      • // 查询列表
        • List<Map<String, Object>> listMaps(Wrapper queryWrapper);
      • // 查询全部记录
        • List listObjs();
      • // 查询全部记录
        • // List listObjs(Function<? super Object, V> mapper);
      • // 根据 Wrapper 条件,查询全部记录
        • List listObjs(Wrapper queryWrapper);
      • // 根据 Wrapper 条件,查询全部记录
        • // List listObjs(Wrapper queryWrapper, Function<? super Object, V> mapper);
    • Page
      • // 无条件分页查询
        • IPage page(IPage page);
      • // 条件分页查询
        • IPage page(IPage page, Wrapper queryWrapper);
      • // 无条件分页查询
        • IPage<Map<String, Object>> pageMaps(IPage page);
      • // 条件分页查询
        • IPage<Map<String, Object>> pageMaps(IPage page, Wrapper queryWrapper);
    • Count
      • // 查询总记录数
        • int count();
      • // 根据 Wrapper 条件,查询总记录数
        • int count(Wrapper queryWrapper);
    • query
      • // 链式查询 普通
        • QueryChainWrapper query();
      • // 链式查询 lambda 式。注意:不支持 Kotlin
        • LambdaQueryChainWrapper lambdaQuery();
        • // 示例:
          query().eq(“column”, value).one();
          lambdaQuery().eq(Entity::getId, value).list();
    • update
      • // 链式更改 普通
        • UpdateChainWrapper update();
      • // 链式更改 lambda 式。注意:不支持 Kotlin
        • LambdaUpdateChainWrapper lambdaUpdate();
        • // 示例:
          update().eq(“column”, value).remove();
          lambdaUpdate().eq(Entity::getId, value).update(entity);
  • Mapper层的CRUD,通用 CRUD 封装BaseMapper (opens new window)接口
    • Insert
      • // 插入一条记录
        • int insert(T entity);
    • Delete
      • // 根据 entity 条件,删除记录
        • int delete(@Param(Constants.WRAPPER) Wrapper wrapper);-- 实体对象封装操作类(可以为 null)
      • // 删除(根据ID 批量删除)
        • int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
      • // 根据 ID 删除
        • int deleteById(Serializable id);
      • // 根据 columnMap 条件,删除记录
        • int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    • Update
      • // 根据 whereWrapper 条件,更新记录
        • int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper whereWrapper);
      • // 根据 ID 修改
        • int updateById(@Param(Constants.ENTITY) T entity);
    • Select
      • // 根据 ID 查询
        • T selectById(Serializable id);
      • // 根据 entity 条件,查询一条记录
        • T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper);-- 实体对象封装操作类(可以为 null)
      • // 查询(根据ID 批量查询)
        • List selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
      • // 根据 entity 条件,查询全部记录
        • List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper);
      • // 查询(根据 columnMap 条件)
        • List selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
      • // 根据 Wrapper 条件,查询全部记录
        • List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper queryWrapper);
      • // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
        • List selectObjs(@Param(Constants.WRAPPER) Wrapper queryWrapper);
      • // 根据 entity 条件,查询全部记录(并翻页)
        • IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
      • // 根据 Wrapper 条件,查询全部记录(并翻页)
        • IPage<Map<String, Object>> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
      • // 根据 Wrapper 条件,查询总记录数
        • Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper);
ActiveRecord 模式
  • 实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 需要项目中已注入对应实体的BaseMapper,不使用 mapper,也需要定义对应的mapper类,Mybatis-Plus通过 mapper 获取到表的结构;
  • 如果实体类继承了 Model 类,那么在对应的 mapper 类中定义的方法会覆盖 Model 类中同名的方法。因此,如果你在 mapper 类中定义了一个与 Model 类中同名的方法,那么最终调用时会执行 mapper 类中的方法,而不是 Model 类中的方法。
class User extends Model<User>{
    // fields...
}
User user = new User();
user.insert();
user.selectAll();
user.updateById();
user.deleteById();
// ...
SimpleQuery 工具类
  • 对selectList查询后的结果用Stream流进行了一些封装,使其可以返回一些指定结果,简洁了api的调用
  • 需要项目中已注入对应实体的BaseMapper
  • keyMap
    • // 查询表内记录,封装返回为Map<属性,实体>
      • Map<A, E> keyMap(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, Consumer… peeks);
    • // 查询表内记录,封装返回为Map<属性,实体>,考虑了并行流的情况
      • Map<A, E> keyMap(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer… peeks);
  • map
    • // 查询表内记录,封装返回为Map<属性,属性>
      • Map<A, P> map(LambdaQueryWrapper wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, Consumer… peeks);
    • // 查询表内记录,封装返回为Map<属性,属性>,考虑了并行流的情况
      • Map<A, P> map(LambdaQueryWrapper wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, boolean isParallel, Consumer… peeks);
  • group(sFunction-封装后list中的元素\downstream-下游收集器\isParallel-为true时底层使用并行流执行\peeks-可叠加的后续操作)
    • // 查询表内记录,封装返回为Map<属性,List<实体>>
      • Map<K, List> group(LambdaQueryWrapper wrapper, SFunction<T, A> sFunction, Consumer… peeks);
    • // 查询表内记录,封装返回为Map<属性,List<实体>>,考虑了并行流的情况
      • Map<K, List> group(LambdaQueryWrapper wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer… peeks);
    • // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>
      • M group(LambdaQueryWrapper wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, Consumer… peeks);
    • // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>,考虑了并行流的情况
      • M group(LambdaQueryWrapper wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, boolean isParallel, Consumer… peeks);
  • list(sFunction-封装后list中的元素\isParallel-为true时底层使用并行流执行\peeks-可叠加的后续操作)
    • // 查询表内记录,封装返回为List<属性>
      • List list(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, Consumer… peeks);
    • // 查询表内记录,封装返回为List<属性>,考虑了并行流的情况
      • List list(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer… peeks);
class SimpleQueryTest extends BaseDbTest<EntityMapper> {

    @Test
    void testList() {
        // 我要这张表里的ids
        List<Long> entityIds = SimpleQuery.list(lambdaQuery(), Entity::getId);
        assertThat(entityIds).containsExactly(1L, 2L);

        // 可叠加后续操作
        List<String> names = SimpleQuery.list(lambdaQuery(), Entity::getName,
            e -> Optional.ofNullable(e.getName())
                .map(String::toUpperCase)
                .ifPresent(e::setName));
        assertThat(names).containsExactly("RUBEN", null);
    }

    @Test
    void testMap() {
        // 我要这个表里对应条件的用户,用id作为key给我一个map
        Map<Long, Entity> idEntityMap = SimpleQuery.keyMap(
            Wrappers.<Entity>lambdaQuery().eq(Entity::getId, 1L), Entity::getId);
        // 校验结果
        Entity entity = new Entity();
        entity.setId(1L);
        entity.setName("ruben");
        Assert.isTrue(idEntityMap.equals(Collections.singletonMap(1L, entity)), "Ops!");

        // 如果我只想要id和name组成的map
        Map<Long, String> idNameMap = SimpleQuery.map(lambdaQuery(), Entity::getId, Entity::getName);
        // 校验结果
        Map<Long, String> map = new HashMap<>(1 << 2);
        map.put(1L, "ruben");
        map.put(2L, null);
        Assert.isTrue(idNameMap.equals(map), "Ops!");
    }

    @Test
    void testGroup() {
        // 我需要相同名字的用户的分为一组,再造一条数据
        doTestAutoCommit(m -> {
            Entity entity = new Entity();
            entity.setId(3L);
            entity.setName("ruben");
            m.insert(entity);
        });

        // 简单查询
        Map<String, List<Entity>> nameUsersMap = SimpleQuery.group(lambdaQuery(), Entity::getName);

        // 校验结果
        Map<String, List<Entity>> map = new HashMap<>(1 << 2);
        Entity chao = new Entity();
        chao.setId(2L);
        chao.setName(null);
        map.put(null, Collections.singletonList(chao));

        Entity ruben = new Entity();
        ruben.setId(1L);
        ruben.setName("ruben");
        Entity ruben2 = new Entity();
        ruben2.setId(3L);
        ruben2.setName("ruben");
        map.put("ruben", Arrays.asList(ruben, ruben2));
        Assert.isTrue(nameUsersMap.equals(map), "Ops!");

        // 解锁高级玩法:
        // 获取Map<name,List<id>>
        Map<String, List<Long>> nameIdMap = SimpleQuery.group(lambdaQuery(), Entity::getName,
            mapping(Entity::getId, toList()));
        assertThat(nameIdMap).containsExactly(entry(null, Arrays.asList(2L)), entry("ruben", Arrays.asList(1L, 3L)));

        // 获取Map<name,个数>
        Map<String, Long> nameCountMap = SimpleQuery.group(lambdaQuery(), Entity::getName, counting());
        assertThat(nameCountMap).containsExactly(entry(null, 1L), entry("ruben", 2L));
        // ...超多花样
    }

    @Override
    protected String tableDataSql() {
        return "insert into entity(id,name) values(1, 'ruben'), (2, null);";
    }

    @Override
    protected List<String> tableSql() {
        return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" +
            "id BIGINT NOT NULL," +
            "name VARCHAR(30) NULL DEFAULT NULL," +
            "PRIMARY KEY (id))");
    }

}
Db类
  • 使用静态调用的方式,执行CRUD方法,避免Spring环境下Service循环注入、简洁代码,提升效率
  • 基本使用:
    • // 根据id查询
      • List list = Db.listByIds(Arrays.asList(1L, 2L), Entity.class);
    • // 根据条件构造器查询
      • List list = Db.list(Wrappers.lambdaQuery(Entity.class));
    • // 批量根据id更新
      • boolean isSuccess = Db.updateBatchById(list);
class DbTest extends BaseDbTest<EntityMapper> {

    @Test
    void testSave() {
        Entity entity = new Entity();
        entity.setName("ruben");
        boolean isSuccess = Db.save(entity);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(3L, Db.count(Entity.class));
    }

    @Test
    void testSaveBatch() {
        List<Entity> list = Arrays.asList(new Entity(), new Entity());
        boolean isSuccess = Db.saveBatch(list);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(4, Db.count(Entity.class));
    }

    @Test
    void testSaveOrUpdateBatch() {
        Entity entity = new Entity();
        entity.setId(1L);
        entity.setName("cat");
        List<Entity> list = Arrays.asList(new Entity(), entity);
        boolean isSuccess = Db.saveOrUpdateBatch(list);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(3, Db.count(Entity.class));
    }

    @Test
    void testRemoveById() {
        Entity entity = new Entity();
        entity.setId(1L);
        boolean isSuccess = Db.removeById(entity);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(1, Db.count(Entity.class));
        isSuccess = Db.removeById(2L, Entity.class);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(0, Db.count(Entity.class));
    }

    @Test
    void testUpdateById() {
        Entity entity = new Entity();
        entity.setId(1L);
        entity.setName("bee bee I'm a sheep");
        boolean isSuccess = Db.updateById(entity);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals("bee bee I'm a sheep", Db.getById(1L, Entity.class).getName());
    }

    @Test
    void testUpdate() {
        boolean isSuccess = Db.update(Wrappers.lambdaUpdate(Entity.class).eq(Entity::getId, 1L).set(Entity::getName, "be better"));
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals("be better", Db.getById(1L, Entity.class).getName());

        Entity entity = new Entity();
        entity.setId(1L);
        entity.setName("bee bee I'm a sheep");
        isSuccess = Db.update(entity, Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L));
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals("bee bee I'm a sheep", Db.getById(1L, Entity.class).getName());
    }

    @Test
    void testUpdateBatchById() {
        Entity sheep = new Entity();
        sheep.setId(1L);
        sheep.setName("bee bee I'm a sheep");

        Entity ruben = new Entity();
        ruben.setId(2L);
        ruben.setName("rabbit");
        boolean isSuccess = Db.updateBatchById(Arrays.asList(sheep, ruben));
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals("bee bee I'm a sheep", Db.getById(1L, Entity.class).getName());
        Assertions.assertEquals("rabbit", Db.getById(2L, Entity.class).getName());
    }

    @Test
    void testRemove() {
        boolean isSuccess = Db.remove(Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L));
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(1, Db.count(Entity.class));
    }

    @Test
    void testRemoveByIds() {
        boolean isSuccess = Db.removeByIds(Arrays.asList(1L, 2L), Entity.class);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(0, Db.count(Entity.class));
    }

    @Test
    void testRemoveByMap() {
        boolean isSuccess = Db.removeByMap(Collections.singletonMap("id", 1L), Entity.class);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals(1, Db.count(Entity.class));
    }

    @Test
    void testSaveOrUpdate() {
        Entity entity = new Entity();
        entity.setId(null);
        entity.setName("bee bee I'm a sheep");
        boolean isSuccess = Db.saveOrUpdate(entity);
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals("bee bee I'm a sheep", Db.getById(entity.getId(), Entity.class).getName());

        entity.setName("be better");
        isSuccess = Db.saveOrUpdate(entity, Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, entity.getId()));
        Assertions.assertTrue(isSuccess);
        Assertions.assertEquals("be better", Db.getById(entity.getId(), Entity.class).getName());
    }

    @Test
    void testGetOne() {
        LambdaQueryWrapper<Entity> wrapper = Wrappers.lambdaQuery(Entity.class);
        Assertions.assertThrows(TooManyResultsException.class, () -> Db.getOne(wrapper));
        Entity one = Db.getOne(wrapper, false);
        Assertions.assertNotNull(one);
        Entity entity = new Entity();
        entity.setId(1L);
        one = Db.getOne(entity);
        Assertions.assertNotNull(one);
    }

    @Test
    void testListByMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("id", 1L);
        List<Entity> list = Db.listByMap(map, Entity.class);
        Assertions.assertEquals(1, list.size());
        Assertions.assertEquals("ruben", list.get(0).getName());
    }

    @Test
    void testByIds() {
        List<Entity> list = Db.listByIds(Arrays.asList(1L, 2L), Entity.class);
        Assertions.assertEquals(2, list.size());
    }

    @Test
    void testGetMap() {
        Map<String, Object> map = Db.getMap(Wrappers.lambdaQuery(Entity.class));
        Assertions.assertNotNull(map);

        Entity entity = new Entity();
        entity.setId(1L);
        map = Db.getMap(entity);
        Assertions.assertNotNull(map);
    }

    @Test
    void testList() {
        List<Entity> list = Db.list(Wrappers.lambdaQuery(Entity.class));
        Assertions.assertEquals(2, list.size());

        list = Db.list(Entity.class);
        Assertions.assertEquals(2, list.size());

        Entity entity = new Entity();
        entity.setId(1L);
        list = Db.list(entity);
        Assertions.assertEquals(1, list.size());
    }

    @Test
    void testListMaps() {
        List<Map<String, Object>> list = Db.listMaps(Wrappers.lambdaQuery(Entity.class));
        Assertions.assertEquals(2, list.size());

        list = Db.listMaps(Entity.class);
        Assertions.assertEquals(2, list.size());

        Entity entity = new Entity();
        entity.setId(1L);
        list = Db.listMaps(entity);
        Assertions.assertEquals(1, list.size());
    }

    @Test
    void testListObjs() {
        List<Entity> list = Db.listObjs(Entity.class);
        Assertions.assertEquals(2, list.size());

        List<Long> objectList = Db.listObjs(Wrappers.lambdaQuery(Entity.class), Entity::getId);
        Assertions.assertEquals(2, objectList.size());

        List<String> names = Db.listObjs(Entity.class, Entity::getName);
        Assertions.assertArrayEquals(new String[]{"ruben", "chocolate"}, names.toArray());
    }

    @Override
    protected List<Interceptor> interceptors() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQLITE));
        return Collections.singletonList(interceptor);
    }

    @Test
    void testPageMaps() {
        Page<Map<String, Object>> page = Db.pageMaps(new Page<>(1, 1), Entity.class);
        Assertions.assertEquals(2, page.getTotal());

        Assertions.assertEquals(Db.listMaps(new Page<>(1, 1, false), Entity.class).size(), page.getRecords().size());

        page = Db.pageMaps(new Page<>(1, 1), Wrappers.lambdaQuery(Entity.class));
        Assertions.assertEquals(1, page.getRecords().size());

        Assertions.assertEquals(Db.listMaps(new Page<>(1, 1, false), Wrappers.lambdaQuery(Entity.class)).size(), page.getRecords().size());
    }

    @Test
    void testPage() {
        IPage<Entity> page = Db.page(new Page<>(1, 1), Entity.class);
        Assertions.assertEquals(2, page.getTotal());
        Assertions.assertEquals(Db.list(new Page<Entity>(1, 1), Entity.class).size(),page.getRecords().size());

        page = Db.page(new Page<>(1, 1), Wrappers.lambdaQuery(Entity.class));
        Assertions.assertEquals(1, page.getRecords().size());

        Assertions.assertEquals(Db.list(new Page<Entity>(1, 1), Wrappers.lambdaQuery(Entity.class)).size(),page.getRecords().size());
    }

    @Test
    void testChain() {
        QueryChainWrapper<Entity> query = Db.query(Entity.class);
        List<Entity> list = query.eq("id", 1L).list();
        Assertions.assertEquals(1, list.size());

        LambdaQueryChainWrapper<Entity> lambdaQuery = Db.lambdaQuery(Entity.class);
        list = lambdaQuery.eq(Entity::getId, 1L).list();
        Assertions.assertEquals(1, list.size());

        UpdateChainWrapper<Entity> update = Db.update(Entity.class);
        update.eq("id", 1L).set("name", "bee bee I'm a sheep").update();
        Assertions.assertEquals("bee bee I'm a sheep", lambdaQuery.eq(Entity::getId, 1L).one().getName());

        LambdaUpdateChainWrapper<Entity> lambdaUpdate = Db.lambdaUpdate(Entity.class);
        lambdaUpdate.eq(Entity::getId, 1L).set(Entity::getName, "be better").update();
        Assertions.assertEquals("be better", lambdaQuery.eq(Entity::getId, 1L).one().getName());
    }

    @Test
    void testGetObj() {
        String name = Db.getObj(Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L), Entity::getName);
        Assertions.assertEquals("ruben", name);
    }


    @Override
    protected String tableDataSql() {
        return "insert into entity(id,name) values(1,'ruben'),(2,'chocolate');";
    }

    @Override
    protected List<String> tableSql() {
        return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" +
            "id BIGINT NOT NULL," +
            "name VARCHAR(30) NULL DEFAULT NULL," +
            "PRIMARY KEY (id))");
    }
}
SpringBoot项目中使用MyBatisplus
  • 导入相关的依赖
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
  • 配置MyBatisplus,主要是配置数据源信息以及整合MyBatisplus的配置
# 数据库地址
datasource:
  url: localhost:3306/liu
spring:
  datasource: # 数据库配置
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${datasource.url}?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=UTC
    username: root
    password: root
    hikari:
      maximum-pool-size: 10 # 最⼤连接池数
      max-lifetime: 1770000
mybatis:
  # 指定别名设置的包为所有entity
  type-aliases-package: com.example.springboot_learn.entity
  configuration:
    map-underscore-to-camel-case: true # 驼峰命名规范
  mapper-locations: # mapper映射⽂件位置
    - classpath:mapper/*.xml

# 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
    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
  • 定义MyBatisPlus的配置类,引入相关的插件
@Configuration
public class MybatisPlusConfig {
    /**
     * mybatis-plus SQL执行效率插件【生产环境可以关闭】
     */
    @Bean
    public PerformanceInterceptor performanceInterceptor() {
        return new PerformanceInterceptor();
    }
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

  • 在数据库中创建book表
-- auto-generated definition
create table book
(
    id        bigint auto_increment
        primary key,
    name      varchar(20)    null,
    code      varchar(20)    null,
    bid_price decimal(20, 4) null
);
  • 创建一个实体类,并使用@TableName绑定数据库中的book表:
@TableName("book")
@Data
public class Book {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String code;
    private String name;
    private BigDecimal bidPrice;
}
  • 创建mapper层的接口,继承BaseMapper
@Mapper
public interface BookMapper extends BaseMapper<Book> {
}
  • 创建service层的接口与实现类,接口继承IService接口;实现类实现前面的接口,并继承ServiceImpl类
public interface BookService extends IService<Book> { }
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
}
  • 创建controller层的测试类:
@RestController
@RequestMapping("/book")
public class TestBookController {
    @Resource
    private BookService bookService;
    private final static Logger logger = LoggerFactory.getLogger(TestBookController.class);
    @RequestMapping("/getById")
    public JsonResult<Book> getInfo(@RequestParam(value = "id")String id){
        Book book = bookService.getById(id);
        return new JsonResult<>(book);
    }

    @RequestMapping("/getList")
    public JsonResult<List> getList(){
        List<Book> bookList = bookService.list();
        return new JsonResult<>(bookList);
    }

    /**
     * 新增用户信息
     */
    @RequestMapping("/saveInfo")
    public void saveInfo(){
        Book book = new Book();
        book.setName("圣龙传说第1卷");
        book.setCode("s1");
        book.setBidPrice(new BigDecimal(18.98));
        bookService.save(book);
    }
    /**
     * 分页查询全部数据
     * @Return IPage<UserInfoEntity> 分页数据
     */
    @RequestMapping("/getInfoListPage")
    public IPage<Book> getInfoListPage(){
        //需要在Config配置类中配置分页插件
        IPage<Book> page = new Page<>();
        page.setCurrent(5); //当前页
        page.setSize(10);    //每页条数
        page = bookService.page(page);
        return page;
    }
    /**
     * 批量新增用户信息
     */
    @RequestMapping("/saveInfoList")
    public void saveInfoList(){
        //批量保存
        List<Book> list =new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Book book = new Book();
            book.setName("圣龙传说第"+i+"卷");
            book.setCode(String.valueOf(i));
            book.setBidPrice(new BigDecimal(18.98));
            list.add(book);
        }
        bookService.saveBatch(list);
    }
    /**
     * 根据指定字段查询用户信息集合
     * @Return Collection<UserInfoEntity> 用户实体集合
     */
    @RequestMapping("/getListMap")
    public JsonResult<Collection<Book>> getListMap(@RequestParam(value = "bidPrice")String bidPrice){
        Map<String,Object> map = new HashMap<>();
        //kay是字段名 value是字段值
        map.put("bid_price",new BigDecimal(bidPrice));
        Collection<Book> list = bookService.listByMap(map);
        return new JsonResult<>(list);
    }
    /**
     * 更新用户信息
     */
    @RequestMapping("/updateInfo")
    public void updateInfo(){
        //根据实体中的ID去更新,其他字段如果值为null则不会更新该字段,参考yml配置文件
        Book book = new Book();
        book.setId(10L);
        book.setName("圣龙传说之我是帅哥");
        book.setBidPrice(new BigDecimal(19.98));
        bookService.updateById(book);
    }
    /**
     * 新增或者更新用户信息
     */
    @RequestMapping("/saveOrUpdateInfo")
    public void saveOrUpdate(){
        //传入的实体类userInfoEntity中ID为null就会新增(ID自增)
        //实体类ID值存在,如果数据库存在ID就会更新,如果不存在就会新增
        Book book = new Book();
        book.setId(2L);
        book.setBidPrice(new BigDecimal(99.98));
        bookService.saveOrUpdate(book);
    }
    /**
     * 根据ID删除用户信息
     */
    @RequestMapping("/deleteInfo")
    public void deleteInfo(String id){
        bookService.removeById(id);
    }
    /**
     * 根据ID批量删除用户信息
     */
    @RequestMapping("/deleteInfoList")
    public void deleteInfoList(){
        List<String> list = new ArrayList<>();
        list.add("99");
        list.add("98");
        bookService.removeByIds(list);
    }
    /**
     * 根据指定字段删除用户信息
     */
    @RequestMapping("/deleteInfoMap")
    public void deleteInfoMap(){
        //kay是字段名 value是字段值
        Map<String,Object> map = new HashMap<>();
        map.put("id","97");
        bookService.removeByMap(map);
    }
}
条件构造器 Wapper
  • 声明:不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输
    • wrapper 很重
    • 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
    • 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
    • 拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr
AbstractWrapper
  • QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类,用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
  • entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
  • 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)
  • eq:指定一个字段为什么值
    • eq(R column, Object val)
    • eq(boolean condition, R column, Object val)
    • 例子:eq(“name”, “老王”)—>name = ‘老王’
  • allEq:全部eq(或个别isNull)
    • 方法
      • allEq(Map<R, V> params)
      • allEq(Map<R, V> params, boolean null2IsNull)
      • allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
    • 参数
      • params : key为数据库字段名,value为字段值
      • null2IsNull : 为true则在map的value为null时调用 isNull 方法,为false时则忽略value为null的
    • 例子:
      • 例1: allEq({id:1,name:“老王”,age:null})—>id = 1 and name = ‘老王’ and age is null
      • 例2: allEq({id:1,name:“老王”,age:null}, false)—>id = 1 and name = ‘老王’
  • ne:不等于
    • 用法:
      • ne(R column, Object val)
    • 例子:ne(“name”,“圣龙传说”);
  • gt:大于
  • ge:大于等于
  • lt:小于
  • le:小于等于
  • between:在。。之间
  • notBetween:不在。。之间
  • like:LIKE ‘%值%’
  • notLike:NOT LIKE ‘%值%’
  • likeLeft:LIKE ‘%值’
  • likeRight:LIKE ‘值%’
  • notLikeLeft:NOT LIKE ‘%值’
  • notLikeRight:NOT LIKE ‘值%’
  • isNull:字段 IS NULL
  • isNotNull
  • in:字段 IN (value.get(0), value.get(1), …)
    • 用法:
      • in(R column, Collection<?> value)
      • in(boolean condition, R column, Collection<?> value)
    • 例子:in(“age”,{1,2,3})—>age in (1,2,3)
  • notIn:字段 NOT IN (value.get(0), value.get(1), …)
  • inSql:字段 IN ( sql语句 )
    • 用法:
      • inSql(R column, String inValue)
      • inSql(boolean condition, R column, String inValue)
    • 例子
      • 例: inSql(“age”, “1,2,3,4,5,6”)—>age in (1,2,3,4,5,6)
      • 例: inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3)
  • notInSql:字段 NOT IN ( sql语句 )
  • groupBy:分组:GROUP BY 字段, …
    • 用法:
      • groupBy(R… columns)
      • groupBy(boolean condition, R… columns)
    • 例子:groupBy(“id”, “name”)—>group by id,name
  • orderByAsc
    • 排序:ORDER BY 字段, … ASC
    • 用法:
      • orderByAsc(R… columns)
      • orderByAsc(boolean condition, R… columns)
    • 例子:orderByAsc(“id”, “name”)—>order by id ASC,name ASC
  • orderByDesc
    • ORDER BY 字段, … DESC
  • orderBy:排序:ORDER BY 字段, …
  • having:HAVING ( sql语句 )
    • 用法:
      • having(String sqlHaving, Object… params)
      • having(boolean condition, String sqlHaving, Object… params)
    • 例子:having(“sum(age) > 10”)—>having sum(age) > 10
  • func:func 方法(主要方便在出现if…else下调用不同方法能不断链)
    • 用法:
      • func(Consumer consumer)
      • func(boolean condition, Consumer consumer)
    • 例子
      • func(i -> if(true) {i.eq(“id”, 1)} else {i.ne(“id”, 1)})
  • or:拼接 OR
    • 用法:
      • or()
      • or(boolean condition)
    • 例子
      • eq(“id”,1).or().eq(“name”,“老王”)—>id = 1 or name = ‘老王’
      • or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>or (name = ‘李白’ and status <> ‘活着’)
    • 主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)
  • and:AND 嵌套
    • 用法:
      • and(Consumer consumer)
      • and(boolean condition, Consumer consumer)
    • and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>and (name = ‘李白’ and status <> ‘活着’)
  • nested:正常嵌套 不带 AND 或者 OR
    • 用法:
      • nested(Consumer consumer)
      • nested(boolean condition, Consumer consumer)
    • nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>(name = ‘李白’ and status <> ‘活着’)
  • apply:拼接 sql
    • 用法:
      • apply(String applySql, Object… params)
      • apply(boolean condition, String applySql, Object… params)
    • 例子
      • apply(“id = 1”)—>id = 1
      • apply(“date_format(dateColumn,‘%Y-%m-%d’) = ‘2008-08-08’”)—>date_format(dateColumn,‘%Y-%m-%d’) = ‘2008-08-08’")
      • apply(“date_format(dateColumn,‘%Y-%m-%d’) = {0}”, “2008-08-08”)—>date_format(dateColumn,‘%Y-%m-%d’) = ‘2008-08-08’")
    • 该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!
  • last:无视优化规则直接拼接到 sql 的最后
    • 用法:
      • last(String lastSql)
      • last(boolean condition, String lastSql)
    • 例子:
      • last(“limit 1”)
  • exists:拼接 EXISTS ( sql语句 )
    • 用法:
      • exists(String existsSql)
      • exists(boolean condition, String existsSql)
    • 例子:exists(“select id from table where age = 1”)—>exists (select id from table where age = 1)
  • notExists
集成MyBatisplus时使用lambda语法
@RestController
@RequestMapping("/book2")
public class TestMyBatisPlusLambda {
  @Resource
  private BookService bookService;
  @RequestMapping("/getInfoListPlus")
  public Map<String,Object> getInfoListPage(){
    //初始化返回类
    Map<String,Object> result = new HashMap<>();
    //查询name等于“斗破苍穹”的书
    //等价SQL: SELECT * FROM book WHERE name = '斗破苍穹';
    QueryWrapper<Book> queryWrapper1 = new QueryWrapper<>();
    queryWrapper1.lambda().eq(Book::getName,"斗破苍穹");
    List<Book> bookList1 = bookService.list(queryWrapper1);
    result.put("bookName",bookList1);
    //查询code大于10的书且小于等于20的书
    //等价SQL: SELECT * FROM book WHERE code > 10 AND code <= 20
    QueryWrapper<Book> queryWrapper2 = new QueryWrapper<>();
    queryWrapper2.lambda().gt(Book::getCode,10);
    queryWrapper2.lambda().le(Book::getCode,20);
    List<Book> bookList2 = bookService.list(queryWrapper2);
    result.put("bookCode10to20",bookList2);
    //模糊查询name字段带有"圣龙"的数据,并按照code排序
    //等价SQL: SELECT * FROM book WHERE name LIKE '%苍穹%' ORDER BY code DESC
    QueryWrapper<Book> queryWrapper3 = new QueryWrapper<>();
    queryWrapper3.lambda().like(Book::getName,"苍穹");
    queryWrapper3.lambda().orderByDesc(Book::getCode);
    List<Book> bookList3 = bookService.list(queryWrapper3);
    result.put("bookNameFilter",bookList3);
    //查询name不为null的书,并且分页
    //等价SQL: SELECT * FROM book WHERE name IS NOT NULL LIMIT 0,5
    IPage<Book> page = new Page<>();
    page.setCurrent(1);
    page.setSize(5);
    QueryWrapper<Book> queryWrapper5 = new QueryWrapper<>();
    queryWrapper5.lambda().isNotNull(Book::getName);
    page = bookService.page(page,queryWrapper5);
    result.put("bookPage",page);
    return result;
  }
  @RequestMapping("/updateInfoListPlus")
  public JsonResult updateInfo(){
    UpdateWrapper<Book> updateWrapper1 = new UpdateWrapper<>();
    updateWrapper1.lambda().set(Book::getBidPrice,new BigDecimal(22.987)).eq(Book::getName,"斗破苍穹");
    Boolean isUpdate = bookService.update(new Book(), updateWrapper1);
    System.out.println(isUpdate);
    return new JsonResult();
  }
}
  • 通过上面的例子可以知道MyBatisplus中的lambda的一些基本使用,通过强大的条件构造器queryWrapper、updateWrapper进行sql语句的构造
    • 1.QueryWrapper: Entity 对象封装操作类
    • 2.UpdateWrapper : Update 条件封装,用于Entity对象更新操作
      在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值