SpringBoot整合MyBatisPlus

1次阅读

共计 12570 个字符,预计需要花费 32 分钟才能阅读完成。

MyBatis-Plus

官网网址:https://mp.baomidou.com/

简介

MyBatis-Plus(简称 MP)是一个 MyBatis 的加强工具,在 MyBatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。

个性

  • 无侵入 :只做加强不做扭转,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小 :启动即会主动注入根本 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 操作智能剖析阻断,也可自定义拦挡规定,预防误操作

疾速入门

步骤

1、创立数据库 mybatis_plus

2、创立 user 表

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)
);
-- 实在开发中,version(乐观锁)、delete(逻辑删除)、gmt_create、gmt_modified

3、插入数据

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

4、编写我的项目,Spring Initializer 疾速初始化一个 Spring Boot 工程

5、导入依赖

<!-- 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

阐明:咱们应用 mybatis-plus 能够节俭咱们大量的代码,尽量不要同时导入 mybatis 和 mybatis-plus!版本的差别!

6、连贯数据库!这一步和 mybatis 雷同!

# mysql 5   驱动不同 com.mysql.jdbc.Driver
        
# mysql 8   驱动不同 com.mysql.cj.jdbc.Driver,须要减少时区的配置 serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

==7、传统形式 pojo-dao(连贯 mybatis,配置 mapper.xml 文件)-service-controller==

7、应用了 mybatis-plus 之后

  • pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Long id;
    private String name;
    private Integer age;
    private String email;
}  
  • mapper 接口
// 在对应的 Mapper 下面继承根本类 BaseMapper
@Repository // 代表长久层
public interface UserMapper extends BaseMapper<User>{
    // 所有的 CRUD 操作有曾经编写实现了
    // 你不须要像以前配置一大堆文件了
}
  • 留神点,咱们须要在主启动类下来扫描咱们的 mapper 包下的所有接口 @MapperScan("com.zwk.mapper")
  • 测试类中测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootMybatisPlusApplicationTests {

    // 继承了 BaseMapper,所有的办法都来自父类
    // 咱们也能够编写本人的扩大办法
    @Autowired
    private UserMapper userMapper;

    @Test
    public void contextLoads() {
        // 参数是一个 Wrapper,条件结构器,这里咱们先不必 null
        // 查问全副用户

        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
        
    }
    
}
  • 后果

思考问题?

1、SQL 谁帮咱们写的?MyBatis-Plus 都写好了

2、办法哪里来的?MyBatis-Plus 都写好了

配置日志

咱们所有的 sql 当初是不可见的,咱们心愿晓得它是怎么执行的,所以咱们必须要看日志!

开发时须要看日志,上线时须要把日志去掉,因为日志是浪费时间的!

# 配置日志     这个是 mybatis-plus 自带的查看日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

配置结束日志后,就须要留神这个主动生成的 SQL,咱们就会喜爱上 MyBatis-Plus!

CRUD 扩大

插入操作

Insert 插入

// 测试插入
@Test
public void testInsert(){User user = new User();
    user.setName("iu");
    user.setAge(18);
    user.setEmail("66668888@qq.com");

    int result = userMapper.insert(user); // 帮咱们主动生成 id
    System.out.println(result); // 受影响的行数
    System.out.println(user); // 发现,id 会主动回填


}

数据库插入的 id 默认值为:全局的惟一 id

主键生成策略

默认 ID_WORKER 全局惟一 id

分布式系统惟一 id 生成计划汇总:https://www.cnblogs.com/haoxi…

雪花算法:

snowflake 是 Twitter 开源的分布式 ID 生成算法,后果是一个 long 型的 ID。其核心思想是:应用 41bit 作为毫秒数,10bit 作为机器的 ID(5 个 bit 是数据中心,5 个 bit 的机器 ID),12bit 作为毫秒内的流水号(意味着每个节点在每毫秒能够产生 4096 个 ID),最初还有一个符号位,永远是 0。简直能够保障寰球惟一!

主键自增

咱们须要配置主键自增

1、实体类字段上 @TableId(type = IdType.AUTO)

2、数据库字段肯定要是自增

3、再次测试插入即可!

其余的源码解释

public enum IdType {AUTO(0), // 数据库 id 自增
    NONE(1), // 未设置主键
    INPUT(2), // 手动输出
    ID_WORKER(3), // 默认的全局惟一 id
    UUID(4), // 全局惟一 id uuid
    ID_WORKER_STR(5); // ID_WORKER 字符串表示法
}    

更新操作

// 测试更新
@Test
public void testUpdate(){
    // 通过条件主动拼接动静 sql
    User user = new User();
    user.setId(9L);
    user.setName("就这样吧");
    user.setAge(3);

    //  留神:updateById 然而参数是一个 对象!int i = userMapper.updateById(user);
    System.out.println(i);
}

所有的 sql 都是主动帮你动静配置的!

主动填充

创立工夫、批改工夫!这些操作都是自动化实现的,咱们不心愿手动更新!

阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified 简直所有的表都要配置上!而且须要自动化!

形式一:数据库级别 (工作用不容许你批改数据库)

1、在表中新增字段 create_time, update_time

2、同步实体类

private Date creatTime;
private Date updateTime;

3、测试插入方法,查看更新后果

形式二:代码级别

1、删除数据库的默认值、更新操作!

2、实体类字段属性上须要减少注解

// 字段增加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

3、编写处理器来解决这个注解即可

@Slf4j
@Component // 肯定不要遗记把处理器加到 IOC 容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {log.info("start insert fill...");
        // setInsertFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
    // 更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {log.info("start update fill...");
        this.setFieldValByName("updateTime",new Date(),metaObject);

    }
}

4、测试插入

5、测试更新、察看工夫即可!

乐观锁

在面试过程中,咱们常常会被问道乐观锁,乐观锁!这个其实非常简单!

乐观锁:顾名思义非常乐观,它总是认为不会呈现问题,无论干什么都不会上锁!如果呈现了问题,再次更新值测试

乐观锁:顾名思义非常乐观,它总是认为总会呈现问题,无论干什么都会上锁!再去操作!

咱们这里次要解说 乐观锁机制!

乐观锁实现形式:

  • 取出记录时,获取以后 version
  • 更新时,带上这个 version
  • 执行更新时,set version = newVersion where version = oldVersion
  • 如果 version 不对,就更新失败
 乐观锁:1、先查问,取得版本号 version = 1
-- A
update user set name = "mingrenyu", version = version + 1
where id = 2 and version = 1

-- B 线程领先实现,这个时候 version = 2,会导致 A 批改失败!update user set name = "mingrenyu", version = version + 1
where id = 2 and version = 1
    

测试一下 MyBatis-Plus 的乐观锁插件

1、给数据库减少 verison 字段!设置默认值为 1

2、实体类加对应的字段

// 乐观锁 version 注解
@Version 
private Integer version;

3、注册组件

@MapperScan("com.zwk.mapper") // 扫描咱们的 mapper 文件夹
@EnableTransactionManagement // 开启事务(默认)@Configuration // 配置类
public class MyBatisPlusConfig {

    // 注册乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();
    }
}

4、测试一下

// 测试乐观锁胜利!@Test
    public void testOptimisticLocker(){
        // 1、查问用户信息
        User user = userMapper.selectById(1L);
        // 2、批改用户信息
        user.setName("mingrenyu");
        user.setEmail("66668888@qq.com");
        // 3、执行更新操作
        userMapper.updateById(user);
    }

    // 测试乐观锁失败!多线程下
    @Test
    public void testOptimisticLocker2(){

        // 线程 1
        User user = userMapper.selectById(2L);
        user.setName("mingrenyu111");
        user.setEmail("66668888@qq.com");

        // 模仿另外一个线程执行了插队操作
        User user2 = userMapper.selectById(2L);
        user2.setName("mingrenyu222");
        user2.setEmail("66668888@qq.com");
        userMapper.updateById(user2);

        // 解决思路:应用自旋锁来屡次尝试提交!userMapper.updateById(user); // 如果没有乐观锁就会笼罩插队线程的值!}

查问操作

// 测试查问 - 单个
@Test
public void testSelectById(){User user = userMapper.selectById(1L);
}

// 测试查问 - 批量
@Test
public void testSelectByBatchId(){List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

// 按条件查问之一  应用 map 操作
@Test
public void testSelectByBatchIds(){HashMap<String, Object> map = new HashMap<>();
    // 自定义查问
    map.put("name","iu");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

分页查问

分页在网站应用的十分多!

1、原始的 limit 进行分页

2、第三方插件 pageHelper

3、MyBatis-Plus 内置了分页插件!

如何应用!

1、配置拦截器组件即可

// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();
}

2、间接应用 Page 对象即可

// 测试分页查问
@Test
public void testPage(){
    // 参数一:当前页
    // 参数二:页面条数
    // 应用了分页插件之后,所有的分页操作也变得简略了!Page<User> page = new Page<>(2,5);
    userMapper.selectPage(page,null);

    page.getRecords().forEach(System.out::println);
    System.out.println(page.getTotal());
}

删除操作

根本的删除操作

// 测试删除
@Test
public void testDeleteById() {userMapper.deleteById(1279694588429324355L);
}

// 通过 id 批量删除
@Test
public void testDeleteBatchId() {userMapper.deleteBatchIds(Arrays.asList(1279694588429324352L, 1279694588429324353L, 1279694588429324354L));
}

// 通过 map 批量删除
@Test
public void testDeleteMap() {HashMap<String, Object> map = new HashMap<>();
    // 自定义删除
    map.put("name", "狂神说 Java");
    userMapper.deleteByMap(map);
}

在工作中会遇到一些问题:逻辑删除!

逻辑删除

物理删除:从数据库中间接移除

逻辑删除:在数据库中没有被移除,而是通过一个变量来让他生效!delete = 0 => delete = 1

场景:管理员能够查看被删除的记录!避免数据的失落,相似于回收站!

测试一下:

1、在数据库中减少一个 delete 字段

2、实体类中减少属性

// 逻辑删除
@TableLogic
private Integer delete;

3、配置!

// 逻辑删除组件
@Bean
public ISqlInjector sqlInjector() {return new LogicSqlInjector();
}
# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

4、测试逻辑删除

记录仍旧在数据库,然而值确曾经变动了

以上所有的 CRUD 及其扩大操作,会大大提高工作和写我的项目的效率!

性能剖析插件

咱们在平时的开发中,会遇到一些慢 sql。

MyBatis-Plus 也提供了性能剖析插件,如果超过这个工夫就报错,进行运行!

作用:性能剖析拦截器,用于输入每条 SQL 语句及其执行工夫

== 留神:== 该插件在 3.2.0 以上版本以移除,举荐应用第三方扩大 执行 SQL 剖析打印 性能!

1、导入插件

// SQL 执行效率插件
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启,保障咱们的效率
public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100); // ms 设置 sql 执行的最大工夫,如果超过了则不执行
    performanceInterceptor.setFormat(true); // 是否格式化代码
    return performanceInterceptor;
}

