MyBatisPlus学习笔记 学习使用看这一篇就够了(上)

举报
长路 发表于 2022/11/28 19:31:24 2022/11/28
【摘要】 文章目录前言导航工具接口及类BaseMapperIService与ServiceImpl(接口定义与实现)IPage与Page一、springboot集成Mybatis plus实现CRUD二、通用IService使用三、分页查询3.1、借助mp的分页插件3.2、XML 自定义分页3.3、第三方插件:PageHelper四、条件构造器wrapper相关条件构造器4.1、QueryWrapper(独

@[toc]

前言

本篇博客是MyBatisPlus实际应用的学习笔记,若文章中出现相关问题,请指出!

所有博客文件目录索引:博客目录索引(持续更新)

导航

MyBatis-Plus



工具接口及类

BaseMapper

BaseMappermp提供给我们用于增强mapper接口方法:覆盖了大量的针对于单表的查询操作

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

    //批量删除
    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);

    //单条查询根据id
    T selectById(Serializable id);

    //查询多条记录根据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);
}


IService与ServiceImpl(接口定义与实现)

Service CRUD 接口—官方文档

下面是ServiceImpl给我们实现的抽象类方法

image-20210823093857486

你能想到的单表查询几乎都有。

public interface IService<T> {
    int DEFAULT_BATCH_SIZE = 1000;

    default boolean save(T entity) {
        return SqlHelper.retBool(this.getBaseMapper().insert(entity));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean saveBatch(Collection<T> entityList) {
        return this.saveBatch(entityList, 1000);
    }

    boolean saveBatch(Collection<T> entityList, int batchSize);

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return this.saveOrUpdateBatch(entityList, 1000);
    }

    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    default boolean removeById(Serializable id) {
        return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
    }

    default boolean removeByMap(Map<String, Object> columnMap) {
        Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]);
        return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap));
    }

    default boolean remove(Wrapper<T> queryWrapper) {
        return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper));
    }

    default boolean removeByIds(Collection<? extends Serializable> idList) {
        return CollectionUtils.isEmpty(idList) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(idList));
    }

    default boolean updateById(T entity) {
        return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
    }

    default boolean update(Wrapper<T> updateWrapper) {
        return this.update((Object)null, updateWrapper);
    }

    default boolean update(T entity, Wrapper<T> updateWrapper) {
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean updateBatchById(Collection<T> entityList) {
        return this.updateBatchById(entityList, 1000);
    }

    boolean updateBatchById(Collection<T> entityList, int batchSize);

    boolean saveOrUpdate(T entity);

    default T getById(Serializable id) {
        return this.getBaseMapper().selectById(id);
    }

    default List<T> listByIds(Collection<? extends Serializable> idList) {
        return this.getBaseMapper().selectBatchIds(idList);
    }

    default List<T> listByMap(Map<String, Object> columnMap) {
        return this.getBaseMapper().selectByMap(columnMap);
    }

    default T getOne(Wrapper<T> queryWrapper) {
        return this.getOne(queryWrapper, true);
    }

    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    default int count() {
        return this.count(Wrappers.emptyWrapper());
    }

    default int count(Wrapper<T> queryWrapper) {
        return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper));
    }

    default List<T> list(Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectList(queryWrapper);
    }

    default List<T> list() {
        return this.list(Wrappers.emptyWrapper());
    }

    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectPage(page, queryWrapper);
    }

    default <E extends IPage<T>> E page(E page) {
        return this.page(page, Wrappers.emptyWrapper());
    }

    default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectMaps(queryWrapper);
    }

    default List<Map<String, Object>> listMaps() {
        return this.listMaps(Wrappers.emptyWrapper());
    }

    default List<Object> listObjs() {
        return this.listObjs(Function.identity());
    }

    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return this.listObjs(Wrappers.emptyWrapper(), mapper);
    }

    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return this.listObjs(queryWrapper, Function.identity());
    }

    default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return (List)this.getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }

    default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectMapsPage(page, queryWrapper);
    }

    default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
        return this.pageMaps(page, Wrappers.emptyWrapper());
    }

    BaseMapper<T> getBaseMapper();

    default QueryChainWrapper<T> query() {
        return ChainWrappers.queryChain(this.getBaseMapper());
    }

    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(this.getBaseMapper());
    }

    default UpdateChainWrapper<T> update() {
        return ChainWrappers.updateChain(this.getBaseMapper());
    }

    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(this.getBaseMapper());
    }

    default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
        return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
    }
}


