基本使用

插入数据

1
userMapper.insert(user);

删除数据

1
userMapper.deleteById(1627302300654780418L);
1
2
3
Map<String, Object> map = new HashMap<>();
map.put("user_name", "wxz");
userMapper.deleteByMap(map);

更新数据

1
2
3
4
User user = new User();
user.setAge(100);
user.setId(4L);
userMapper.updateById(user);

条件构造器

我们在实际操作数据库的时候会涉及到很多条件,所以MP为我们提供了一个功能强大的条件构造器Wrapper,使用它可以让我们非常方便的构造条件。在其子类AbstractWrapper中提供了很多用于构造条件的方法。AbstractWrapper的子类QueryWrapper则额外提供了用于针对Select语法的select方法,可以用来设置查询哪些列。AbstractWrapper的子类UpdateWrapper则额外提供了用于针对SET语法的set方法,可以用来设置对哪些列进行更新

AbstractWrapper

allEq

全部eq(或个别isNull)。个别参数说明:

  • 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 = 老王
  • filter: 过滤函数,是否允许字段传入比对条件中
  • params 与 null2IsNull : 同上
  • 例1: allEq((k,v) -> k.contains(a), {id: 1, name: 老王, age: null}) —-> name = 老王 and age is null
  • 例2: allEq((k,v) -> k.contains(a), {id: 1, name: 老王, age: null}, false) —-> name = 老王

eq

  • 等于 =
  • 例: eq(name, 老王) —-> name = 老王

ne

  • 不等于 <>
  • 例: ne(name, 老王) —-> name <> 老王

gt

  • 大于 >
  • 例: gt(age, 18) —-> age > 18

ge

  • 大于等于 >=
  • 例: ge(age, 18) —-> age >= 18

lt

  • 小于 <
  • 例: lt(age, 18) —-> age < 18

le

  • 小于等于 <=
  • 例: le(age, 18) —-> age <= 18

between

  • BETWEEN 值1 AND 值2
  • 例: between(age, 18, 30) —- >age between 18 and 30

notBetween

  • NOT BETWEEN 值1 AND 值2
  • 例: notBetween(age, 18, 30) —-> age not between 18 and 30

like

  • LIKE %值%
  • 例: like(name, 王) —-> name like %王%

notlike

  • NOT LIKE %值%
  • 例: notLike(name, 王) —-> name not like %王%

likeLeft

  • LIKE %值
  • 例: likeLeft(name, 王) —-> name like %王

likeRight

  • LIKE 值%
  • 例: likeRight(name, 王) —-> name like 王%

notLikeLeft

  • NOT LIKE %值
  • 例: notLikeLeft(name, 王) —-> name not like %王

notLikeRight

NOT LIKE 值%
例: notLikeRight(name, 王) —-> name not like 王%

isNull

  • 字段 IS NULL
  • 例: isNull(name) —-> name is null

isNotNull

  • 字段 IS NOT NULL
  • 例: isNotNull(name) —-> name is not null

in

  • 字段 IN (value.get(0), value.get(1), …)
  • 例: in(age, {1, 2, 3}) —-> age in (1, 2, 3)
  • 字段 IN (v0, v1, …)
  • 例: in(age, 1, 2, 3) —-> age in (1, 2, 3)

notIn

  • 字段 NOT IN (value.get(0), value.get(1), …)
  • 例: notIn(age, {1, 2, 3}) —-> age not in (1, 2, 3)
  • 字段 NOT IN (v0, v1, …)
  • 例: notIn(age, 1, 2, 3) —-> age not in (1, 2, 3)

inSql

  • 字段 IN ( sql语句 )
  • 例: 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语句 )
例: notInSql(age, 1, 2, 3, 4, 5, 6) —-> age not in (1, 2, 3, 4, 5, 6)
例: notInSql(id, select id from table where id < 3) —-> id not in (select id from table where id < 3)

groupBy

  • 分组:GROUP BY 字段, …
  • 例: groupBy(id, name) —-> group by id,name

orderByAsc

  • 排序:ORDER BY 字段, … ASC
  • 例: orderByAsc(id, name) —-> order by id ASC, name ASC

orderByDesc

  • 排序:ORDER BY 字段, … DESC
  • 例: orderByDesc(id, name) —-> order by id DESC, name DESC

orderBy

  • 排序:ORDER BY 字段, …
  • 例: orderBy(true, true, id, name) —-> order by id ASC, name ASC

having

  • HAVING ( sql语句 )
  • 例: having(sum(age) > 10) —-> having sum(age) > 10
  • 例: having(sum(age) > {0}, 11) —-> having sum(age) > 11

func

  • func 方法(主要方便在出现if…else下调用不同方法能不断链)
  • 例: func(i -> if(true) {i.eq(id, 1)} else {i.ne(id, 1)})

or

  • 注意事项: 主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)
  • 例: eq(id, 1).or().eq(name, 老王) —-> id = 1 or name = 老王
  • OR 嵌套
  • 例: or(i -> i.eq(name, 李白).ne(status, 活着)) —-> or (name = 李白 and status <> 活着)

and

  • AND 嵌套
  • 例: and(i -> i.eq(name, 李白).ne(status, 活着)) —-> and (name = 李白 and status <> 活着)

nested

  • 正常嵌套 不带 AND 或者 OR
  • 例: nested(i -> i.eq(name, 李白).ne(status, 活着)) —-> (name = 李白 and status <> 活着)