2、配置开发环境为 dev 或者 test

开发环境(dev):是程序猿们专门用于开发的服务器,配置能够比拟随便,为了开发调试不便,个别关上全副错误报告。

测试环境 (test):个别是克隆一份生产环境的配置,一个程序在测试环境工作不失常,那么必定不能把它公布到生产机上。

生产环境(prod):是值正式提供对外服务的,个别会关掉错误报告,关上谬误日志。

三个环境也能够说是零碎开发的三个阶段:开发 -> 测试 -> 上线,其中生产环境也就是通常说的实在环境。

# 设置开发环境
spring.profiles.active=dev

3、测试应用!

使用性能剖析插件,能够帮忙咱们提高效率!

条件结构器

非常重要:Wrapper

咱们写一些简单的 sql 就能够应用它来代替!

1、测试一,记住查看输入的 SQL 进行剖析

@Test
void contextLoads() {
    // 查问 name 不为空的用户,并且邮箱不为空的用户,年龄大于等于 12
    QueryWrapper<User> Wrapper = new QueryWrapper<>();
    Wrapper
            .isNotNull("name")
            .isNotNull("email")
            .ge("age", 12);
    userMapper.selectList(Wrapper).forEach(System.out::println);
}

2、测试二,记住查看输入的 SQL 进行剖析

@Test
void test2(){
    // 查问名字 鸣人语
    QueryWrapper<User> Wrapper = new QueryWrapper<>();
    Wrapper.eq("name","鸣人语");
    User user = userMapper.selectOne(Wrapper); // 查问一个数据,呈现多个后果应用 List 或者 Map
    System.out.println(user);
}

3、测试三,记住查看输入的 SQL 进行剖析

@Test
void test3(){
    // 查问年龄在 20 ~ 30 岁之间的用户
    QueryWrapper<User> Wrapper = new QueryWrapper<>();
    Wrapper.between("age",20,30); // 区间
    Integer count = userMapper.selectCount(Wrapper); // 查问后果数
    System.out.println(count); 

}

4、测试四,记住查看输入的 SQL 进行剖析

@Test
void test4(){
    // 含糊查问
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper
            .notLike("name","e")
            .likeRight("email","t");
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}

5、测试五,记住查看输入的 SQL 进行剖析

@Test
void test5() {
    // id 在子查问中查出来
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id","select id from user where id<4");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

6、测试六,记住查看输入的 SQL 进行剖析

@Test
void test6() {
    // 通过 id 进行排序
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByDesc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

余下很多办法能够查看 https://mp.baomidou.com/guide…

代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 能够疾速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的晋升了开发效率。

1、增加 代码生成器 依赖

<!-- mybatis-plus 代码生成器的 模板引擎 -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.0</version>
    </dependency>

2、进行配置

// 代码主动生成器
public class MyAutoGenerator {public static void main(String[] args) {
        // 须要构建一个 代码主动生成器 对象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略

        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("鸣人语");
        gc.setOpen(false);
        gc.setFileOverride(false); // 是否笼罩
        gc.setServiceName("%sService"); // 去 Service 的 I 前缀
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        // 2、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Hongkong");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 3、包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.zwk");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);

        // 4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user"); // 设置要映射的表名(重要)//strategy.setInclude("user","user_record","user_say"); // 设置要映射的表名(重要)生成多表!!!strategy.setNaming(NamingStrategy.underline_to_camel); // 下划线 转 驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true); // 主动 Lombok
        strategy.setLogicDeleteFieldName("deleted");

        // 主动填充配置
        TableFill gmtCreat = new TableFill("gmt_creat", FieldFill.INSERT);
        TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreat);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        // 乐观锁
        strategy.setVersionFieldName("version");
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true); // Localhost:8080/hello_id_2
        mpg.setStrategy(strategy);

        mpg.execute(); // 执行}

}

3、测试后果如下

正文完
 0