Mybatis-Plus(基礎)


一、Mybatis-Plus概述

1.1、Plus簡介

MyBatis-Plus(簡稱 MP)由baomidou(苞米豆)組織開發並且開源的一個項目,是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。

image-20201002095744290

mybatis-plusde與mybatis的關系就像是魂斗羅P1玩家和P2玩家的關系,Mp的功能是基於Mybatis的,是在其基礎上做進一步的封裝和完善,在一定程度上簡化了sql的編寫,自動生成了基本的CRUD方法。基友搭配,效率翻倍

image-20201002100036517

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的架構

image-20201002101035095

二、快速開始(基於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);
    }
}

結果如下:

image-20201002113803636

可以看到我們沒有寫Mapper.xml文件也沒通過注解的方式寫sql語句,直接調用selectList()方法就自動生成了查詢所有的sql,並自動處理了結果集,即便實體類的屬性有些和mysql的列字段有些不一致。

可以留意下User實體類會發現上面有個注解,這個注解指定了這個實體類與數據庫的哪張表關聯,我們所查詢的表也正是這張表。

image-20201002122809323

三、注解

注解類包:👉 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 的自動構建並注入)

image-20201002123659594

@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 "" 指定小數點后保留的位數

image-20201002124936403

FieldStrategy

描述
IGNORED 忽略判斷
NOT_NULL 非NULL判斷
NOT_EMPTY 非空判斷(只對字符串類型字段,其他類型字段依然為非NULL判斷)
DEFAULT 追隨全局配置

FieldFill

描述
DEFAULT 默認不處理
INSERT 插入時填充字段
UPDATE 更新時填充字段
INSERT_UPDATE 插入和更新時填充字段

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

四、通用CRUD

​ UserMapper集成了BaseMapper接口,在接口中Mp預先定義了一些通用的CRUD

方法,使用這些方法不用寫SQL語句,Mp會自動為我們生成SQL。

image-20201002132112009

4.1、插入操作

​ 在實體類id上添加注解並指定類型為自動增長(也可以在yaml配置全局的類型)image-20201002133319951

@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());
    }

結果如下,成功插入一條數據,並且還會返回最后一條記錄的主鍵自增值

image-20201002134016789

如果將IdType.AUTO改為IdType.ASSIGN_ID,再插入數據image-20201002134200453

結果如下:id會通過雪花算法生成全球唯一的id

image-20201002134321171

image-20201002134409919

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

結果如下:不為空的字段進行更新

image-20201002135724865

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

結果如下:

image-20201002140436964

QueryWrapper和UpdateWrapper都是Wrapper的實現類

image-20201002140619538

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+"條");
    }

結果如下:后面兩個刪除語句沒有刪除數據是因為沒有匹配的條件

image-20201002142410380

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

結果如下:

image-20201002143618264

image-20201002143630477

image-20201002143640826

image-20201002143646712

BaseMapper的查詢方法不僅僅四個,其中selectPage和selectMapsPage,為分頁查詢。

image-20201002143927207

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為總頁數

image-20201002144222889

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的並將結果按年齡降序

image-20201002145929191

五、SQL的注入原理

MP在啟動后會將BaseMapper中的一系列的方法注冊到meppedStatements中,其中ISqlInjector接口就是負責Sql的注入工作,AbstractSqlInjector是其實現類。

image-20201002153033357

image-20201002155109767

inject方法調用了injectMappedStatement方法

image-20201002155235637

injectMappedStatement是一個抽象方法,它由具體的實現類實現

image-20201002155414902

image-20201002155506570

每一個被自動注入的方法都有一個類對其進行注入,以SelectList類為例

image-20201002160711079

image-20201002155756079

六、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接口的各種實現類如下:

image-20201002163643566

image-20201002164010559

  • 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:排序

更多請看官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM