02-MyBatisPlus

Image

1. 框架、工具介绍

  • MyBatis Plusopen in new window
  • MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生


  • MybatisX 是一款基于IDEA的快速开发插件,为效率而生。用于一键生成ORM代码;该插件在本文中的主要目的是为了快速生成基于MyBatis Plus相关的代码

2. 导入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3.4</version>
</dependency>
  • 数据库连接依赖。大版本务必和数据库版本一致
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>
  • 分页
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>3.4.1</version>
</dependency>
  • 联表查询
<!--https://gitee.com/best_handsome/mybatis-plus-join-->
<dependency>
    <groupId>com.github.yulichang</groupId>
    <artifactId>mybatis-plus-join</artifactId>
    <version>1.1.8</version>
</dependency>
  • 辅助
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.9</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
</dependency>

3. 数据库配置

create table user_info
(
    id        int auto_increment comment '主键ID' primary key,
    user_name varchar(64) null comment '用户名',
    age       int         null comment '年龄',
    source    tinyint     null comment '来源'
)
    row_format = DYNAMIC;

create index source_id on user_info (source);
spring:
  application:
    name: mybatis-plus
  # 数据库连接相关配置
  datasource:
    url: jdbc:mysql://ip:port/ehang?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: ******
    # 阿里巴巴的druid的mysql连接池
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
  • 启动类配置Dao扫描
  • 其中basePackages路径,请根据个人的实际路径填写
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = {"com.ehang.springboot.mybatisplus.generator.**.mapper"})

4. MybatisX

Image
  • 配置基础信息
Image
  • 属性、方法配置
Image
  • 生成后的效果
Image

5. MyBatis_Plus使用

1. 结构说明

1. pojo

  • 用于接收数据库数据的Java实体类

2. Mapper.xml

  • 指明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.ehang.springboot.mybatisplus.generator.user.mapper.UserInfoMapper">

    <resultMap id="BaseResultMap" type="com.ehang.springboot.mybatisplus.generator.user.demain.UserInfo">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="userName" column="user_name" jdbcType="VARCHAR"/>
            <result property="age" column="age" jdbcType="INTEGER"/>
            <result property="source" column="source" jdbcType="TINYINT"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,user_name,age,
        source
    </sql>
</mapper>

3. mapper

  • 数据库操作的Mapper,继承了MyBatis Plus的BaseMapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
  • BaseMapper做了大量的数据库基础操作
public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> queryWrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}

4. Service

  • 继承了MyBatisPlus的IService,定义了众多基础的Service方法
  • 自动生成的接口无法满足业务需求时,也可以定义接口,满足个性化需要
public interface UserInfoService extends IService<UserInfo> {
}
  • 继承了MyBatisPlus的ServiceImpl,ServiceImpl基于BaseMapper实现了IService定义的基础的接口
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

}

2. Service的CURD

API功能描述
save添加、保存支持单条和批量
saveOrUpdate添加或者修改主键不存在就添加,否则就基于主键修改
remove删除数据条件删除、主键删除、批量删除
update修改支持单条修改、批量修改
get查询单条记录
list批量查询批量查询
page分页查询需要分页插件的支持
count记录数查询总数、满足条件的记录数
chain流式调用让API调用更加方便简单

1. save()

// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量) batchSize指明单批次最大数据量,批量插入数量较大时,推荐使用这个
boolean saveBatch(Collection<T> entityList, int batchSize);
// 单个插入
@Test
void save() {
    UserInfo userInfo = new UserInfo(null, "张三", 10, (byte) 1);
    boolean save = userInfoService.save(userInfo);
    log.info("单条添加的结果:{}", save);
}

// 批量插入
@Test
void saveBatch() {
    List<UserInfo> userInfos = List.of(new UserInfo(null, "李四", 10, (byte) 1), new UserInfo(null, "王五", 10, (byte) 1))
    boolean saveBatch = userInfoService.saveBatch(userInfos, 10);
    log.info("批量添加的结果:{}", saveBatch);
}




 







 


