MyBatis-Plus

从分析MyBatis框架的开发效率开始

当使用MyBatis框架编写一个SQL需求时,需要以下几个步骤
1.创建Mapper接口,并提供一个抽象方法;
2.创建Mapper接口对应的配置文件,并提供对应的标签和SQL语句;
3.在Service对象中注入Mapper实例对象;
4.在Controller对象中注入Service对象;
5.Controller对象调用Service对象的方法,接着Service对象调用Mapper对象中的方法。
在以上几个步骤中,有几个步骤期望由框架进一步抽象化,带来更好的体验:
1.在单表查询时,可以由框架将常用的Mapper抽象方法和对应的SQL实现;
2.同时,进一步提供Service调用Mapper的常用方法;
3.其他的通用操作;
所以,MyBatis-Plus框架就由此而生。

MyBatis-Plus的特性

  1. 无侵入,低损耗;
  2. 内置通用Mapper,通用Service,通过少量配置即可实现单表大部分CRUD操作;
  3. 支持Lambda形式调用,主键自动生成,支持ActiveRecord模式;
  4. 支持自定义全局通用操作;
  5. 内置代码生成器,分页插件,性能分析器,全局拦截插件。

搭建SpringBoot工程启动第一个MyBatis-Plus程序

通过pom.xml引入MyBatis-Plus依赖;
创建UserMapper接口实现BaseMapper接口;
创建UserService接口实现IService接口;
创建UserServiceImpl类继承ServiceImpl类,并实现UserService接口;
在UserMapper接口中自定义的抽象方法,通过编写Mapper配置文件,提供对应的SQL即可;
通过Controller对象调用UserService对象的方法,UserService对象调用UserMapper对象中的方法完成操作。

// pom.xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>

// application.xml
spring:
  main:
    // 去除SpringBoot的logo
    banner-mode: off
  datasource:
    password: root
    username: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/...
mybatis-plus:
  global-config:
    // 去除MyBatis-Plus的logo
    banner: false
  configuration:
    // 配置log
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

// UserMapper.java
public class UserMapper extends BaseMapper<User> {
    // 自定义抽象方法
    List<User> selectUsersByBirthday(Date date);
}

// UserMapper.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.mapper.UserMapper">
     <select id="selectByBirthday" resultType="com.domain.User">
        select * from user where birthday = #{value}
     </select>
</mapper>

// UserService.java
public interface UserService extends IService<User> {
    // 自定义抽象方法
    List<User> getUsersByBirthday(Date date);
}

// UserServiceImpl.java
public class UserServiceImpl extends ServiceImpl<User> implements UserService {
    @Autowired
    UserMapper userMapper;
    // 自定义抽象方法
    User getUserByBirthday(Date date) {
        return userMapper.selectUsersByBirthday(date);
    }
}

// UserController.java
public class UserController {
    @Autowired
    UserService userService;
    public void saveUser(User user) {
        // 框架内置的单表CRUD方法
        userService.save(user);
    }

    public List<User> getUsers(Date date) {
        // 自定义方法
        return userService.getUsersByBirthday(date);
    }
}

MyBatis-plus中的映射规则

当表名和实体类名一致,表中的字段名和实体类属性名一致,即可自动映射;
在配置文件中开启驼峰命名自动映射后,可以支持下划线到驼峰的映射

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true

通过使用注解@TableName指定映射的数据库表名,按照指定的表名进行映射;
通过使用注解@TableField指定映射的数据库表中的字段名,将注解中的名称和字段名完成映射;
配置属性@TableField(select=false)完成隐藏字段,那么在拼接SQL语句时,就不会拼接这个字段;
配置属性@TableField(exit=false)可以将表中不存在的字段去掉,那么这个字段就不会作为查询字段。

条件构造器

除了使用框架内置的单表CRUD方法,还通过使用条件构造器拼接方法,就能完成条件查询。

  • 继承关系
    Wrapper是抽象类,条件类的顶层,提供获取和判断相关的方法;
    AbstractWrapper是抽象类,Wrapper类的子类,提供所有条件相关的方法;
    AbstractLambdaWrapper是抽象类,AbstractWrapper类的子类,将参数限定为方法引用类型;
    LambdaWrapperAbstractLambdaWrapper类的实现类,用于传递方法引用类型的字段信息;
    QueryWrapperAbstractWrapper类的实现类,用于传递String类型的字段信息。
// QueryWrapper方式
User getUser() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "John");
    return userMapper.selectOne(wrapper);
}
// LambdaWrapper方式
User getUser() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getName, "John");
    return userMapper.selectOne(lambdaQueryWrapper);
}
  • API
    等值查询:eq / allEq / ne
    范围查询:gt / ge / lt / le / between / notBetween
    模糊查询:like / notLike / likeLeft / likeRight
    判空查询:isNull / isNotNull
    包含查询:in / notIn / inSql / notInSql (inSql / notInSql存在SQL注入隐患)
    分组查询:groupBy
    聚合查询:having
    排序查询:orderBy = orderByAsc / orderByDesc
    逻辑查询:and(非方法,正常拼接代表and) / or / nested(构建条件查询语句并and)
    字段查询:select
    func查询:func(构建逻辑判断语句)
    last查询:last(将SQL拼接到语句最后,存在SQL注入隐患)
    exists查询:exists
    自定义条件查询:apply(拼接在where后面)
    主键策略
    使用注解@TableId指定主键生成策略
    @TableId(type=IdType.AUTO)
    跟随数据库表的主键递增策略
    @TableId(type=IdType.INPUT)
    指定手动插入id,否则无法添加数据
    @TableId(type=IdType.ASSIGN-ID)
    采用雪花算法策略(19位数字)
    @TableId(type=IdType.NONE)
    不指定策略时,设置为全局默认策略(默认是雪花算法)
    @TableId(type=IdType.ASSIGN-UUID)
    采用UUID策略(32位数字)