IPage与Page

image-20210817011208095

在Page对象中,包含了关于分页的相关信息,其中包含对应的查询结果集records以及对于分页的相关属性:

image-20210817011240481

可以说满足了基本的需求了。



一、springboot集成Mybatis plus实现CRUD

Mapper CRUD 接口

前提准备

image-20210816231056986

引入依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
</dependency>

yml配置项:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatisplusExer?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    # 启动时需要初始化的建表sql语句
    schema: classpath:sql/schema.sql
    # 执行好就修改为never
    initialization-mode: always  # always为始终执行初始化,embedded只初始化内存数据库(默认值),如h2等,never为不执行初始化

#控制台打印sql(默认不会有打印sql语句)
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 默认开启时会进行pojo字段映射如:lastName => last_name
    mapUnderscoreToCamelCase: false   # 取消自动驼峰命名规则映射(默认是开启的),这里仅仅是用于测试

sql

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

pojo

/**
 * @ClassName User
 * @Author ChangLu
 * @Date 2021/8/16 22:11
 * @Description TODO
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {  //mp会默认将pojo类名当做表名(首字母小写),若是类名与表名不一致可使用注解
    //默认会识别id作为主键,这里是对id自动生成进行设置: IdType.AUTO为自动,还有其他很多策略
    @TableId(value = "id",type = IdType.AUTO)
    private Long id;
    // 设置表字段别名(当表属性不一致时即可进行指定)  默认是自动开启别名转换的(这里只是为了熟悉使用)
    @TableField("last_name")
    private String lastName;  //建议pojo字段名与表名像lastName=>last_name要对应,否则使用(如name=>last_name)包装器select()查询返回赋值会无效。
    private Integer age;
    private String email;

}
  • 默认mybatis-plus中会将你的pojo类自动进行转换,如字段lastName,之后来查询数据库会自动转为last_name来进行查询。
  • 若是你插入一条数据的id(mp默认会认为你的id为主键id)没有设置,mp会自动帮你生成一个全局唯一的id(字符串)!并且你插入后该对象就会得到对应自动生成的id值。

UserMapper

/**
 * @ClassName UserMapper
 * @Author ChangLu
 * @Date 2021/8/16 22:11
 * @Description TODO
 */
public interface UserMapper extends BaseMapper<User> {
}

springboot启动类上标注自动扫描mapper包:

@MapperScan("com.changlu.mybatisplusexer.mapper")

CRUD操作

这里暂时没有Wrapper相关的操作,只是进行了对selectByMapdeleteByMap以及其他普通的测试用例,其是根据传入map的属性来进行字段查询的,可设置多个字段,这里仅有一个字段。

特别说明的是:mp中对于查询的键值对,传入与数据库中不同类型的也可以兼容,真的是强大呀,例如数据库中的age字段为int类型,你设置map属性字段中传入put(“age”,“20”)也完全是ok的。

import com.changlu.mybatisplusexer.mapper.UserMapper;
import com.changlu.mybatisplusexer.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootTest
class MybatisplusexerApplicationTests {

    @Resource
    private UserMapper userMapper;

    @Test
    void queryAll() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

    @Test
    void query(){
        System.out.println(userMapper.selectById(1));
    }