2. SaveOrUpdate()

  • 插入,如果数据存在则修改
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
@Test
void saveOrUpdate() {
    // 单个修改
    UserInfo userInfo = new UserInfo(1004, "张三(改)", 20, (byte) 1);
    boolean saveOrUpdate = userInfoService.saveOrUpdate(userInfo);
    log.info("单条插入(或修改)的结果:{}", saveOrUpdate);

    // 根据条件修改
    LambdaUpdateWrapper<UserInfo> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.eq(UserInfo::getSource, 1);
    boolean saveOrUpdateByWrapper = userInfoService.saveOrUpdate(userInfo, updateWrapper);
    log.info("单条插入(或根据条件修改)的结果:{}", saveOrUpdateByWrapper);

    // 批量插入
    List<UserInfo> userInfos = List.of(new UserInfo(null, "李四", 10, (byte) 1), new UserInfo(null, "王五", 10, (byte) 1))
    boolean saveBatch = userInfoService.saveOrUpdateBatch(userInfos, 10);
    log.info("批量插入(或修改)的结果:{}", saveBatch);
}




 





 




 


4. remove()

  • 删除数据
// 根据 entity 条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);
@Test
void remove() {
    // 根据条件删除
    LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(UserInfo::getUserName, "张三");
    boolean remove = userInfoService.remove(queryWrapper);
    log.info("根据条件删除用户数据:{}", remove);
}

@Test
void removeById() {
    // 根据主键id删除
    boolean removeById = userInfoService.removeById(1006);
    log.info("根据主键ID删除用户数据:{}", removeById);

}

@Test
void removeByMap() {
    // 根据列的值删除
    Map<String, Object> cms = new HashMap();
    cms.put("user_name", "李四");
    cms.put("source", 1);
    boolean removeByMap = userInfoService.removeByMap(cms);

    log.info("根据字段值删除用户数据:{}", removeByMap);
}

@Test
void removeByIds() {
    // 根据主键id批量删除
    List<Integer> ids = List.of(1004, 1005, 1006);
    boolean removeByIds = userInfoService.removeByIds(ids);
    log.info("根据主键ids批量删除用户数据:", removeByIds);
}





 






 










 








 


5. update()

  • 修改数据
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);
@SpringBootTest
@Slf4j
public class UpdateTest {
    @Autowired
    UserInfoService userInfoService;

    @Test
    public void update() {
        // 不建议使用,有
        // 以下的setSql和set选一个即可,务必要设置条件 否则有全部修改的风险
        // updateWrapper.setSql("user_name = '张三'");
        LambdaUpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>()
                .lambda()
                .set(UserInfo::getUserName, "ooxx(改1)")
                .eq(UserInfo::getId, 1);
        boolean update = userInfoService.update(updateWrapper);
        log.info("根据UpdateWrapper修改(不推荐使用):{}", update);
    }

    @Test
    public void update2() {
        // 将符合UpdateWrapper全部修改为entity的值
        LambdaUpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>()
                .lambda()
                .eq(UserInfo::getUserName, "ooxx(改1)");
        UserInfo user = new UserInfo(1, "ooxx(改2)", 10, (byte) 1);
        boolean update = userInfoService.update(user, updateWrapper);
        log.info("根据UpdateWrapper修改为指定对象:{}", update);
    }

    // 根据对象ID进行修改
    @Test
    public void updateById() {
        UserInfo user = new UserInfo(1, "ooxx(改2)", 10, (byte) 1);
        boolean update = userInfoService.updateById(user);
        log.info("根据对象ID修改:{}", update);
    }

    // 根据ID批量修改数据
    @Test
    public void updateBatchById() {
        List<UserInfo> us = List.of(new UserInfo(null, "李四", 10, (byte) 1), new UserInfo(null, "王五", 10, (byte) 1))
        boolean update = userInfoService.updateBatchById(us);
        log.info("根据对象ID批量修改:{}", update);
    }

