三款神器(MyBatis Plus + MyBatisX + MyBatis Plus Join);终于不用写数据库操作代码,一键生成直接调用

分类: best 365官网体育投注 时间: 2025-09-13 11:59:54 作者: admin 阅读: 559
三款神器(MyBatis Plus + MyBatisX + MyBatis Plus Join);终于不用写数据库操作代码,一键生成直接调用

大家好,我是一航!

程序员每天的搬砖日常,可以说CURD占据了绝大部分的工作;因此,数据库的CURD也就占据了很大一部分的工作时间,不是在配置xml,就是在写sql的路上;

那有没有什么方式能否把这份苦力活给替代了呢?当然是有的,也就是今天介绍的2框框架+1个工具(MyBatis Plus + MyBatisX + MyBatis Plus Join);不写一行数据库操作代码,不加一行配置文件;一键生成代码,基础的CURD、联表查询API统统搞定;让我们可以安心将精力完全放在产品业务逻辑开发上。

开整!!!

目录说明

框架、工具介绍数据库相关配置MybatisX代码自动生成MyBatis Plus使用

结构说明Service的CURD操作条件构造器 MyBatis Plus Join联表查询总结

框架、工具介绍

MyBatis Plus

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

官网地址:https://mp.baomidou.com/

MyBatis Plus Join

一款对MyBatis Plus 扩展的框架,在其基础上增加了联表查询相关的API;

https://gitee.com/best_handsome/mybatis-plus-join

MyBatisX

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

接下来就要开始对框架和工具的实战运用了;

导入依赖

必备

com.baomidou

mybatis-plus-boot-starter

3.4.3.4

数据库连接依赖;大版本务必和自己的数据库版本一致

mysql

mysql-connector-java

5.1.46

分页

com.baomidou

mybatis-plus-extension

3.4.1

联表查询

com.github.yulichang

mybatis-plus-join

1.1.8

辅助

com.alibaba

druid-spring-boot-starter

1.1.9

org.projectlombok

lombok

true

com.alibaba

fastjson

1.2.73

数据库配置

数据库表

一张简单的用户数据表

CREATE TABLE `user_info` (

`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',

`user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',

`age` int(11) NULL DEFAULT NULL COMMENT '年龄',

`source` tinyint(4) NULL DEFAULT NULL COMMENT '来源',

PRIMARY KEY (`id`) USING BTREE,

INDEX `source_id`(`source`) USING BTREE

) ENGINE = InnoDB AUTO_INCREMENT = 1008 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SpringBoot数据库配置

spring:

application:

name: ehang-mybatis-plus

#数据库连接相关配置

datasource:

url: jdbc:mysql://192.168.1.237:3306/ehang?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false

username: root

password: 123456

#阿里巴巴的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"})

MybatisX

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。一键生成ORM代码;结合MyBatis Plus,生成的代码就已经具备了数据库增删改查的基本功能,直接去开发业务功能就好了;

插件使用步骤如下:

安装插件

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

配置数据源

自动生成ORM代码

第一步

选中表(支持多选),右键选择“MybatisX-Generator”

配置基础信息

属性、方法配置

生成后的效果

MyBatis Plus使用

官网:https://mp.baomidou.com/

官方示例:https://github.com/baomidou/mybatis-plus-samples

结构说明

上面介绍的工具(MyBatisX)已经帮我们基于MyBatis Plus3生成好了数据库操作的基础CURD代码,先一起来简单看一下有那些内容

demain

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

UserInfoMapper.xml

指明Java实体类与数据库表之间的映射关系

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

id,user_name,age,

source

mapper

数据库操作的Mapper,继承了MyBatis Plus的BaseMapper

public interface UserInfoMapper extends BaseMapper {

}

BaseMapper帮我们做了大量的数据库基础操作,详情如下:

