首页 > 其他 > 详细

Mybatis-Plus(基础)

时间:2020-10-02 22:47:06      阅读:69      评论:0      收藏:0      [点我收藏+]

一、Mybatis-Plus概述

1.1、Plus简介

? MyBatis-Plus(简称 MP)由baomidou(苞米豆)组织开发并且开源的一个项目,是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

技术分享图片

mybatis-plusde与mybatis的关系就像是魂斗罗P1玩家和P2玩家的关系,Mp的功能是基于Mybatis的,是在其基础上做进一步的封装和完善,在一定程度上简化了sql的编写,自动生成了基本的CRUD方法。基友搭配,效率翻倍

技术分享图片

gitee:ttps://gitee.com/baomidou/mybatis-plus

GitHub:https://github.com/baomidou/mybatis-plus

文档地址:https://mybatis.plus/guide/

1.2、MP的特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  • 支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操 作

  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  • 支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词

  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List查询

  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

  • 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击

1.3、MP的架构

技术分享图片

二、快速开始(基于Springboot)

2.1、搭建环境

--建库的sql语句
CREATE TABLE `tb_user` ( 
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘主键ID‘, 	`user_name` varchar(20) NOT NULL COMMENT ‘用户名‘,
    `password` varchar(20) NOT NULL COMMENT ‘密码‘, 
    `name` varchar(30) DEFAULT NULL COMMENT ‘姓名‘, 
    `age` int(11) DEFAULT NULL COMMENT ‘年龄‘, 
    `email` varchar(50) DEFAULT NULL COMMENT ‘邮箱‘, 
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 
-- 插入测试数据 
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES (‘1‘, ‘tom‘, ‘123456‘, ‘汤姆‘, ‘18‘, ‘tom@qq.cn‘); 
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES (‘2‘, ‘rose‘, ‘123456‘, ‘柔丝‘, ‘20‘, ‘rose@qq.cn‘);
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES (‘3‘, ‘jack‘, ‘123456‘, ‘杰克‘, ‘28‘, ‘jack@qq.cn‘); 
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES (‘4‘, ‘bill‘, ‘123456‘, ‘比尔‘, ‘21‘, ‘bill@qq.cn‘); 
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES (‘5‘, ‘fack‘, ‘123456‘, ‘法克‘, ‘24‘, ‘fake@qq.cn‘);
<!-- pom依赖 -->
<dependencies>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
      <!--mybatis-plus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

yaml配置:

spring:
  datasource:
    username: ‘root‘
    password: ‘root‘
    url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  # configuration下的是mybatis的配置
  configuration:
    # 打印sql日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

@SpringBootApplication
@MapperScan("com..mybatisplus.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(QuickStartApplication.class, args);
    }
}

编写User.java实体类

@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_user")
public class User extends Model<User> {
    private Long id;
    //注意userName的属性和数据库列不一样,mysql中是user_name
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private String email;
}

? Mapper类UserMapper.java

/**
Mapper接口需要继承BaseMapper<>接口,这样就完整了springboot和Mp的整合
*/
@Repository
public interface UserMapper extends BaseMapper<User> {

}

以上就完成了springboot正和MP的过程,和整合Mybatis基本没有差异,唯独Mapper接口继承了BaseMapper接口,获取一些通用的CRUD操作。

测试查询所有:

@SpringBootTest
@RunWith(SpringRunner.class)
public class MybatisPlusDemo1 {
    @Autowired
    private UserMapper userMapper;
    @Test
    public  void testSelect1(){
        List<User> userDemos = userMapper.selectList(null);
        //Assert.assertEquals(5, studentUsers.size());
        userDemos.forEach(System.out::println);
    }
}

结果如下:

技术分享图片

可以看到我们没有写Mapper.xml文件也没通过注解的方式写sql语句,直接调用selectList()方法就自动生成了查询所有的sql,并自动处理了结果集,即便实体类的属性有些和mysql的列字段有些不一致。

可以留意下User实体类会发现上面有个注解,这个注解指定了这个实体类与数据库的哪张表关联,我们所查询的表也正是这张表。

技术分享图片

三、注解

注解类包:?? mybatis-plus-annotation

@TableName

  • 描述:表名注解

常用的属性就是value,指定表名。也可以在application.yaml中配置全局的表前缀

mybatis-plus:
  global-config:
    db-config:
      #表名前缀,全局配置后可省略@TableName()配置
      table-prefix: tb_

实体类是User,默认关联的表名为user,配置全局的表前缀后,就关联tb_user表

属性 类型 必须指定 默认值 描述
value String "" 表名
schema String "" schema
keepGlobalPrefix boolean false 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)
resultMap String "" xml 中 resultMap 的 id
autoResultMap boolean false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)

技术分享图片

@TableId

  • 描述:主键注解

value指定主键在表中的列名,默认和属性名相同。

id-type为主键的类型,也可以在yaml配置文件中配置全局的主键类型:

mybatis-plus:
 global-config:
   db-config:
  	#全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。默认值ASSIGN_ID
  	id-type: auto
属性 类型 必须指定 默认值 描述
value String "" 主键字段名
type Enum IdType.NONE 主键类型

IdType

描述
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方法)
ID_WORKER(已过时) 分布式全局唯一ID 长整型类型(please use ASSIGN_ID)
UUID(已过时) 32位UUID字符串(please use ASSIGN_UUID)
ID_WORKER_STR(已过时) 分布式全局唯一ID 字符串类型(please use ASSIGN_ID)

@TableField

  • 描述:字段注解(非主键)

在MP中通过@TableField注解与数据库表中非主键的字段关联,常常解决的问题有2个:

1、对象中的属性名和字段名不一致的问题(非驼峰)

2、对象中的属性字段在表中不存在的问题(exist=false)

默认开启了驼峰命名规则映射,也可以在yaml文件中配置关闭或者开启

# 开启驼峰命名规则映射,对象属性为驼峰命令,表字段为_A_COLUMN(下划线命名)(默认也是打开的)
# 例如实体类userName字段关联表中user_name的列
mybatis-plus:
  # configuration下的是mybatis的配置
  configuration:
	map-underscore-to-camel-case: true
属性 类型 必须指定 默认值 描述
value String "" 数据库字段名
el String "" 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分
exist boolean true 是否为数据库表字段
condition String "" 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考
update String "" 字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性)
insertStrategy Enum N DEFAULT 举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategy Enum N DEFAULT 举例:IGNORED: update table_a set column=#{columnProperty}
whereStrategy Enum N DEFAULT 举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=‘‘">column=#{columnProperty}</if>
fill Enum FieldFill.DEFAULT 字段自动填充策略
select boolean true 是否进行 select 查询
keepGlobalFormat boolean false 是否保持使用全局的 format 进行处理
jdbcType JdbcType JdbcType.UNDEFINED JDBC类型 (该默认值不代表会按照该值生效)
typeHandler Class<? extends TypeHandler> UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效)
numericScale String "" 指定小数点后保留的位数

技术分享图片

FieldStrategy

描述
IGNORED 忽略判断
NOT_NULL 非NULL判断
NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
DEFAULT 追随全局配置

FieldFill

描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段

更多注解和配置可以查看官方文档注解基本配置

四、通用CRUD

? UserMapper集成了BaseMapper接口,在接口中Mp预先定义了一些通用的CRUD

方法,使用这些方法不用写SQL语句,Mp会自动为我们生成SQL。

技术分享图片

4.1、插入操作

? 在实体类id上添加注解并指定类型为自动增长(也可以在yaml配置全局的类型)技术分享图片

@Test
    public  void testInsert(){
        User user = new User();
        user.setAge(20);
        user.setEmail("caocao@qq.cn");
        user.setName("曹操");
        user.setUserName("caocao");
        user.setPassword("123456");
        int insert = userMapper.insert(user);
        System.out.println("自增id:"+ user.getId());
    }

结果如下,成功插入一条数据,并且还会返回最后一条记录的主键自增值

技术分享图片

如果将IdType.AUTO改为IdType.ASSIGN_ID,再插入数据技术分享图片

结果如下:id会通过雪花算法生成全球唯一的id

技术分享图片

技术分享图片

4.2、更新操作

4.2.1、根据id删除

 @Test
    public void testUpdate(){
        User user = new User();
        user.setId(6l);
        user.setEmail("liubei@qq.cn");
        user.setName("刘备");
        user.setUserName("liubei");
        //更新不为null的字段
        userMapper.updateById(user);
    }

结果如下:不为空的字段进行更新

技术分享图片

4.2..2、根据条件更新

 	@Test
    public void testUpdate(){
        User user = new User();
        user.setAge(25);
        //更新的条件
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("id", 6);
        //更新不为null的字段
        userMapper.update(user,wrapper);
		//根据UpdateWrapper删除
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", 1).set("age", 23);
        //执行更新操作
        int result = this.userMapper.update(null, updateWrapper);
    }

结果如下:

技术分享图片

QueryWrapper和UpdateWrapper都是Wrapper的实现类

技术分享图片

4.3、删除操作

 @Test
    public void testDel(){
      //根据id删除一条记录
        int i = userMapper.deleteById(5);

        //批量删除操作
        int i1 = userMapper.deleteBatchIds(Arrays.asList(1311904394600452098l, 1311904394600452099l, 1311904394600452100l));
        System.out.println("删除"+i1+"条记录");

        //根据Map条件删除
        Map<String,Object> delCondition = new HashMap<>();
        delCondition.put("id","3");
        delCondition.put("name","jack");
        int i2 = userMapper.deleteByMap(delCondition);
        System.out.println("删除"+i2+"条记录");

        //根据Wrapper删除
        QueryWrapper<User> wrapper = new QueryWrapper();
        Map<String,Object> map = new HashMap<>();
        map.put("id",1);
        map.put("name","tom");
        wrapper.allEq(map);
        int delete = userMapper.delete(wrapper);
        System.out.println("删除数据:"+delete+"条");
    }