    // 根据ID批量修改数据,每个批次的数量由后面的batchSize指定
    @Test
    public void updateBatchById2() {
        List<UserInfo> us = List.of(new UserInfo(null, "李四", 10, (byte) 1), new UserInfo(null, "王五", 10, (byte) 1))
        boolean update = userInfoService.updateBatchById(us, 2);
        log.info("根据对象ID批量修改:{}", update);
    }
}















 










 







 







 







 



6. get()

  • 获取单条记录
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
@SpringBootTest
@Slf4j
public class GetTest {
    @Autowired
    UserInfoService userInfoService;

    @Test
    void getById() {
        UserInfo userInfo = userInfoService.getById(1);
        log.info("根据ID查询用户信息:{}", userInfo);
    }

    // 查询一条数据,如果根据条件查询出了多条,则会报错
    @Test
    void getOne() {
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new QueryWrapper<UserInfo>()
                .lambda()
                .eq(UserInfo::getId, 1);
        UserInfo userInfo = userInfoService.getOne(lambdaQueryWrapper);
        log.info("根据ID查询单用户信息:{}", userInfo);
    }

    // 查询单条数据,如果返回多条数据则去取第一条返回
    @Test
    void getOne2() {
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new QueryWrapper<UserInfo>()
                .lambda()
                .eq(UserInfo::getUserName, "ooxx 1")
                .orderByDesc(UserInfo::getId);
        UserInfo userInfo = userInfoService.getOne(lambdaQueryWrapper, false);
        log.info("根据ID查询单用户信息:{}", userInfo);
    }

    // 查询单条数据 以Map的方式返回
    @Test
    void getMap() {
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new QueryWrapper<UserInfo>()
                .lambda()
                .eq(UserInfo::getId, 1);
        // String为数据库列名  Object为值
        Map<String, Object> map = userInfoService.getMap(lambdaQueryWrapper);
        log.info("根据ID查询单用户信息:{}", map);
    }

    // 查询返回结果的第一列
    @Test
    void getObj() {
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new QueryWrapper<UserInfo>()
                .lambda()
                .eq(UserInfo::getUserName, "ooxx 1")
                .select(UserInfo::getUserName);

        String obj = userInfoService.getObj(lambdaQueryWrapper, (u) -> u.toString());
        log.info("getObj:{}", obj);
    }
}








 









 










 










 











 



7. list

  • 批量查询
// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

8. page

  • 分页插件
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>3.4.1</version>
</dependency>
  • 分页插件配置
// 新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

// 旧版
@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;
}

 
 
 
 
 
 













// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
@SpringBootTest
@Slf4j
public class PageTest {

    @Autowired
    UserInfoService userInfoService;

    @Test
    void page() {
        // 分页查询;结果以对象方式返回
        Page<UserInfo> page = userInfoService.page(new Page<UserInfo>(2, 5));
        log.info("page:{}", page);
    }

    @Test
    void pageByWrapper() {
        // 带查询条件的分页查询; 结果以对象方式返回
        // 查询条件是id大于10
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new QueryWrapper<UserInfo>()
                .lambda()
                .ge(UserInfo::getId, 10);
        Page<UserInfo> page = userInfoService.page(new Page<UserInfo>(2, 5), lambdaQueryWrapper);
        log.info("pageByWrapper:{}", page);
    }

    @Test
    void pageMaps() {
        // 分页查询;以Map的方式返回
        Page<Map<String, Object>> page = userInfoService.pageMaps(new Page(2, 5));
        log.info("pageMaps:{}", JSON.toJSONString(page));
    }

    @Test
    void pageMapsByWrapper() {
        // 带查询条件的分页查询,结果以Map方式返回
        // 查询条件是id大于10
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new QueryWrapper<UserInfo>()
                .lambda()
                .ge(UserInfo::getId, 10);
        Page<Map<String, Object>> page = userInfoService.pageMaps(new Page(2, 5), lambdaQueryWrapper);
        log.info("pageMapsByWrapper:{}", JSON.toJSONString(page));
    }
}










 










 






 










 



9. count

查询记录数

  • API列表
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);
@SpringBootTest
@Slf4j
public class CountTest {
    @Autowired
    UserInfoService userInfoService;

    @Test
    void count() {
        int count = userInfoService.count();
        log.info("总数:{}", count);
    }