public interface BaseMapper extends Mapper {

int insert(T entity);

int deleteById(Serializable id);

int deleteByMap(@Param("cm") Map columnMap);

int delete(@Param("ew") Wrapper queryWrapper);

int deleteBatchIds(@Param("coll") Collection idList);

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

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

T selectById(Serializable id);

List selectBatchIds(@Param("coll") Collection idList);

List selectByMap(@Param("cm") Map columnMap);

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

Integer selectCount(@Param("ew") Wrapper queryWrapper);

List selectList(@Param("ew") Wrapper queryWrapper);

List> selectMaps(@Param("ew") Wrapper queryWrapper);

List selectObjs(@Param("ew") Wrapper queryWrapper);

> E selectPage(E page, @Param("ew") Wrapper queryWrapper);

>> E selectMapsPage(E page, @Param("ew") Wrapper queryWrapper);

}

Service

service层的基础接口,继承了MyBatis Plus的IService,定义了众多基础的Service接口,由于内容较多,这里就不贴出来了,可以自行查看IService接口的定义;

如果自动生成的接口无法满足业务需求的时候,也可以在这里定义接口,来满足个性化的需要。

public interface UserInfoService extends IService {

}

ServiceImpl

继承了MyBatis Plus 的ServiceImpl,ServiceImpl基于BaseMapper实现了IService定义的基础的接口

public class UserInfoServiceImpl extends ServiceImpl

implements UserInfoService{

}

到此,三个简单的类,CURD相关的Service层、Dao层功能就已经全部有了;如果是非特殊情况,这些API已经足够我们去做业务功能开发了。

Service的CURD功能

基本的结构了解清楚之后,就一起来看看,IService到底帮我们提供了那些API,这些API又要如何去使用;

API列表

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

save

插入功能

API列表

// 插入一条记录(选择字段,策略插入)

boolean save(T entity);

// 插入(批量)

boolean saveBatch(Collection entityList);

// 插入(批量) batchSize指明单批次最大数据量,批量插入数量较大时,推荐使用这个