    @Test
    void query2(){  //selectByMap测试:根据条件筛选
        Map<String, Object> columnMap = new HashMap<>(1);
        columnMap.put("age",20);//条件为age=20
        List<User> users = userMapper.selectByMap(columnMap);
        System.out.println(users);
    }

    @Test
    void insert(){
        User user = new User(null, "changlu", 18, "939974883@qq.com");
        //执行保存sql前会先生成id到user中
        int result = userMapper.insert(user);
        System.out.println("user=>"+user+",result=>"+result);
    }

    @Test
    public void delete(){
        int result = userMapper.deleteById(1);
        System.out.println(result);
    }

    @Test
    public void delete2(){ //deleteByMap:根据指定字段删除
        Map<String, Object> columnMap = new HashMap<>(1);
        columnMap.put("age","20");//条件为age=20,
        int result = userMapper.deleteByMap(columnMap);
        System.out.println(result);
    }

    @Test
    public void update(){
        User user = new User((long)2, "changlu", 20, "939974883@qq.com");
        int result = userMapper.updateById(user);
        System.out.println(result);
    }

}


二、通用IService使用

在一中介绍的是对mapper接口进行的增强,在mp中还可以对service进行增强,也就是说一些常见的普通的service方法已经帮你进行封账好了,能够更快的进行一系列的操作。

Service CRUD 接口

说明:

//更新操作
//①若是更新某个model实体类,使用updateWrapper一定要进行指定id,否则就会更新整张表
//②若是批量对多个model的某个字段进行更新,调用方法
updateBatchById()
    
save():执行完后返回的是一个布尔值,表示是否插入成功,并且若是你没有填写id,会自动先生成主键到指定实体类中,再进行执行sql保存操作

准备

在示例一的基础上,我们来添加service接口以及service实现类,对应的接口需要实现IService,实现类需要继承一个抽象Service,同样也是mp给我们封装号的对应增强方法接口的实现体。

image-20210817003910217

UserService:service接口定义

public interface UserService extends IService<User> {
}

UserServiceImpl:实现类定义

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

测试

在mp给我们提供的service方法中,对于增删改操作也都包含了对应的事务处理,总体来说还是特别方便的!

@SpringBootTest
class MybatisplusexerApplicationTests {

    @Autowired
    private UserService userService;

    //saveOrUpdate:先根据id查,若是有则进行更新,没有进行插入
    @Test
    public void saveOrUpdate(){
        //测试插入
        User user = new User((long)1,"changlu",18,"9999");
        boolean b = userService.saveOrUpdate(user);//true:成功;false:失败
        System.out.println(b);
    }

    //saveOrUpdate:先根据id查,若是有则进行更新,没有进行插入
    @Test
    public void saveOrUpdate2(){
        //测试更新
        User user = new User((long)1,"liner",22,"368");
        boolean b = userService.saveOrUpdate(user);
        System.out.println(b);
    }

    //listByIds:根据多个id来进行批量查询。(底层采用in方式来进行查询)
    @Test
    public void query(){
        List<User> users = userService.listByIds(Arrays.asList(1, 3, 5));
        System.out.println(users);
    }


}


三、分页查询

3.1中的分页方法只对调用IService里的分页方法才有效!

我们编写请求实现类时,若是进行分页可以直接继承下面的实体类就是表示分页和:

@Data
public class BasePage implements Serializable {
    private static final long serialVersionUID = 8491572200460447988L;
    protected long size = 10;
    protected long current = 1;
}


3.1、借助mp的分页插件

准备 分页插件

在mp的Iservice中提高了四个分页方法:

image-20210817010654080

在BaseMapper中提供了两个分页方法:

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

注意:要想使用mp中的分页功能,就必须需要安装分页插件,否则不会生效!

step1:编写一个mp的配置类,在该配置类中添加分页插件

