MyBatis-Plus 学习笔记

MyBatis-Plus官网地址:https://mp.baomidou.com/

一、mybatis-plus简介:

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考mybatis-plus官网。那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。

二、mybatis-plus特性:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

三、MyBatis-Plus VS JPA

MyBatis-Plus优势:

  • SQL语句可以自由控制,更灵活,性能较高
  • SQL于代码分离,易于阅读和维护
  • 提供XML标签,支持编写动态SQL语句

JPA优势:

  • JPA移植性比较好
  • 提供了很多 CRUD方法、开发效率高
  • 对象化程度更高

MyBatis-Plus劣势:

  • 简单的CURD都要写SQL
  • XML中有大量的SQL要维护
  • MyBatis自身功能很有限,但支持PLlugin

四、常用注解

  • @TableName:对数据表名注解
  • @TableId:表主键标识
  • @TableId(value = “id”, type = IdType.AUTO):自增
  • @TableId(value = “id”, type = IdType.ID_WORKER_STR):分布式全局唯一ID字符串类型
  • @TableId(value = “id”, type = IdType.INPUT):自行输入
  • @TableId(value = “id”, type = IdType.ID_WORKER):分布式全局唯一ID 长整型类型
  • @TableId(value = “id”, type = IdType.UUID):32位UUID字符串
  • @TableId(value = “id”, type = IdType.NONE):无状态
  • @TableField:表字段标识
  • @TableField(exist = false):表示该属性不为数据库表字段,但又是必须使用的。
  • @TableField(exist = true):表示该属性为数据库表字段。
  • @TableField(condition = SqlCondition.LIKE):表示该属性可以模糊搜索。
  • @TableField(fill = FieldFill.INSERT):注解填充字段 ,生成器策略部分也可以配置!
  • @Version:乐观锁注解、标记
  • @EnumValue:通枚举类注解
  • @TableLogic:表字段逻辑处理注解(逻辑删除)
  • @SqlParser:租户注解
  • @KeySequence:序列主键策略

五、普通查询

增:

    @Autowired
    private RentDetailMapper rentDetailMapper;

    @Override
    public void insert(String userId, String dealerId, String strategyId) {
        RentDetail rentDetail=new RentDetail();
        //插入
        rentDetail.setUserId(userId);
        rentDetail.setDealerId(dealerId);
        rentDetail.setStrategyId(strategyId);
        rentDetailMapper.insert(rentDetail);
    }

删:

    @Autowired
    private RentDetailMapper rentDetailMapper;

    @Override
    public void delete(String Id) {
        //删除
        rentDetailMapper.deleteById(Id);
    }

改:

    @Autowired
    private RentDetailMapper rentDetailMapper;

    @Override
    public void update(String userId, String dealerId, String strategyId) {
        RentDetail rentDetail=new RentDetail();
        //更新
        rentDetail.setUserId(userId);
        rentDetail.setDealerId(dealerId);
        rentDetail.setStrategyId(strategyId);
        rentDetailMapper.updateById(rentDetail);
    }

查:

    @Autowired
    private RentDetailMapper rentDetailMapper;

    @Override
    public void select(String Id) {
        //查找
        rentDetailMapper.selectById(Id);
    }

六、自定义SQL查询

接口示例:

@Mapper
@Repository
public interface IUserDao extends BaseMapper<User> {

    /**
     * 根据电话查找id、昵称
     * @param mobile
     * @param sourceId
     * @return
     */
    List<UserMobileVO> findId(@Param("mobile")String mobile,@Param("sourceId")String sourceId);
}

xml示例:
ps:MyBatisPlus是有驼峰命名的,不需要一个一个字段进行映射

<?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.netx.web.dao.IUserDao">

    <select id="findId" resultType="com.netx.web.vo.UserMobileVO" >
   SELECT
    id,
    mobile,
    real_name
    FROM
        `user`
    WHERE
        mobile like '%${mobile}%'and
        deleted = 0
        and register_source=#{sourceId}
    </select>