boolean saveBatch(Collection 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() {

UserInfo lisi = new UserInfo(null, "李四", 10, (byte) 1);

UserInfo wangwu = new UserInfo(null, "王五", 10, (byte) 1);

List userInfos = new ArrayList<>();

userInfos.add(lisi);

userInfos.add(wangwu);

boolean saveBatch = userInfoService.saveBatch(userInfos, 10);

log.info("批量添加的结果:{}", saveBatch);

}

测试结果

SaveOrUpdate

插入,如果数据存在则修改

API列表

// TableId 注解存在更新记录,否插入一条记录

boolean saveOrUpdate(T entity);

// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法

boolean saveOrUpdate(T entity, Wrapper updateWrapper);

// 批量修改插入

boolean saveOrUpdateBatch(Collection entityList);

// 批量修改插入

boolean saveOrUpdateBatch(Collection entityList, int batchSize);

测试代码

@Test

void saveOrUpdate() {

// 单个修改

UserInfo userInfo = new UserInfo(1004, "张三(改)", 20, (byte) 1);

boolean saveOrUpdate = userInfoService.saveOrUpdate(userInfo);

log.info("单条插入(或修改)的结果:{}", saveOrUpdate);

// 根据条件修改

LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>();

updateWrapper.eq(UserInfo::getSource, 1);

boolean saveOrUpdateByWrapper = userInfoService.saveOrUpdate(userInfo, updateWrapper);

log.info("单条插入(或根据条件修改)的结果:{}", saveOrUpdateByWrapper);

// 批量插入

UserInfo lisi = new UserInfo(1005, "李四(改)", 10, (byte) 1);

UserInfo wangwu = new UserInfo(1006, "王五(改)", 10, (byte) 1);

List userInfos = new ArrayList<>();

userInfos.add(lisi);

userInfos.add(wangwu);

boolean saveBatch = userInfoService.saveOrUpdateBatch(userInfos, 10);

log.info("批量插入(或修改)的结果:{}", saveBatch);

}

执行结果

remove

删除数据

API列表

// 根据 entity 条件,删除记录

boolean remove(Wrapper queryWrapper);

// 根据 ID 删除

boolean removeById(Serializable id);

// 根据 columnMap 条件,删除记录

boolean removeByMap(Map columnMap);

// 删除(根据ID 批量删除)

boolean removeByIds(Collection idList);

测试代码

@Test

void remove() {

// 根据条件删除

LambdaQueryWrapper 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 cms = new HashMap();

cms.put("user_name", "李四");

cms.put("source", 1);

boolean removeByMap = userInfoService.removeByMap(cms);

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

}

@Test

void removeByIds() {

// 根据主键id批量删除

List ids = Arrays.asList(new Integer[]{1004, 1005, 1006});

boolean removeByIds = userInfoService.removeByIds(ids);

log.info("根据主键ids批量删除用户数据:", removeByIds);

}

测试结果

update

修改数据

API列表

// 根据 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);

测试代码

@SpringBootTest

@Slf4j

public class UpdateTest {

@Autowired

UserInfoService userInfoService;

@Test

public void update() {

// 不建议使用,有

// 以下的setSql和set选一个即可,务必要设置条件 否则有全部修改的风险

//updateWrapper.setSql("user_name = '张三'");

LambdaUpdateWrapper updateWrapper = new UpdateWrapper()

.lambda()

.set(UserInfo::getUserName, "一行Java(改1)")

.eq(UserInfo::getId, 1);

boolean update = userInfoService.update(updateWrapper);

log.info("根据UpdateWrapper修改(不推荐使用):{}", update);

}

@Test

public void update2() {

// 将符合UpdateWrapper全部修改为entity的值

LambdaUpdateWrapper updateWrapper1 = new UpdateWrapper()

.lambda()

.eq(UserInfo::getUserName, "一行Java(改1)");

UserInfo wangwu = new UserInfo(1, "一行Java(改2)", 10, (byte) 1);

boolean update = userInfoService.update(wangwu, updateWrapper1);

log.info("根据UpdateWrapper修改为指定对象:{}", update);

}

// 根据对象ID进行修改

@Test

public void updateById() {

UserInfo wangwu = new UserInfo(1, "一行Java(改2)", 10, (byte) 1);

boolean update = userInfoService.updateById(wangwu);

log.info("根据对象ID修改:{}", update);

}

// 根据ID批量修改数据

@Test

public void updateBatchById() {

UserInfo u1 = new UserInfo(1, "一行Java 1", 10, (byte) 1);

UserInfo u2 = new UserInfo(2, "一行Java 2", 20, (byte) 1);

UserInfo u3 = new UserInfo(3, "一行Java 3", 30, (byte) 1);

List us = new ArrayList<>();

us.add(u1);

us.add(u2);

us.add(u3);

boolean update = userInfoService.updateBatchById(us);

log.info("根据对象ID批量修改:{}", update);

}

// 根据ID批量修改数据,每个批次的数量由后面的batchSize指定

@Test

public void updateBatchById2() {

UserInfo u1 = new UserInfo(1, "一行Java 1", 10, (byte) 1);

UserInfo u2 = new UserInfo(2, "一行Java 2", 20, (byte) 1);

UserInfo u3 = new UserInfo(3, "一行Java 3", 30, (byte) 1);

List us = new ArrayList<>();

us.add(u1);

us.add(u2);

us.add(u3);

boolean update = userInfoService.updateBatchById(us, 2);

log.info("根据对象ID批量修改:{}", update);

}

}

测试结果

Get

获取单条记录

API列表

// 根据 ID 查询

T getById(Serializable id);

// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")

T getOne(Wrapper queryWrapper);

// 根据 Wrapper,查询一条记录

T getOne(Wrapper queryWrapper, boolean throwEx);

// 根据 Wrapper,查询一条记录

Map getMap(Wrapper queryWrapper);

// 根据 Wrapper,查询一条记录

V getObj(Wrapper queryWrapper, Function 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 lambdaQueryWrapper = new QueryWrapper()

.lambda()

.eq(UserInfo::getId, 1);

UserInfo userInfo = userInfoService.getOne(lambdaQueryWrapper);

log.info("根据ID查询单用户信息:{}", userInfo);

}

// 查询单条数据,如果返回多条数据则去取第一条返回

@Test

void getOne2() {

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.eq(UserInfo::getUserName, "一行Java 1")

.orderByDesc(UserInfo::getId);

UserInfo userInfo = userInfoService.getOne(lambdaQueryWrapper, false);

log.info("根据ID查询单用户信息:{}", userInfo);

}

// 查询单条数据 以Map的方式返回

@Test

void getMap() {

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.eq(UserInfo::getId, 1);

// String为数据库列名 Object为值

Map map = userInfoService.getMap(lambdaQueryWrapper);

log.info("根据ID查询单用户信息:{}", map);

}

// 查询返回结果的第一列

@Test

void getObj() {

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.eq(UserInfo::getUserName, "一行Java 1")

.select(UserInfo::getUserName);

String obj = userInfoService.getObj(lambdaQueryWrapper, (u) -> u.toString());

log.info("getObj:{}", obj);

}

}