    @Test
    void countByWrapper() {
        int count = userInfoService.count(new QueryWrapper<UserInfo>()
                .lambda()
                .ge(UserInfo::getId, 100));
        log.info("按条件查询总数:{}", count);
    }
}








 





 





10. Chain(重要)

  • service的链式操作,用的比较频繁的API
// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery();

// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();
@SpringBootTest
@Slf4j
public class ChainTest {
    @Autowired
    UserInfoService userInfoService;

    @Test
    void chainQuery() {
        List<UserInfo> userInfos = userInfoService
                .query()
                .eq("user_name", "ooxx 1")
                .list();
        log.info("流式查询:{}", JSON.toJSONString(userInfos));
    }

    @Test
    void chainLambdaQuery() {
        List<UserInfo> userInfos = userInfoService
                .lambdaQuery()
                .eq(UserInfo::getUserName, "ooxx 1")
                .list();
        log.info("流式查询:{}", JSON.toJSONString(userInfos));
    }
}








 
 
 
 





 
 
 
 



3. 条件构造器

1. 构造器列表

关键字作用示例等价SQL
allEq匹配所有字段全部eq.query().allEq({id:1,user_name:"老王",age:null}).list()WHERE id =1 AND user_neme="老王" AND age IS NULL
eq等于(==).lambdaQuery().eq(UserInfo::getId, 1)WHERE id = 1
ne不等于(<>).lambdaQuery().ne(UserInfo::getId, 1)WHERE id <> 1
gt大于(>).lambdaQuery().gt(UserInfo::getId, 1)WHERE id > 1
ge大于等于(>=).lambdaQuery().ge(UserInfo::getId, 1)WHERE id >= 1
lt小于(<).lambdaQuery().lt(UserInfo::getId, 1)WHERE id < 1
le小于等于(<=).lambdaQuery().le(UserInfo::getId, 1)WHERE id <= 1
between指定区间内.lambdaQuery().between(UserInfo::getId, 1,10)WHERE (id BETWEEN 1 AND 10)
notBetween指定区间外.lambdaQuery().notBetween(UserInfo::getId, 5,100)WHERE (id NOT BETWEEN 5 AND 100)
like字符串匹配.lambdaQuery().like(UserInfo::getUserName, “ooxx”)WHERE (user_name LIKE "%ooxx%")
notLike字符串不匹配.lambdaQuery().notLike(UserInfo::getUserName, “ooxx”)WHERE (user_name NOT LIKE "%ooxx%")
likeLeft字符串左匹配.lambdaQuery().likeLeft(UserInfo::getUserName, “ooxx”)WHERE (user_name LIKE "%ooxx")
likeRight字符串右匹配.lambdaQuery().likeRight(UserInfo::getUserName, “ooxx”)WHERE (user_name LIKE "ooxx%")
isNull等于null.lambdaQuery().isNull(UserInfo::getUserName)WHERE (user_name IS NULL)
isNotNull不等于null.lambdaQuery().isNotNull(UserInfo::getUserName)WHERE (user_name IS NOT NULL)
in包含.lambdaQuery().in(UserInfo::getId, 1, 2, 3)WHERE (id IN (1, 2, 3))
notIn不包含.lambdaQuery().notIn(UserInfo::getId, 1, 2, 3)WHERE (id NOT IN (1, 2, 3))
inSqlsql方式包含.lambdaQuery().inSql(UserInfo::getId, "1, 2, 3")WHERE (id IN (1, 2, 3))
notInSqlsql方式不包含.lambdaQuery().notInSql(UserInfo::getId, "1, 2, 3")WHERE (id NOT IN (1, 2, 3))
groupBy分组.select("source,count(id) as sum").groupBy("source").having("count(id) > {0}", 35);GROUP BY source HAVING count(id) > 35
orderByAsc升序.lambdaQuery().orderByAsc(UserInfo::getSource)ORDER BY source ASC
orderByDesc降序.lambdaQuery().orderByDesc(UserInfo::getSource)ORDER BY source DESC
orderBy排序.lambdaQuery().orderBy(true, true, UserInfo::getSource)ORDER BY source ASC
havinghaving子句.select("source,count(id) as sum").groupBy("source").having("count(id) > {0}", 35);GROUP BY source HAVING count(id) > 35
func自定义Consumer.lambdaQuery().func(i -> {if (true) {i.eq(UserInfo::getId, 10);} else {i.eq(UserInfo::getId, 100); }})WHERE (id = 10)
or多条件满足一个.lambdaQuery()..le(UserInfo::getId, 10) .or(i -> .eq(UserInfo::getUserName, "张三").ge(UserInfo::getId, 1005))WHERE (id <= 10 OR (user_name = "张三" AND id >= 1005))
and多条件同时满足.lambdaQuery().le(UserInfo::getId, 10) .and(i -> i.eq(UserInfo::getUserName, "ooxx 1"))WHERE (id <= 10 AND (user_name = "ooxx 1"))
nested指定条件用()嵌套.lambdaQuery().ge(UserInfo::getId, 10).nested(i -> i.eq(UserInfo::getUserName, "张三").or(m -> m.ge(UserInfo::getId, 1005)))WHERE (id >= 10 AND (user_name = "张三" OR (id >= 1005)))
apply拼接sql.lambdaQuery().apply("id < {0}", 20)WHERE (id < 20)
last拼接语句在sql最后.lambdaQuery().apply("id < {0}", 20).last("limit 1")WHERE (id < ?) limit 1
exists子句存在数据.lambdaQuery().exists("select id from user_info where id > 1000")WHERE (EXISTS (select id from user_info where id > 1000))
notExists子句不存在数据.lambdaQuery().notExists("select id from user_info where id > 10000")WHERE (NOT EXISTS (select id from user_info where id > 10000))