</mapper>

动态SQL示例:
PS:若是排序的动态查询需要注意不用#{字段名}而是用${字段名}

<select id="pageStrategy" resultType="com.app.entity.Strategy">
        select * from strategy
        <where>
             <!--若传入的参数为空,则不做对应的筛选-->
            <if test="search.house != null and search.house != ''">
                house = #{search.house}
            </if>
            <if test="search.currncy != null and search.currncy != ''">
                and currncy = #{search.currncy}
            </if>
            and deleted = 0 order by
        <choose>
             <!--若某一参数不为空,则做对应的排序(这里要前端传desc或asc来做降升序控制)-->
            <when test="search.yiedlRate != null and search.yiedlRate != ''">
                yiedl_rate ${search.yiedlRate}
            </when>
            <when test="search.winRate != null and search.winRate != ''">
                win_rate ${search.winRate}
            </when>
            <when test="search.totalRentedCount != null and search.totalRentedCount != ''">
                total_rented_count ${search.totalRentedCount}
            </when>
            <otherwise>
                create_time desc
            </otherwise>
        </choose>
        </where>
    </select>

七、条件构造器

更多详细用法尽在官方文档:https://mp.baomidou.com/guide/wrapper.html#select

查:

    /**
     * 条件构造器 查询操作
     * 年龄在 18~50 之间性别为男且姓名为 xx 的所有用户
     *SELECT id AS id,last_name AS lastName,email,gender,age FROM tbl_employee WHERE (age BETWEEN ? AND ? AND gender = ? AND last_name = ?)
     */

    @Test
    public void testSelectPage(){
        Page<Employee> page=new Page<>(0,2);
        EntityWrapper<Employee> entityWrapper=new EntityWrapper<>();
        entityWrapper.between("age",18,50).eq("gender",1).eq("last_name","Tom");
        List<Employee> list = employeeMapper.selectPage(page, entityWrapper);
        System.out.println(list);
    }

删:

     /**
     * 条件构造器 删除操作
     *删除名字为Tom并且性别为女年龄为45
     *DELETE FROM tbl_employee WHERE (last_name = ? AND gender = ? AND age = ?)
     */
    @Test
    public void testDelete(){
        EntityWrapper<Employee> entityWrapper=new EntityWrapper<>();
        entityWrapper.eq("last_name","Tom").eq("gender",0).eq("age",45);
        Integer row = employeeMapper.delete(entityWrapper);
        System.out.println(row);
    }

改:

      /**
     * 条件构造器 修改操作
     *名字为xiaohong并且性别为女年龄为10的人名字改为honghonglaoshi,邮箱改为120@qq.com
     * UPDATE tbl_employee SET last_name=?, email=? WHERE (last_name = ? AND gender = ? AND age = ?)
     */
    @Test
    public void testUpdate(){
        Employee employee=new Employee();
        employee.setLastName("honghonglaoshi");
        employee.setEmail("120@qq.com");
        EntityWrapper<Employee> entityWrapper=new EntityWrapper<>();
        entityWrapper.eq("last_name","xiaohong").eq("gender",0).eq("age",10);
        Integer row = employeeMapper.update(employee, entityWrapper);
        System.out.println(row);
    }

八、分页查询

官方文档:https://mybatis.plus/guide/page.html

XML 自定义分页

public interface UserMapper {//可以继承或者不继承BaseMapper
    /**
     * <p>
     * 查询 : 根据state状态查询用户列表,分页显示
     * </p>
     *
     * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
     * @param state 状态
     * @return 分页对象
     */
    IPage<User> selectPageVo(Page<?> page, Integer state);
}
  • UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页
<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
    SELECT id,name FROM user WHERE state=#{state}
</select>
  • UserServiceImpl.java 调用分页方法