执行结果

List

批量查询

API列表

// 查询所有

List list();

// 查询列表

List list(Wrapper queryWrapper);

// 查询(根据ID 批量查询)

Collection listByIds(Collection idList);

// 查询(根据 columnMap 条件)

Collection listByMap(Map columnMap);

// 查询所有列表

List> listMaps();

// 查询列表

List> listMaps(Wrapper queryWrapper);

// 查询全部记录

List listObjs();

// 查询全部记录

List listObjs(Function mapper);

// 根据 Wrapper 条件,查询全部记录

List listObjs(Wrapper queryWrapper);

// 根据 Wrapper 条件,查询全部记录

List listObjs(Wrapper queryWrapper, Function mapper);

测试代码

package com.ehang.springboot.mybatisplus;

import com.alibaba.fastjson.JSON;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.ehang.springboot.mybatisplus.generator.user.demain.UserInfo;

import com.ehang.springboot.mybatisplus.generator.user.service.UserInfoService;

import lombok.extern.slf4j.Slf4j;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import java.util.Map;

@SpringBootTest

@Slf4j

public class PageTest {

@Autowired

UserInfoService userInfoService;

@Test

void page() {

// 分页查询;结果以对象方式返回

Page page = userInfoService.page(new Page(2, 5));

log.info("page:{}", page);

}

@Test

void pageByWrapper() {

// 带查询条件的分页查询; 结果以对象方式返回

// 查询条件是id大于10

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.ge(UserInfo::getId, 10);

Page page = userInfoService.page(new Page(2, 5), lambdaQueryWrapper);

log.info(":{}", page);

}

@Test

void pageMaps() {

// 分页查询;以Map的方式返回

Page> page = userInfoService.pageMaps(new Page(2, 5));

log.info("pageMaps:{}", JSON.toJSONString(page));

}

@Test

void pageMapsByWrapper() {

// 带查询条件的分页查询,结果以Map方式返回

// 查询条件是id大于10

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.ge(UserInfo::getId, 10);

Page> page = userInfoService.pageMaps(new Page(2, 5), lambdaQueryWrapper);

log.info("pageMapsByWrapper:{}", JSON.toJSONString(page));

}

}

执行结果

page

分页查询

分页插件

com.baomidou

mybatis-plus-extension

3.4.1

分页插件配置

// 新版

@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;

// }

API列表

// 无条件分页查询

IPage page(IPage page);

// 条件分页查询

IPage page(IPage page, Wrapper queryWrapper);

// 无条件分页查询

IPage> pageMaps(IPage page);

// 条件分页查询

IPage> pageMaps(IPage page, Wrapper queryWrapper);

测试代码

@SpringBootTest

@Slf4j

public class PageTest {

@Autowired

UserInfoService userInfoService;

@Test

void page() {

// 分页查询;结果以对象方式返回

Page page = userInfoService.page(new Page(2, 5));

log.info("page:{}", page);

}

@Test

void pageByWrapper() {

// 带查询条件的分页查询; 结果以对象方式返回

// 查询条件是id大于10

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.ge(UserInfo::getId, 10);

Page page = userInfoService.page(new Page(2, 5), lambdaQueryWrapper);

log.info("pageByWrapper:{}", page);

}

@Test

void pageMaps() {

// 分页查询;以Map的方式返回

Page> page = userInfoService.pageMaps(new Page(2, 5));

log.info("pageMaps:{}", JSON.toJSONString(page));

}

@Test

void pageMapsByWrapper() {

// 带查询条件的分页查询,结果以Map方式返回

// 查询条件是id大于10

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper()

.lambda()

.ge(UserInfo::getId, 10);

Page> page = userInfoService.pageMaps(new Page(2, 5), lambdaQueryWrapper);

log.info("pageMapsByWrapper:{}", JSON.toJSONString(page));

}

}