2. allEq

  • condition
    • 所有条件是否生效,默认是true;设置为false之后,设置的所有的条件都不会生效
  • params
    • Map参数;设置需要匹配的字段和对应的值
  • filter
    • 用于设置需要过滤的字段
  • null2IsNull
    • 是否忽略null值;默认是true,如果有需要匹配的字段是null,则会添加 is null的查询条件;如果设置为false,将会自动剔除所有值null的字段校验
/**
 * AllEq
 */
@SpringBootTest
@Slf4j
public class AllEqTest {
    @Autowired
    UserInfoService userInfoService;

    Map<String, Object> params = new HashMap();

    @BeforeEach
    public void init() {
        params.put("user_name", "ooxx 1");
        params.put("id", null);
    }

    // SELECT * FROM user_info WHERE (user_name = "ooxx 1" AND id IS NULL)
    @Test
    void allEq() {
        List<UserInfo> list = userInfoService.query().allEq(params).list();
        log.info("{}", JSON.toJSONString(list));
    }

    // SELECT * FROM user_info
    @Test
    void allEqConditionFalse() {
        List<UserInfo> list = userInfoService.query().allEq(false, params, true).list();
        log.info("{}", JSON.toJSONString(list));
    }

    // SELECT * FROM user_info WHERE (user_name = "ooxx 1")
    @Test
    void allEqNull2IsNull() {
        List<UserInfo> list = userInfoService.query().allEq(params, false).list();
        log.info("{}", JSON.toJSONString(list));
    }

    // SELECT * FROM user_info WHERE (user_name = "ooxx 1")
    @Test
    void allEqFilter() {
        // filter字段,表示要忽略的字段
        List<UserInfo> list = userInfoService.query().allEq((k, v) -> !k.equals("id"), params).list();
        log.info("{}", JSON.toJSONString(list));
    }
}




















 






 






 







 



3. groupBy and having

  • 分组跟having筛选
@SpringBootTest
@Slf4j
public class GroupByAndHavingTest {
    @Autowired
    UserInfoService userInfoService;

    // SELECT source,count(id) as sum FROM user_info GROUP BY source HAVING count(id) > 35
    @Test
    void groupByAndHaving() {
        QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        userInfoQueryWrapper.select("source,count(id) as sum")
                .groupBy("source")
                .having("count(id) > {0}", 35);
        List<Map<String, Object>> maps = userInfoService.listMaps(userInfoQueryWrapper);
    }
}











 
 