结果如下:后面两个删除语句没有删除数据是因为没有匹配的条件

技术分享图片

4.4、查询操作

		@Test
        public void testSelect(){
          userMapper.selectBatchIds(Arrays.asList(1,2,3));
          userMapper.selectById(2);
          userMapper.selectCount(null);
          QueryWrapper<User> wrapper = new QueryWrapper<>();
          wrapper.eq("id",1);
          userMapper.selectOne(wrapper);
        }

结果如下:

技术分享图片

技术分享图片

技术分享图片

技术分享图片

BaseMapper的查询方法不仅仅四个,其中selectPage和selectMapsPage,为分页查询。

技术分享图片

 		@Test
        public void testPageSelect(){
            //分页查询需要配置分页插件
            Page<User> page = new Page<>(1,3);
            IPage<User> pages = userMapper.selectPage(page, null);
            System.out.println("总记录数:"+pages.getTotal());
            System.out.println("总页数"+pages.getSize());
            pages.getRecords().forEach(System.out::println);
        }

4.4.2、分页查询

分页查询需要配置插件

@Configuration
public class MybatisPlusConfig  {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}
@Test
        public void testPageSelect(){
            //分页查询需要配置分页插件
            Page<User> page = new Page<>(1,3);
            IPage<User> pages = userMapper.selectPage(page, null);
            System.out.println("总记录数:"+pages.getTotal());
            System.out.println("总页数"+pages.getSize());
            pages.getRecords().forEach(System.out::println);
        }

结果如下:Records代表查询的集合,Total为总记录数,size为总页数

技术分享图片

4.4.3、模糊查询

MP的各种复杂的条件都需要使用Wrapper来实现

        @Test
    public void testLikeQuery(){
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.like("user_name","o");
            wrapper.orderByDesc(true,"age");
            List<User> userDemos = userMapper.selectList(wrapper);
            userDemos.forEach(System.out::println);
        }

结果如下:模糊查询user_name中带o的并将结果按年龄降序

技术分享图片

五、SQL的注入原理

MP在启动后会将BaseMapper中的一系列的方法注册到meppedStatements中,其中ISqlInjector接口就是负责Sql的注入工作,AbstractSqlInjector是其实现类。

技术分享图片

技术分享图片

inject方法调用了injectMappedStatement方法

技术分享图片

injectMappedStatement是一个抽象方法,它由具体的实现类实现

技术分享图片

技术分享图片

每一个被自动注入的方法都有一个类对其进行注入,以SelectList类为例

技术分享图片

技术分享图片

六、Mp的基本配置

Mybatis-Plus中有许多的配置有一部分是mybatis原生的配置,有一部分是mybatis的配置(mybatis-plus.configuration下的配置就是mybatis的配置)更过配置请查看官方文档

mybatis-plus:
  # configuration下的是mybatis的配置
  configuration:
    # 打印sql日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 开启驼峰命名规则映射,对象属性为驼峰命令,表字段为_A_COLUMN(下划线命名)
    map-underscore-to-camel-case: true
    # 缓存开启(默认开启)
    cache-enabled: true
    # 这下面的是mybatis-plus的配置,对应配置类MybatisPlusProperties
    # mapper文件的位置,Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
  mapper-locations: classpath*:/mapper/*.xml
 # config-location:   # mybatis配置文件的位置
  # pojo包下起别名
  type-aliases-package: com.mybatisplus.pojo
  # 全局配置
  global-config:
    #db策略配置
    db-config:
      #全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。默认值ASSIGN_ID
      id-type: auto
      #表名前缀,全局配置后可省略@TableName()配置
      table-prefix: tb_

七、条件构造器Wrapper

Wrapper接口的各种实现类如下:

技术分享图片

技术分享图片

  • eq:等于 =

  • allEq:

  • ne:不等于 <>

  • gt:大于 >

  • ge:大于等于 >=

  • lt:小于 <

  • le:小于等于 <=

  • between:BETWEEN 值1 AND 值2

  • notBetween:NOT BETWEEN 值1 AND 值2

  • isNull: is null

  • isNotNull: is not null

  • or:or(各个条件之间默认使用and连接)

  • in:字段 IN (value.get(0), value.get(1), ...)

  • notIn:字段 NOT IN (v0, v1, ...)

  • like: like‘%值%‘

  • likeLeft: like ‘%值‘

  • likeRight: like ‘值%‘

  • groupBy:分组

  • orderBy:排序

更多请看官方文档

Mybatis-Plus(基础)

原文:https://www.cnblogs.com/myblogstart/p/13762178.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!