Count

查询记录数

API列表

// 查询总记录数

int count();

// 根据 Wrapper 条件,查询总记录数

int count(Wrapper 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()

.lambda()

.ge(UserInfo::getId, 100));

log.info("按条件查询总数:{}", count);

}

}

Chain(重要)

service的链式操作,这个是实际使用中会用的比较频繁的API,让我们在写代码时,调用API的操作更加的优雅;

API列表

// 链式查询 普通

QueryChainWrapper query();

// 链式查询 lambda 式。注意:不支持 Kotlin

LambdaQueryChainWrapper lambdaQuery();

// 链式更改 普通

UpdateChainWrapper update();

// 链式更改 lambda 式。注意:不支持 Kotlin

LambdaUpdateChainWrapper lambdaUpdate();

测试代码

@SpringBootTest

@Slf4j

public class ChainTest {

@Autowired

UserInfoService userInfoService;

@Test

void chainQuery() {

List userInfos = userInfoService

.query()

.eq("user_name", "一行Java 1")

.list();

log.info("流式查询:{}", JSON.toJSONString(userInfos));

}

@Test

void chainLambdaQuery() {

List userInfos = userInfoService

.lambdaQuery()

.eq(UserInfo::getUserName, "一行Java 1")

.list();

log.info("流式查询:{}", JSON.toJSONString(userInfos));

}

}

Service相关的API基本已经演示完毕了,在示例代码中,也见到了一些常用的条件构造器,比如eq、ge等,但条件构造器远不止这么一点点;MyBatis Plus 给所有的条件构造都提供了详细的API支持

条件构造器

构造器详细列表

下面通过一张表格,来完整的看一下所有条件构造器的方法;

关键字作用示例等价SQLallEq匹配所有字段全部eq.query().allEq({id:1,user_name:“老王”,age:null}).list()WHERE id =1 AND user_neme=“老王” AND age IS NULLeq等于(==).lambdaQuery().eq(UserInfo::getId, 1)WHERE id = 1ne不等于(<>).lambdaQuery().ne( UserInfo::getId, 1)WHERE id <> 1gt大于(>).lambdaQuery().gt( UserInfo::getId, 1)WHERE id > 1ge大于等于(>=).lambdaQuery().ge( UserInfo::getId, 1)WHERE id >= 1lt小于(<).lambdaQuery().lt( UserInfo::getId, 1)WHERE id < 1le小于等于(<=).lambdaQuery().le( UserInfo::getId, 1)WHERE id <= 1between指定区间内.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, “一行Java”)WHERE (user_name LIKE “%一行Java%”)notLike字符串不匹配.lambdaQuery().notLike( UserInfo::getUserName, “一行Java”)WHERE (user_name NOT LIKE “%一行Java%”)likeLeft字符串左匹配.lambdaQuery().likeLeft( UserInfo::getUserName, “一行Java”)WHERE (user_name LIKE “%一行Java”)likeRight字符串右匹配.lambdaQuery().likeRight( UserInfo::getUserName, “一行Java”)WHERE (user_name LIKE “一行Java%”)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) > 35orderByAsc升序.lambdaQuery().orderByAsc(UserInfo::getSource)ORDER BY source ASCorderByDesc降序.lambdaQuery().orderByDesc(UserInfo::getSource)ORDER BY source DESCorderBy排序.lambdaQuery().orderBy(true, true, UserInfo::getSource)ORDER BY source ASChavinghaving子句.select(“source,count(id) as sum”).groupBy(“source”).having(“count(id) > {0}”, 35);GROUP BY source HAVING count(id) > 35func自定义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, “一行Java 1”))WHERE (id <= 10 AND (user_name = “一行Java 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 1exists子句存在数据.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))

通过上面的表格,再结合示例代码以及等价SQL就能很清晰的看出各个条件构造器的功能了;

下面拧几个不好理解或者需要注意的构造器,专门说一下

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 params = new HashMap();

@BeforeEach

public void init() {

params.put("user_name", "一行Java 1");

params.put("id", null);

}

@Test