4. func

  • 用于设置条件子句
  • 不同的业务条件下,sql执行条件也有所不同;通过func子句来进行设置
@SpringBootTest
@Slf4j
public class FuncTest {
    @Autowired
    UserInfoService userInfoService;

    @Test
    void func() {
        Boolean condition = true;
        List<UserInfo> userInfos = userInfoService.lambdaQuery()
                .func(i -> {
                    if (condition) {
                        i.eq(UserInfo::getId, 10);
                    } else {
                        i.eq(UserInfo::getId, 100);
                    }
                }).list();
        log.info("userInfos:{}", userInfos);
    }
}










 
 
 
 
 
 
 



5. or, and

  • or:多条件满足一个即可
  • and:多条件同时满足
@SpringBootTest
@Slf4j
public class OrAndTest {
    @Autowired
    UserInfoService userInfoService;

    // SELECT * FROM user_info WHERE (id <= 10 OR (user_name = "张三" AND id >= 1005))
    @Test
    void or() {
        List<UserInfo> userInfos = userInfoService.lambdaQuery()
                .le(UserInfo::getId, 10)
                .or(i -> i.eq(UserInfo::getUserName, "张三").ge(UserInfo::getId, 1005))
                .list();
        log.info("userInfo:{}", userInfos);
    }

    // SELECT * FROM user_info WHERE (id <= 10 AND (user_name = "ooxx 1"))
    @Test
    void and() {
        List<UserInfo> userInfos = userInfoService.lambdaQuery()
                .le(UserInfo::getId, 10)
                .and(i -> i.eq(UserInfo::getUserName, "ooxx 1"))
            	.list();
        log.info("userInfo:{}", userInfos);
    }
}










 
 








 
 




6. nested, apply, last

  • nested
    • 嵌套。eg:当条件中存在and和or组合时,就需要对or的多个条件进行嵌套,防止与and之间产生错误的组合关系
  • apply
    • 拼接sql。有些特殊个性化场景下,很难用api去定义一些操作。eg:需要对时间格式化后作查询条件,此时就需要借助一段简单的sql拼接来完成
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")` => `date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • last
    • 在sql末尾带上指定语句。eg:last("limit 1"),就会在sql语句的末尾加上limit 1
  • API列表
// nested
nested(Consumer<Param> consumer)
nested(boolean condition, Consumer<Param> consumer)

// apply
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)

// last
last(String lastSql)
last(boolean condition, String lastSql)
@SpringBootTest
@Slf4j
public class Nested_Apply_Limit_Test {
    @Autowired
    UserInfoService userInfoService;

    // SELECT * FROM user_info WHERE (id <= 10 OR (user_name = "张三" AND id >= 1005))
    @Test
    void nested() {
        List<UserInfo> userInfos = userInfoService.lambdaQuery()
                .ge(UserInfo::getId, 10)
                .nested(
                        i -> i.eq(UserInfo::getUserName, "张三").or(m -> m.ge(UserInfo::getId, 1005))
                )
                .list();
        log.info("userInfo:{}", userInfos);
    }

    // SELECT * FROM user_info WHERE (id < 20)
    @Test
    void apply() {
        List<UserInfo> userInfos = userInfoService.lambdaQuery()
                .apply("id < {0}", 20)
                .list();
        log.info("userInfo:{}", userInfos);
    }

    // SELECT * FROM user_info limit 1
    @Test
    void last() {
        List<UserInfo> userInfos = userInfoService.lambdaQuery()
                .last("limit 1")
                .list();
        log.info("userInfo:{}", userInfos);
    }
}











 
 
 








 








 




6. MyBatis_Plus_Join

1. DB

  • 学校表、班级表、学生表
-- auto-generated definition
create table school_info
(
    id          int          not null primary key,
    school_name varchar(64)  not null comment '学校名称',
    school_addr varchar(255) not null comment '学校地址'
)
    row_format = DYNAMIC;