public IPage<User> selectUserPage(Page<User> page, Integer state) {
    // 不进行 count sql 优化,解决 MP 无法自动优化 SQL 问题,这时候你需要自己查询 count 部分
    // page.setOptimizeCountSql(false);
    // 当 total 为小于 0 或者设置 setSearchCount(false) 分页插件不会进行 count 查询
    // 要点!! 分页返回的对象与传入的对象是同一个
    return userMapper.selectPageVo(page, state);
}

九、AR策略 (Active Record活动记录)

  • Active Record(活动记录),简称AR,是一种领域模型模式,特点就是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一条记录;
    参考文章:https://blog.csdn.net/xiaofeivip_top/article/details/94871238
    开启AR模式的方法很简单,就是让我们的实体类继承Model类,并实现其抽象方法,指定主键即可,如下:
@Data
public class Employee extends Model<Employee> {

   @TableId(value = "id",type = IdType.AUTO)
   private Integer id;

   @TableField
   private String lastName;
   private String email;
   private Integer gender;
   private Integer age;

   /**
    * 通过exit设置当前字段不存在数据库里面
    */
   @TableField(exist = false)
   private Double salary;

   /**
    * 指定当前实体类的 主键属性
    * @return
    */
   @Override
   protected Serializable pkVal() {
       return id;
   }

}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootMybatisplusARTests {

    /**
     * 添加
     */
    @Test
    public void insertAR() {
        Employee employee = new Employee();
        employee.setLastName("AR");
        employee.setEmail("ar@xiaofei.com");
        employee.setGender(1);
        employee.setAge(22);

        boolean insert = employee.insert();
        System.err.println(insert);
        System.err.println(employee.getId());
    }

    /**
     * 修改
     */
    @Test
    public void updateAR() {
        Employee employee = new Employee();
        employee.setId(10);
        employee.setGender(0);
        employee.setAge(0);
        boolean b = employee.updateById();
        System.err.println(b);
    }

    /**
     * 查询
     */
    @Test
    public void selectAR() {
        // 根据Id查询
        Employee employee = new Employee();
        Employee employee1 = employee.selectById(1);
        // System.err.println(employee1);

        // 查询全部
        List<Employee> employeesAll = employee.selectAll();
        // System.err.println(employeesAll);

        // 查询名字带有M的
        List<Employee> employees = employee.selectList(new EntityWrapper().like("last_name", "M"));
        // System.err.println(employees);

        // 查询总数量
        int count = employee.selectCount(null);
        // System.err.println(count);

        // 分页
        Page<Employee> page = employee.selectPage(
                new Page<Employee>(1, 2), null);
        System.err.println(page);
        /**
         *  Page:{ [Pagination { total=0 ,size=2 ,pages=0 ,current=1 }], records-size:2 }
         */
        List<Employee> emps = page.getRecords();
        System.err.println("数据:" + emps);
    }

    /**
     * 删除
     * <p>
     * 注意:删除不存在的数据在逻辑上也是成功的;
     */
    @Test
    public void deleteAR() {
        Employee employee = new Employee();
        boolean b = employee.deleteById(10);
        System.err.println(b);
    }
}

注意点:删除不存在的数据在逻辑上也是成功的;

十、基本配置

官方文档:https://mp.baomidou.com/config/#基本配置
使用方式:
Spring Boot:

mybatis-plus:
  ......
  configuration:
    ......
  global-config:
    ......
    db-config:
      ......  

Configuration

mapUnderscoreToCamelCase

  • 类型:boolean
  • 默认值:true

是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。

​注意
​此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body
​如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名

#defaultEnumTypeHandler

  • 类型:Class<? extends TypeHandler
  • 默认值:org.apache.ibatis.type.EnumTypeHandler

默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理

  • org.apache.ibatis.type.EnumTypeHandler : 存储枚举的名称
  • org.apache.ibatis.type.EnumOrdinalTypeHandler : 存储枚举的索引
  • com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : 枚举类需要实现IEnum接口或字段标记@EnumValue注解.(3.1.2以下版本为EnumTypeHandler)

其他就不一一列举,有兴趣可以在官方文档查阅

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容