void allEq() {

List list = userInfoService.query().allEq(params).list();

log.info("{}", JSON.toJSONString(list));

// 等价sql: SELECT id,user_name,age,source FROM user_info WHERE (user_name = "一行Java 1" AND id IS NULL)

}

@Test

void allEqConditionFalse() {

//-----------condition参数------------

// 表示是否才上查询条件,如果等于false 将不会添加任何查询条件

List list = userInfoService.query().allEq(false, params, true).list();

log.info("{}", JSON.toJSONString(list));

// 等价sql: SELECT id,user_name,age,source FROM user_info

}

@Test

void allEqNull2IsNull() {

//-----------null2IsNull演示------------

// null2IsNull = false;会自动踢出null值条件

List list = userInfoService.query().allEq(params, false).list();

// 等价sql: SELECT id,user_name,age,source FROM user_info WHERE (user_name = "一行Java 1")

log.info("{}", JSON.toJSONString(list));

}

@Test

void allEqFilter() {

//---------filter延时----------

// filter字段,表示要忽略的字段

// 以下是忽略key为id的条件

List list = userInfoService.query().allEq((k, v) -> !k.equals("id"), params).list();

// 等价sql:SELECT id,user_name,age,source FROM user_info WHERE (user_name = "一行Java 1")

log.info("{}", JSON.toJSONString(list));

}

}

groupBy and having

跟组跟having筛选

示例代码

@SpringBootTest

@Slf4j

public class GroupByAndHavingTest {

@Autowired

UserInfoService userInfoService;

@Test

void groupByAndHaving() {

QueryWrapper userInfoQueryWrapper = new QueryWrapper<>();

userInfoQueryWrapper.select("source,count(id) as sum")

.groupBy("source")

.having("count(id) > {0}", 35);

List> maps = userInfoService.listMaps(userInfoQueryWrapper);

// 等价sql:SELECT source,count(id) as sum FROM user_info GROUP BY source HAVING count(id) > 35

}

}

func

用于设置条件子句;

实际的业务场景下,可能存在不同的业务条件下导致的sql执行条件也有所不同;那么就可以通过func子句来进行设置

测试代码

@SpringBootTest

@Slf4j

public class FuncTest {

@Autowired

UserInfoService userInfoService;

@Test

void func() {

Boolean condition = true;

List userInfos = userInfoService.lambdaQuery()

.func(i -> {

if (condition) {

i.eq(UserInfo::getId, 10);

} else {

i.eq(UserInfo::getId, 100);

}

}).list();

log.info("userInfos:{}", userInfos);

//func(i -> {if (true) {i.eq(UserInfo::getId, 10);} else {i.eq(UserInfo::getId, 100); }})

}

}

执行结果

Boolean condition = true;

Boolean condition = false;

or 、 and

or:多条件满足一个即可

and:多条件同时满足

示例代码

@SpringBootTest

@Slf4j

public class OrAndTest {

@Autowired

UserInfoService userInfoService;

@Test

void or() {

List userInfos = userInfoService.lambdaQuery()

.le(UserInfo::getId, 10)

.or(i -> i.eq(UserInfo::getUserName, "张三").ge(UserInfo::getId, 1005))

.list();

log.info("userInfo:{}", userInfos);

// 等价sql:SELECT id,user_name,age,source FROM user_info WHERE (id <= 10 OR (user_name = "张三" AND id >= 1005))

}

@Test

void and() {

List userInfos = userInfoService.lambdaQuery()

.le(UserInfo::getId, 10)

.and(i -> i.eq(UserInfo::getUserName, "一行Java 1")).list();

log.info("userInfo:{}", userInfos);

// 等价sql:SELECT id,user_name,age,source FROM user_info WHERE (id <= 10 AND (user_name = "一行Java 1"))

}

}

or

and

nested、apply、last

nested

嵌套;

比如当条件中存在and和or组合的时候,就需要对or的多个条件进行嵌套,防止与and之间产生错误的组合关系

apply

拼接sql;有些特殊个性化场景下,很难用api去定义一些操作;比如,需要对时间继续格式化之后作为查询条件,此时就需要借助一段简单的sql拼接来完成效果

apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")`--->`date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