insert into `school_info` values (1, 'XXX小学', 'xx区xx街道80号');

#  ----------------------------------------------------------------

-- auto-generated definition
create table class_info
(
    id         int          not null primary key,
    class_name varchar(64)  not null comment '名称',
    class_desc varchar(255) null comment '描述',
    school_id  int          not null comment '隶属的学校'
)
    row_format = DYNAMIC;

insert into `class_info` values (1, '一年级1班', NULL, 1);
insert into `class_info` values (2, '一年级2班', NULL, 1);

#  ----------------------------------------------------------------

-- auto-generated definition
create table student_info
(
    id        int         not null primary key,
    name      varchar(64) null,
    age       int         null,
    class_id  int         null,
    school_id int         null
)
    row_format = DYNAMIC;

insert into `student_info` values (1, '张三', 7, 1, 1);
insert into `student_info` values (2, '李四', 7, 2, 1);
insert into `student_info` values (3, '王五', 8, 1, 1);
insert into `student_info` values (4, '赵六', 8, 1, 1);

2. 核心类说明

  • MPJBaseMapper 扩展了MyBatis Plus的 BaseMapper 接口
public interface MPJBaseMapper<T> extends BaseMapper<T> {
    Integer selectJoinCount(@Param("ew") MPJBaseJoin var1);

    <DTO> DTO selectJoinOne(@Param("resultTypeClass_Eg1sG") Class<DTO> var1, @Param("ew") MPJBaseJoin var2);

    Map<String, Object> selectJoinMap(@Param("ew") MPJBaseJoin var1);

    <DTO> List<DTO> selectJoinList(@Param("resultTypeClass_Eg1sG") Class<DTO> var1, @Param("ew") MPJBaseJoin var2);

    List<Map<String, Object>> selectJoinMaps(@Param("ew") MPJBaseJoin var1);

    <DTO, P extends IPage<?>> IPage<DTO> selectJoinPage(P var1, @Param("resultTypeClass_Eg1sG") Class<DTO> var2, @Param("ew") MPJBaseJoin var3);

    <P extends IPage<?>> IPage<Map<String, Object>> selectJoinMapsPage(P var1, @Param("ew") MPJBaseJoin var2);
}
  • MPJBaseService 扩展了MyBatis Plus的 IService 接口
public interface MPJBaseService<T> extends IService<T> {
    Integer selectJoinCount(MPJBaseJoin var1);

    <DTO> DTO selectJoinOne(Class<DTO> var1, MPJBaseJoin var2);

    <DTO> List<DTO> selectJoinList(Class<DTO> var1, MPJBaseJoin var2);

    <DTO, P extends IPage<?>> IPage<DTO> selectJoinListPage(P var1, Class<DTO> var2, MPJBaseJoin var3);

    Map<String, Object> selectJoinMap(MPJBaseJoin var1);

    List<Map<String, Object>> selectJoinMaps(MPJBaseJoin var1);

    <P extends IPage<Map<String, Object>>> IPage<Map<String, Object>> selectJoinMapsPage(P var1, MPJBaseJoin var2);
}
  • MPJBaseServiceImpl 扩展了MyBatis Plus的 ServiceImpl 接口实现
public class MPJBaseServiceImpl<M extends MPJBaseMapper<T>, T> extends ServiceImpl<M, T> implements MPJBaseService<T> {
    // ...
}

3. 基础代码调整

// 将Mapper改为继承MPJBaseMapper(必选)
public interface StudentInfoMapper extends MPJBaseMapper<StudentInfo> {
}
// 将Service改为继承MPJBaseService(可选)
public interface StudentInfoService extends MPJBaseService<StudentInfo> {
}
// 将ServiceImpl改为继承MPJBaseServiceImpl(可选)
@Service
public class StudentInfoServiceImpl extends MPJBaseServiceImpl<StudentInfoMapper, StudentInfo> implements StudentInfoService{
}

4. 联表测试

  • 查询学生所处的班级及学校

1. DTO定义

@Data
public class StudentInfoDTO {
 	// 学生id
    private Integer id;