@Configuration
@MapperScan("com.changlu.mybatisplusexer.mapper")  //使用了分配类,即可将启动器的自动打包放置在这里
public class MybatisPlusConfig {
    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  //设置方言为MySQL
        return interceptor;
    }
}

实操

数据库中有四条记录,我们来对其进行测试:

image-20210817010955168

@SpringBootTest
class MybatisplusexerApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    public void saveOrUpdate() throws JsonProcessingException {
        //当前页与每页数量
        IPage<User> page = new Page<>(1,2);
        //分页查询       
        IPage<User> pageResult = userService.page(page);
        //对象转JSON字符串
        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(pageResult);
        System.out.println(json);
    }
    
}

image-20210817011403523

可以看到查询了两条sql,一个是查询数量,另一个是进行limit查询操作!

为了方便查看Page的相关属性,我们将其转为JSON字符串并且借助工具网站来美化显示其中的内容:

image-20210817011530643

  • records:分页查询的记录结果。
  • total:总记录数。
  • size:每页显示条数,根据初始定义的Page决定。
  • current:当前页。
  • pages:总共2页。

这样的结果内容,对于前台简直不要太舒服了!!!



3.2、XML 自定义分页

前提准备 XML 自定义分页

对于我们自定义sql语句,也就是书写xml也可以实现自定义分页!

在mp中我们无需指定mapper位置,因为对应springboot启动器已经为我们设置默认mapper读取位置:

image-20210817224140184

若是我们想要修改mapper映射配置的位置,直接在yml配置中进行配置即可:

mybatis-plus:
  mapper-locations: classpath*:/mapperxxx/**/*.xml

<br/0

实操

image-20210817224742083

直接在resources下的mapper文件夹中编写xml配置文件:

<?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.changlu.mybatisplusexer.mapper.UserMapper">
    <select id="queryByAge" resultType="com.changlu.mybatisplusexer.pojo.User">
        select * from Blog where age = #{age}
    </select>
</mapper>

若是想要对xml的查询结果进行分页,那么只需要在mapper中接口返回值以及属性中设置即可!

public interface UserMapper extends BaseMapper<User> {
	//此时就会根据我们传入的page来进行对结果进行分页返回
    IPage<User> queryByAge(Page<?> page,Integer age);
}

测试一下

@Resource
private UserMapper userMapper;

@Test
public void saveOrUpdate() throws JsonProcessingException {
    //当前页与每页数量
    Page<User> page = new Page<>(1,1);
    IPage<User> userIPage = userMapper.queryByAge(page, 22);
    String json = new ObjectMapper().writeValueAsString(userIPage);
    System.out.println(json);
}

image-20210817230540782

根据设置的Page信息,查询出来的分页内容与实际情况符合,分页完成!

image-20210817230518925



3.3、第三方插件:PageHelper

原理分析

底层使用了一个PageInterceptor分页拦截器

PageHelper文档

提前准备

引入依赖:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
</dependency>

注意一下:若是你同时引入mybatis-plus以及pagehelper启动器依赖,运行时就会报异常如下。

image-20210820113302095

如何解决呢?将对应pagehelper启动器中的指定mybatis依赖移除即可。

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.1</version>
    <exclusions>
        <exclusion>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </exclusion>
    </exclusions>
</dependency>
</dependencies>

实操

主要就是test方法中的第1以及第3行,即可实现分页效果!

@GetMapping("/")
public ResultBody test(@RequestParam("pagenum")Integer pageNum,@RequestParam("pagesize")Integer pageSize){
    PageHelper.startPage(pageNum,pageSize);//设置起始页以及每页数量  
    List<User> users = userMapper.selectList(null);//此时底层对于select就是进行分页来得到的结果集
    PageInfo<User> userPageInfo = new PageInfo<>(users);//封装PageInfo对象
    return ResultBody.success(userPageInfo);
}

image-20210820133749697

最终拿到的返回值对象:其中包含了很多的关于分页的属性,前端判断就更加容易了

image-20210820133826937

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。