last

在sql的末尾带上指定的语句;比如last("limit 1"),就会在sql语句的末尾加上limit 1

API列表

// nested

nested(Consumer consumer)

nested(boolean condition, Consumer 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;

@Test

void nested() {

List 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);

// 等价sql:SELECT id,user_name,age,source FROM user_info WHERE (id <= 10 OR (user_name = "张三" AND id >= 1005))

}

@Test

void apply() {

List userInfos = userInfoService.lambdaQuery()

.apply("id < {0}", 20)

.list();

log.info("userInfo:{}", userInfos);

// 等价sql:SELECT id,user_name,age,source FROM user_info WHERE (id < 20)

}

@Test

void last() {

List userInfos = userInfoService.lambdaQuery()

.last("limit 1")

.list();

log.info("userInfo:{}", userInfos);

// 等价sql:SELECT id,user_name,age,source FROM user_info limit 1

}

}

有了这些API和条件构造器,是不是一行数据库操作的代码都没有写,基础的CURD统统都能搞定了;

但是,实际的业务并不只是基础的CURD,有没有发现,联表查询MyBatis Plus并没有支持,但是关联查询在业务开发中,又会经常用到,如果单纯基于MyBatis Plus,要实现联表,就只能自己写配置,写SQL去实现了,这就违背了本文的初衷了;

那有没有一款框架能帮助我们去封装联表查询呢?那就是下面要介绍的一款框架MyBatis Plus Join

MyBatis Plus Join

MyBatis Plus Join一款专门解决MyBatis Plus 关联查询问题的扩展框架,他并不一款全新的框架,而是基于MyBatis Plus功能的增强,所以MyBatis Plus的所有功能MyBatis Plus Join同样拥有;框架的使用方式和MyBatis Plus一样简单,几行代码就能实现联表查询的功能

官方仓库:https://gitee.com/best_handsome/mybatis-plus-join

准备工作

为了方便做联表测试,这里预先准备三张表(学校表、班级表、学生表),用来做关联查询,sql如下:

DROP TABLE IF EXISTS `school_info`;

CREATE TABLE `school_info` (

`id` int(11) NOT NULL,

`school_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '学校名称',

`school_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '学校地址',

PRIMARY KEY (`id`) USING BTREE

) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `school_info` VALUES (1, 'XXX小学', 'xx区xx街道80号');

-- ----------------------------------------------------------------

CREATE TABLE `class_info` (

`id` int(11) NOT NULL,

`class_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',

`class_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '描述',

`school_id` int(11) NOT NULL COMMENT '隶属的学校',

PRIMARY KEY (`id`) USING BTREE

) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `class_info` VALUES (1, '一年级1班', NULL, 1);

INSERT INTO `class_info` VALUES (2, '一年级2班', NULL, 1);

-- ----------------------------------------------------------------

