MyBatisPlus
图
MyBatisPlus为简化开发而生,只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑,只需简单配置,即可快速进行单表CRUD操作,从而节省大量时间,代码生成、自动分页、逻辑删除、自动填充等功能一应俱全

集成方式

引入依赖,MyBatisPlus依赖了MyBatis,所以MyBatis不用引入了

最新版本可到官网Github仓库查看,可搭配mybatis-plus-join使用,关联查询更方便

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>Latest Version</version>
</dependency>

配置

mybatis-plus:
  configuration:
    # 链接超时时间
    default-statement-timeout: 15
    # 开启驼峰命名规则映射(用于数据返回映射到实体类)
    map-underscore-to-camel-case: true
  # xml路径
  mapper-locations: classpath:mapper/*.xml

记得在启动类上指定扫描包

@MapperScan("com.example.mapper")

将Mapper类都继承BaseMapper并指定实体类

public interface UserMapper extends BaseMapper<User> {

}

然后该Mapper就有了很多基础查询的方法

实体类注解

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("user")
public class User {

    /**
     * ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 姓名
     */
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
    @TableField(value = "age")
    private Integer age;

    /**
     * 性别:0-男,1-女
     */
    @TableField(value = "sex")
    private Integer sex;

    /**
     * 创建时间
     */
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private Date createTime;

    /**
     * 更新时间
     */
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    /**
     * 性别:男
     */
    public static final int SEX_MAN = 0;

    /**
     * 性别:女
     */
    public static final int SEX_WOMAN = 1;

    /**
     * 性别显示,数据表中不存在
     */
    @TableField(exist = false)
    private String sexName;

    /**
     * 通过Get处理性别的显示
     */
    public String getSexName() {
        if (sex == null) {
            return "不明";
        } else if (sex == 0) {
            return "男";
        } else if (sex == 1) {
            return "女";
        }
        return "不明";
    }

}

注意:实体类中不要使用基本数据类型,会自动填充,例如使用了int类型字段,在updateById时会更新该字段为0,只有为null的字段才不会更新

IDEA插件

MybatisX是苞米豆提供了一款全免费且强大的IDEA插件,支持跳转,自动补全生成SQL,代码生成

查看SQL日志

只需要将输入日志的级别调整到debug即可

# 日志级别
logging:
  level:
    com.example: debug

主键策略

通过@TableId(value = "id", type = IdType.AUTO)中的IdType可以指定ID的生成策略

类型 说明
AUTO 数据库ID自增
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于INPUT)
INPUT insert前自行set主键值
ASSIGN_ID 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)

简单CRUD

/**
 * 添加
 */
@Test
void insert() {
    User user = User.builder()
            .name("张三")
            .age(20)
            .createTime(new Date())
            .build();
    int row = userMapper.insert(user);
    // 可以直接获取到插入成功后的主键
    System.out.println(user.getId());
}

/**
 * 修改
 */
@Test
void update() {
    User user = User.builder()
            .id(1L)
            .name("李四")
            .age(21)
            .updateTime(new Date())
            .build();
    int row = userMapper.updateById(user);
    System.out.println(row);
}

/**
 * 删除
 */
@Test
void delete() {
    int row = userMapper.deleteById(3);
    System.out.println(row);
}

/**
 * 查询
 */
@Test
void query() {
    // 根据ID查询
    User user = userMapper.selectById(1L);
    System.out.println(user);
    // 根据Map条件查询List
    Map<String, Object> userMap = new HashMap<>();
    userMap.put("age", 20);
    List<User> users = userMapper.selectByMap(userMap);
    System.out.println(users);
}

Service CRUD接口

MyBatisPlus提供了很多Service层的通用接口,文档说明

接口定义

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.entity.User;

/**
 * User Service
 * 需要通过泛型指定要操作的实体类
 */
public interface UserService extends IService<User> {

    // 这里写自己定义的方法

}

实现接口

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import org.springframework.stereotype.Service;

/**
 * 继承通用ServiceImpl实现,并指定要操作的实体类
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    // 实现自己写方法

}

mapper继承

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fan.entity.Users;

public interface UserMapper extends BaseMapper<Users> {
}

使用方法,需要注意的是,MyBatisPlus在Mapper命名采用的是SQL关键字方式,而Service层采用的是getsave等方式

/**
 * 添加
 */
@Test
void save() {
    User user = User.builder()
            .name("王五")
            .age(18)
            .createTime(new Date())
            .build();
    boolean save = userService.save(user);
    System.out.println("操作状态:" + save + ",ID:" + user.getId());
}

/**
 * 修改
 */
@Test
void saveOrUpdate() {
    User user = User.builder()
            .id(6L)
            .name("王五")
            .age(24)
            .createTime(new Date())
            .build();
    // 根据ID先查询再更新,如果没有ID直接插入
    boolean save = userService.saveOrUpdate(user);
    System.out.println("操作状态:" + save + ",ID:" + user.getId());
}

分页插件

分页的使用需要先配置分页插件,已内置,需要开启

// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

示例

/**
 * 分页
 */
public Result<List<Question>> list(@RequestBody @Valid QuestionListReqVo params) {
    // 查询条件
    QueryWrapper<Question> wrapper = new QueryWrapper<>();
    wrapper.lambda().eq(Question::getType, params.getType());
    if (Objects.nonNull(params.getCateId())) {
        wrapper.lambda().eq(Question::getCateId, params.getCateId());
    }
    if (StringUtils.hasLength(params.getKeyword())) {
        wrapper.lambda().like(Question::getTitle, params.getKeyword());
    }
    // 分页
    Page<Question> page = new Page<>();
    page.setSize(params.getPageSize());
    page.setCurrent(params.getPageIndex());
    Page<Question> pageObj = questionService.page(page, wrapper);
    // 列表
    List<Question> list = pageObj.getRecords();
    // 当前页
    long current = pageObj.getCurrent();
    // 页大小
    long size = pageObj.getSize();
    // 总页数
    long pages = pageObj.getPages();
    return Result.data(list);
}

注意Pageindex从1开始

Wapper

在所以需要Wapper的地方都可以,实现更为复杂的操作

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 可指定查询的字段
queryWrapper.select("id","name")
        // 添加控制
        .eq("name", "张三");
// Lambda方式
queryWrapper.lambda()
        .select(User::getId, User::getName)
        .eq(User::getName, "张三");
List<User> users = userService.list(queryWrapper);
System.out.println(users);

LambdaQueryWrapper

都能以Lambda形式操作,再写字符字段,推荐

LambdaQueryWrapper<Busmanager> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Busmanager::getState, 0);
queryWrapper.eq(Busmanager::getSmsType, 1);
List<Busmanager> list = busmanagerService.list(queryWrapper);

图

自动填充

/**
 * 创建时间
 */
@TableField(fill = FieldFill.INSERT)
private Date createTime;

/**
 * 更新时间
 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

需要配置

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 自动填充
 */
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("开始插入填充...");
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }

}

注意填充类型,官方示例是字符类型,可根据自己的填充类型修改

逻辑删除

https://baomidou.com/guides/logic-delete/

or语句

wrapper.like(Article::getName, params.getKeyword())
       .or()
       .like(Article::getContent, params.getKeyword());