    // 性名
    private String name;

    // 年龄
    private Integer age;

    // 班级名称
    private String className;

    // 学校名称
    private String schoolName;

    // 学校地址 用于测试别名
    private String scAddr;
}

2. 单记录联表查询

@Autowired
StudentInfoService sutdentInfoService;

/**
 * 联表查询单个
 */
@Test
public void selectJoinOne() {
    StudentInfoDTO studentInfoDTO = sutdentInfoService.selectJoinOne(StudentInfoDTO.class,
            new MPJLambdaWrapper<StudentInfo>()
                    .selectAll(StudentInfo.class)
                    .select(SchoolInfo::getSchoolName)
                    .selectAs(SchoolInfo::getSchoolAddr, StudentInfoDTO::getScAddr)
                    .select(ClassInfo::getClassName)
                    .leftJoin(SchoolInfo.class, SchoolInfo::getId, StudentInfo::getSchoolId)
                    .leftJoin(ClassInfo.class, ClassInfo::getId, StudentInfo::getClassId)
                    .eq(StudentInfo::getId, 1));
    log.info("selectJoinOne:{}", JSON.toJSONString(studentInfoDTO));
}














 
 



  • StudentInfoDTO.class
    • 表示resultType,用于接收联表查询之后的数据库返回
  • selectAll
    • 指明查询实体对应的所有字段
  • select
    • 指定查询列,同一个select只能指明单个表的列,所以多表关联时需要使用多个select去指明不同表的列
  • selectAs
    • 重命名,表现在sql层面是会给字段加上as(别名);主要用在数据库字段名也实体对象的名称不一致的情况;
  • leftJoin、rightJoin、innerJoin SQL中 inner join、left join、right join、full join 到底怎么选?详解来了open in new window
    • 参数一:参与联表的对象
    • 参数二:on关联的指定,此属性必须是第一个对象中的值
    • 参数三:参与连表的ON的另一个实体类属性
  • 条件构造器
    • 联表后可能会存在各种筛选条件
  • 表名
    • 默认主表别名是t,其他的表别名以先后调用的顺序使用 t1, t2, t3 ...
    • 需要直接apply语句的时候,就得知道对应的表名是什么再进行添加,不建议直接追加语句
SELECT t.id,
       t.name,
       t.age,
       t.class_id,
       t.school_id,
       t1.school_name,
       t1.school_addr AS scAddr,
       t2.class_name
FROM student_info t
         LEFT JOIN school_info t1 ON (t1.id = t.school_id)
         LEFT JOIN class_info t2 ON (t2.id = t.class_id)
WHERE (t.id = ?)

3. 联表分页查询

@Autowired
StudentInfoService sutdentInfoService;

/**
 * 分页查询
 */
@Test
public void selectJoinPage() {
    IPage<StudentInfoDTO> studentInfoDTOIPage = sutdentInfoService.selectJoinListPage(new Page<>(1, 2), StudentInfoDTO.class,
            new MPJLambdaWrapper<StudentInfo>()
                    .selectAll(StudentInfo.class)
                    .select(SchoolInfo::getSchoolName)
                    .selectAs(SchoolInfo::getSchoolAddr, StudentInfoDTO::getScAddr)
                    .select(ClassInfo::getClassName)
                    .leftJoin(SchoolInfo.class, SchoolInfo::getId, StudentInfo::getSchoolId)
                    .leftJoin(ClassInfo.class, ClassInfo::getId, StudentInfo::getClassId)
                    .orderByAsc(StudentInfo::getId)
    );
    log.info("selectJoinPage:{}", JSON.toJSONString(studentInfoDTOIPage));
}














 
 




SELECT t.id,
       t.name,
       t.age,
       t.class_id,
       t.school_id,
       t1.school_name,
       t1.school_addr AS scAddr,
       t2.class_name
FROM student_info t
         LEFT JOIN school_info t1 ON (t1.id = t.school_id)
         LEFT JOIN class_info t2 ON (t2.id = t.class_id)
ORDER BY t.id ASC
LIMIT 2