CREATE TABLE `student_info` (

`id` int(11) NOT NULL,

`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,

`age` int(11) NULL DEFAULT NULL,

`class_id` int(11) NULL DEFAULT NULL,

`school_id` int(11) NULL DEFAULT NULL,

PRIMARY KEY (`id`) USING BTREE

) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci 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);

基础MyBatis Plus代码生成

参考MyBatisX的插件使用,这里就不重复了

MyBatis Plus Join 核心类说明

MPJBaseMapper

扩展了MyBatis Plus的 BaseMapper 接口

public interface MPJBaseMapper extends BaseMapper {

Integer selectJoinCount(@Param("ew") MPJBaseJoin var1);

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

Map selectJoinMap(@Param("ew") MPJBaseJoin var1);

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

List> selectJoinMaps(@Param("ew") MPJBaseJoin var1);

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

> IPage> selectJoinMapsPage(P var1, @Param("ew") MPJBaseJoin var2);

}

MPJBaseService

扩展了MyBatis Plus的 IService 接口

public interface MPJBaseService extends IService {

Integer selectJoinCount(MPJBaseJoin var1);

DTO selectJoinOne(Class var1, MPJBaseJoin var2);

List selectJoinList(Class var1, MPJBaseJoin var2);

> IPage selectJoinListPage(P var1, Class var2, MPJBaseJoin var3);

Map selectJoinMap(MPJBaseJoin var1);

List> selectJoinMaps(MPJBaseJoin var1);

>> IPage> selectJoinMapsPage(P var1, MPJBaseJoin var2);

}

MPJBaseServiceImpl

扩展了MyBatis Plus的 ServiceImpl 接口实现

public class MPJBaseServiceImpl, T> extends ServiceImpl implements MPJBaseService {

...

}

基础代码调整

简单的三处调整,就能完成整合工作

将mapper改为继承MPJBaseMapper (必选)

修改前

public interface StudentInfoMapper extends BaseMapper {

}

修改后

public interface StudentInfoMapper extends MPJBaseMapper {

}

将service改为继承MPJBaseService (可选)

修改前

public interface StudentInfoService extends BaseService {

}

修改后

public interface StudentInfoService extends MPJBaseService {

}

将serviceImpl改为继承MPJBaseServiceImpl (可选)

修改前

@Service

public class StudentInfoServiceImpl extends BaseServiceImpl

implements StudentInfoService{

}

修改后

@Service

public class StudentInfoServiceImpl extends MPJBaseServiceImpl

implements StudentInfoService{

}

联表测试

测试需求:查询学生所处的班级及学校

DTO定义

用于联表查询后接收数据的实体类

@Data

public class StudentInfoDTO {

// 学生id

private Integer id;

// 性名

private String name;

// 年龄

private Integer age;

// 班级名称

private String className;

// 学校名称

private String schoolName;

// 学校地址 用于测试别名

private String scAddr;

}

单记录联表查询

@Autowired

StudentInfoService sutdentInfoService;

/**

* 联表查询单个

*/

@Test

public void selectJoinOne() {

StudentInfoDTO studentInfoDTO = sutdentInfoService.selectJoinOne(StudentInfoDTO.class,

new MPJLambdaWrapper()

.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 到底怎么选?详解来了

参数一:参与联表的对象参数二:on关联的指定,此属性必须是第一个对象中的值参数三:参与连表的ON的另一个实体类属性 条件构造器

联表后可能会存在各种筛选条件,可以根据上面对条件构造器的介绍,指明所需要的筛选条件,比如上面.eq(StudentInfo::getId, 1)),就是用来指明ID为1的学生信息。

表名

默认主表别名是t,其他的表别名以先后调用的顺序使用t1,t2,t3…;

需要直接apply语句的时候,就得知道对应的表面是什么再进行添加,所以不到万不得已的时候,不建议直接追加语句。

等价SQL

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 = ?)

执行结果

联表查多条

@Autowired

StudentInfoService sutdentInfoService;

/**

* 联表查询批量

*/

@Test

public void selectJoinList() {

List studentInfoDTOS = sutdentInfoService.selectJoinList(StudentInfoDTO.class,

new MPJLambdaWrapper()

.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("selectJoinList:{}", JSON.toJSONString(studentInfoDTOS));

}

等价SQL

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)

执行结果

联表分页查询

@Autowired

StudentInfoService sutdentInfoService;

/**

* 分页查询

*/

@Test

public void selectJoinPage() {

IPage studentInfoDTOIPage = sutdentInfoService.selectJoinListPage(new Page<>(1, 2), StudentInfoDTO.class,

new MPJLambdaWrapper()

.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));

}

等价SQL

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

执行结果

总结

好了,MyBatis Plus + MyBatisX + MyBatis Plus Join的详细使用教程就讲解完了;再回头看,是不是发现业务功能开发一下子变的简单多了;

本文也只是介绍了大部分常用的内容,并没有列举出两款框架的所有东西;知道怎么使用之后,更多的使用细节,可以结合API文档以及各种条件构造器,灵活变通,即可完成各种想要的效果;

码字不易,如果觉得好用,帮忙点个赞,点个再看呗!感激涕零…

相关文章

单机游戏哪个好玩 十大必玩单机游戏推荐
正在阅读:赤潮什么种族最厉害 赤潮新手选择什么种族赤潮什么种族最厉害 赤潮新手选择什么种族
揭开金泰妍的感情面纱:从地下恋到公开缘分,边伯贤背后的故事
Unity3d二 如何打开unity3d工程