apply

  • 注意事项: 该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!
  • 拼接sql
  • 例: 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)

last

  • 无视优化规则直接拼接到 sql 的最后
  • 只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
  • 例: last(limit 1)

exists

  • 拼接 EXISTS ( sql语句 )
  • 例: exists(select id from table where age = 1) —-> exists (select id from table where age = 1)

notExists

  • 拼接 NOT EXISTS ( sql语句 )
  • 例: notExists(select id from table where age = 1) —-> not exists (select id from table where age = 1)

QueryWrapper

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

  • 设置查询字段
  • 以上方法分为两类。第二类方法为: 过滤查询字段(主键除外), 入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准
  • 例: select(id, name, age)
  • 例: select(i -> i.getProperty().startsWith(test))

UpdateWrapper

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set

SQL SET 字段
例: set(name, 老李头)
例: set(name, )—->数据库字段值变为空字符串
例: set(name, null)—->数据库字段值变为null

setSql

设置 SET 部分 SQL
例: setSql(name = 老李头)

lambda

获取 LambdaWrapper,在QueryWrapper中是获取LambdaQueryWrapper,在UpdateWrapper中是获取LambdaUpdateWrapper

自定义SQL

虽然MP为我们提供了很多常用的方法,并且也提供了条件构造器,但是如果真的遇到复杂的SQL时,我还是需要自己去定义方法,自己去写对应的SQL,这样SQL也更有利于后期维护。因为MP对Mybatis做了增强,所以还是支持之前Mybatis的方式去自定义方法。同时也支持在使用Mybatis的自定义方法时使用MP的条件构造器帮助我们进行条件构造

  • 方法定义中添加Wrapper类型的参数,添加Wrapper类型的参数,并且要注意给其指定参数名
1
User findUserByWrapper(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
  • 在SQL语句中获取Wrapper拼接的SQL片段进行拼接
1
2
3
<select id="findUserByWrapper" resultType="com.example.mybatis_plus.domain.User">
select * from User ${ew.customSqlSegment}
</select>

注意: 这里不能使用 #{},要使用 ${}

分页查询

基本分页查询

  • 配置分页查询拦截器
1
2
3
4
5
6
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
  • 进行分页查询
1
2
3
4
5
IPage<User> page = new Page<>();
page.setSize(2);
page.setCurrent(1);
IPage<User> userIPage = userMapper.selectPage(page, null);
System.out.println(userIPage);

多表分页查询

如果需要在多表查询时进行分页查询的话,就可以在mapper接口中自定义方法,然后让方法接受Page对象

1
IPage<Orders> orders = ordersMapper.findOrders(page);
1
2
3
4
5
Page<Orders> page = new Page<>();
page.setSize(2);
page.setCurrent(1);
IPage<Orders> orders = ordersMapper.findOrders(page);
System.out.println(orders.getRecords());

Service接口

MP也为我们提供了Service层的实现。我们值需要编写一个接口,继承IService,并创建一个接口实现类继承ServiceImpl,即可使用

  • 基本使用
1
public interface UserService extends IService<User> {}
1
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {}
  • 自定义方法
1
User getUser();
1
2
3
4
5
6
7
@Override
public User getUser() {
UserMapper userMapper = getBaseMapper();
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", 1);
return userMapper.selectOne(queryWrapper);
}

自动填充

在实际项目中的表会和我们的Orders表一样,有更新时间,创建时间,创建人,更新人等字段。我们可以使用@TableField的fill属性来设置字段的自动填充,让我们更方便的更新相关字段

1
2
@TableField(fill = FieldFill.INSERT)
private Date createTime;
1
2
3
4
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}

逻辑删除

MP也支持逻辑删除的处理,我们只需要配置好逻辑删除的实体字段名,代表删除的字段值和代表未删除的字段值后即可。注意: 如果3.3.0版本之前还需要在对应的字段上加上@TableLogic注解

1
2
3
4
5
6
7
mybatis-plus:
global-config:
db-config:
id-type: auto
logic-delete-field: delFlag
logic-delete-value: 1
logic-not-delete-value: 0

乐观锁

并发操作时,我们需要保证对数据的操作不发生冲突。乐观锁就是其中一种方式。乐观锁就是先加上不存在并发冲突问题,在进行实际数据操作的时候再检查是否冲突,我们子啊使用乐观锁时一般在表中增加一个version列,用来记录我们对每天记录操作的版本。每次对某条记录进行过操作对应版本+1,然后我们在每次要进行更新操作时,先查询对应数据的version值,在执行更新时,set version = 老版本 + 1 where version = 老版本,如果在查询老版本号到更新操作的中间时刻有其他人更新了这条数据,这样这次更新语句就会更新失败,这里在更新时对version的操作如果有我们自己做就会显得有些麻烦,所以MP提供了乐观锁插件,使用后我们可以非常方便的对version进行操作

1
2
@Version
private Integer version;
1
2
3
4
5
6
@Bean
public MybatisPlusInterceptor mybatisPlusOptimisticLockerInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}

多插件配置问题

使用多个功能需要注意顺序关系,建议使用如下顺序

  • 多租户,动态表名
  • 分页,乐观锁
  • sql性能规范,防止全表更新与删除
  • 总结: 对sql进行单次改造的优先放入,不对sql进行改造的最后放入