分页

使用分页插件需要配置分页插件

@Configuration
public class MybatisPlusconfig {
    @Bean
    public MybatisPlusInterceptor pageInterceptor() {
        return new MybatisPlusInterceptor().addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL);
    }
}

// 在自定义SQL语句中进行分页查询
// 将IPage对象作为参数传入
@Mapper
public interface UserMapper extends BaseMapper<User> {
    IPage<User> selectByName(IPage<User> page, String name);
}

// controller
void selectPage() {
    ...
    IPage<User> page = new Page<>(1,3);
    userMapper.selectPage(page, lambdaQueryMapper);
    userMapper.selectByName(page, lambdaQueryMapper);
    ...
}

ActiveRecord模式

将实体类对象继承Model类,可以直接使用这个对象调用增删改查方法。底层仍需要Mapper接口的支持,所以持久类接口并不能省略。

public class User extends Model<User> {
    ...
}

// 直接调用方法
user.insert();
user.deleteById();
user.updateById();
user.selectById();

SimpleQuery工具

使用SimpleQuery类的API可以对查询结果使用Stream流进行封装。

拓展功能

  • 逻辑删除
    使用注解@TableLogic指定数据表中的一个字段,将其设置为逻辑删除的状态字段。
// 执行删除操作后,记录不消失,状态由1变为0
@TableLogic(value="1", delval="0")
private Integer status;

// 还可以通过在配置文件进行全局配置
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: status
      logic-not-delete-value: 1
      logic-delete-value: 0      
  • 通用枚举
    对于适用枚举类作为数据类型的数据来说,无法将其插入到数据库中。这种情况下,使用注解@EnumValue可以解决。
public enum GenderEnum {
    MAN(0, "man"),
    WOMAN(1, "woman");
    
    @EnumValue
    private Integer gender;
    private String genderName;
    
    GenderEnum(Integer gender, String genderName) {
        this.gender = gender;
        this.gender = genderName;
    }
}
  • 字段类型处理器
    通过使用字段类型处理器,将实体类中的Map集合类型和数据库中的varchar类型数据进行互相转换。
// pom.xml
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

// 实体类字段
@TableField(typeHandler=FastjsonTypeHandler.class)
privete Map<String, String> contact;
  • 自动填充
    通过使用自动填充处理器完成自动填充。
// 配置处理器
@Component
public class TimeMetaOjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        setFieldValByName("createTime", new Date(), metaObject);
        setFieldValByName("updateTime", new Date(), metaObject);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        setFieldValByName("updateTime", new Date(), metaObject);
    }
// 实体类
public class user {
    ...
    @TableField(fill=FieldFill.INSERT)
    private Date createTime;
    @TableField(fill=FieldFill.INSERT_UPDATE)
    private Date updateTime;
}
  • 防全表更新与删除插件
    与配置分页插件同样的方式。
@Configuration
public class MybatisPlusconfig {
    @Bean
    public MybatisPlusInterceptor pageInterceptor() {
        return new MybatisPlusInterceptor().addInnerInterceptor(new BlockAttackInnerInterceptor();
    }
}
  • 乐观锁
    使用注解@Version实现乐观锁。
    需要在数据库的表中由对应的字段。
    执行SQL分析打印
    通过p6spy组件实现执行SQL分析,并将结果输出。
// pom.xml
<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.1</version>
</dependency>

// 配置文件
spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql
    ...

// p6spy配置文件
    ...
  • 多数据源操作
    使用注解@DS可以对不同的数据库进行操作。
// pom.xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.1.0</version>
</dependency>

// 配置文件
spring:
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          username: root
          password: root
          url: jdbc:mysql://localhost:3306/...
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          username: root
          password: root
          url: jdbc:mysql://localhost:3306/...
          driver-class-name: com.mysql.cj.jdbc.Driver
// 不同的Service使用不同的数据库
@Service
@DS("master")
public class UserServiceImplMaster implements UserService {
    ...
}
@Service
@DS("slave_1")
public class UserServiceImplSlaver implements UserService {
    ...
}
  • 代码生成
    使用MybatisX工具使用逆向工程生产Java工程
    通过数据库的表结构可以生产出:实体类,Mapper接口,Mapper映射文件,Service接口,Service实体类。
    通过代码生成器可以生成更多的结构,更多的内容。可配置项更丰富。
// pom.xml
<!--代码生成器依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3</version>
</dependency>

<!--freemarker模板依赖-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

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

推荐阅读更多精彩内容