Mybatis-plus 上


Mybatis-plus 上

簡介

1.什么是Mybatis-plus

MyBatis-Plus(簡稱 MP)是一個 MyBatis的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。

官網:https://baomidou.com/

願景

我們的願景是成為 MyBatis 最好的搭檔,就像魂斗羅中的 1P、2P,基友搭配,效率翻倍。

1

2.特性

  • 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
  • 損耗小:啟動即會自動注入基本 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 操作智能分析阻斷,也可自定義攔截規則,預防誤操作

快速入門

我們將通過一個簡單的 Demo 來闡述 MyBatis-Plus 的強大功能,在此之前,我們假設您已經:

  • 擁有 Java 開發環境以及相應 IDE
  • 熟悉 Spring Boot
  • 熟悉 Maven

1.數據庫

創建一個mybatis-plus數據庫

現有一張 User 表,其表結構如下:

id name age email
1 小張 18 test1@qq.com
2 小王 20 test2@163.com
3 小李 25 test3@qq.com
4 小馳 21 test4@qq.com
5 小劉 24 test5@163.com
  • 對應的數據庫 Schema 腳本如下:
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(樂觀鎖)、deleted(邏輯刪除)、gmt_create、gmt_modified

  • 其對應的數據庫 Data 腳本如下:
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, '小張', 18, 'test1@qq.com'),
(2, '小王', 20, 'test2@163.com'),
(3, '小李', 25, 'test3@qq.com'),
(4, '小馳', 21, 'test4@qq.com'),
(5, '小劉', 24, 'test5@163.com');

2.初始化工程

創建一個SpringBoot項目:創建時選擇 starter-web 依賴。

2.1 導入依賴

<!-- 數據庫驅動  -->
<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.1.0</version>
</dependency>

使用mybatis-plus可以節省我們大量的代碼,盡量不要同時導入 mybatismybatis-plus

2.2 連接數據庫

直接在application.properties配置文件中配置:

# mysql 5   驅動不同
# mysql 8   驅動不同、需要增加時區  serverTime=UTC
spring.datasource.username=root
spring.datasource.password=148729
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false&useUnicode=true&charEncoding=utf-8&serverTime=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3.搭建項目

傳統方式:pojo---dao(連接mybatis,配置mapper.xml文件)--- service---- controller

使用了mybatis-plus:

  • pojo
  • mapper接口
  • 啟動

3.1 pojo

這里使用了lombok插件,導入lombok依賴,

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email; 
}

3.2 mapper

  • 創建一個xxxmapper接口
  • 該接口繼承BaseMapper<>,泛型為實體類
  • 加注解@Repository
@Repository
public interface UserMapper extends BaseMapper<User> {
}

3.3 啟動器

加入@MapperScan(""),對mapper包進行掃描

@SpringBootApplication
@MapperScan("com.zc.mapper")
public class MybatisPlus01QsApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlus01QsApplication.class, args);
    }
}

3.4 測試類

@SpringBootTest
class MybatisPlus01QsApplicationTests {
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void contextLoads() {
        //查詢全部用戶
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::print;
    }
}
2

4.配置日志

在配置文件中進行配置:

# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

測試運行:

3

插入操作

1.插入數據

@Test
void insert() {
    //插入新用戶
    User user = new User();
    user.setName("咚咚");
    user.setAge(16);
    user.setEmail("111@insert.com");

    int insert = userMapper.insert(user);
    System.out.println(insert);
    System.out.println(user);
}
4

我們從上圖可以看出:主鍵自動生成

2.雪花算法

2.1 含義

SnowFlake算法是Twitter公司出品的開源的分布式id生成算法,結果是一個long型的ID
其特點為 使用一個64 bit的long型的數字作為全局唯一 id
雪花算法在分布式系統中的應用十分廣泛 且引入了時間戳 基本保持自增

2.2 字符串含義

其核心思想是:

41bit作為毫秒數

10bit作為機器的ID(5個bit是數據中心,5個bit的機器D)

12bit作為毫秒內的流水號(意味着每個節點在每毫秒可以產生4096個ID)

最后還有個符號位,永遠是0。可以保證幾乎全球唯一!

5
  • 第1位是符號位 始終為0
    (這是因為生成的id都是正數 而在二進制中第一個bit若為0則不為負數)

  • 后面是41位的時間戳 精確到毫秒級
    41位的長度可以表示2^41-1個毫秒值 也就是說可以使用69年
    時間戳還有一個很重要的作用 可以根據時間進行排序

  • 之后的10位是機器標識 前5bit是機房id 后5bit是機器id
    10位的長度表明該服務最多可以部署在2^10台機器(即1024台機器)上

  • 最后12位是計數序列號
    序列號是一系列的自增id 表示了同一個毫秒內產生的不同id
    可以支持同一節點同一毫秒生成多個id 12位的計數序列號支持每個節點每毫秒產生2^12-1(即4096)個ID序號

2.3 生成過程

  1. 若某個服務需要生成一個唯一id 則發送一個請求給部署了SnowFlake算法的系統(前提是該SnowFlake算法系統知道自己所在的機房和機器的編號)

  2. SnowFlake算法系統接收到該請求后 使用二進制位運算的方式生成一個64bit的long型id 當然 第一個bit是無意義的

  3. 接着41個bit使用當前時間戳(單位為毫秒) 然后的5bit設為該機房的id 剩余5bit設為機器的id

  4. 最后 再判斷當前機房的該機器在這一毫秒內是第幾個請求 給本次生成id的請求后再累加一個序號 作為id最后的12個bit

至此 就得到了一個64bit的唯一id 這就是雪花算法

3.主鍵自增

需要配置主鍵自增:

  1. 開啟數據庫 主鍵自增

  2. 實體類主鍵字段上 @TableId(type=IdType.AUTO)

  3. 再次測試

IdType類中枚舉解釋

AUTO(0),           // 數據庫id自增
NONE(1),           // 未設置主鍵
INPUT(2),          // 手動輸入
ID_WORKER(3),      // 默認的全局唯一id
UUID(4),           // 全局唯-id uuid
ID_WORKER_STR(5);  // ID_WORKER 字符串表示法

更新操作

@Test
void update() {
    //更新用戶
    User user = new User();
    user.setId(5L);      // id 在數據庫中設置的類型為 long
    user.setName("我不是小劉啦");
    // 雖然是ById,實際上應該傳入 泛型T
    int i = userMapper.updateById(user);
}
6

mybatis-plus 中都是自動化,自動拼接動態Sql

自動填充

創建時間修改時間,這些操作一般都是自動化完成的,不希望手動更新
阿里巴巴開發手冊:所有的數據庫表:gmt_creategmt_modified幾乎所有的表都要配置上,而且需要自動化

1.數據庫級別

如果你使用的Navicat Premium,在mysql5.5以上已經不支持兩個字段自動更新

如果覺得很麻煩,可以直接看第二種代碼級別自動填充

1、在表中新增字段create_timeupdate_time

8

9

因為不支持兩個列為timestamp類型,所以這里設置更新時間為timestamp

下面的默認必須為CURRENT_TIMESTAMP,然后打對號

如果沒有CURRENT_TIMESTAMP,可以使用兩個方法:

一、默認框下拉,選擇空白處,將這段英文復制進去 ,然后保存

二、把表刪了,運行下面的sql語句

CREATE TABLE `mybatis-plus`.`Untitled`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年齡',
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '郵箱',
  `create_time` datetime NULL DEFAULT NULL COMMENT '創建時間',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1374350451559940100 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;

2.實體類增加字段

private Date createTime;
private Date updateTime;

3.更新測試

9

10

數據庫中的更新時間也會進行更新

2.代碼級別

1.在表中新增字段create_timeupdate_time

11

2.實體類加入注解

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

3.創建配置類

@Component
@Slf4j
public class MyMetaobjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log. info("start insert fill.....");
        //setFieldValByName(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.插入測試

@Test
void insert() {
    User user = new User();
    user.setName("邦邦");
    user.setAge(20);
    user.setEmail("insert@insert.com");

    int insert = userMapper.insert(user);
    System.out.println(insert);
    System.out.println(user);
}
12

5.更新測試

@Test
void update() {
    User user = new User();
    user.setId(5L);      // id 在數據庫中設置的類型為 long
    user.setName("我bu是小劉啦");

    // 雖然是ById,實際上應該傳入 泛型T
    int i = userMapper.updateById(user);
}
13

樂觀鎖

樂觀鎖:故名思意十分樂觀,總是認為不會出現問題,無論干什么不去上鎖,先進行事務,如果出現了問題,再次更新值測試

悲觀鎖:故名思意十分悲觀,總是認為總是出現問題,無論干什么都會上鎖,再去操作

樂觀鎖實現方式:

  • 取出記錄時,獲取當前versionl
  • 更新時,帶上這個version執行
  • 更新時,set version = newVersion where version =oldVersion
  • 如果version不對,就更新失敗

舉例:

先查詢出 version,進行操作時 version + 1
線程A:
update user set name = "zc",version = version+1 where id=? and version=1

線程B:
update user set name = "zc",version = version+1 where id=? and version=1

可以看出,先查詢了老的version,在更新時version+1;

如果 線程B先於線程A完成該更新操作,那version==2,這時候線程A不成立,更新失敗

添加樂觀鎖

1.數據庫中添加version字段:int類型,全部設為 1 即可

14

2.實體類加入對應字段、注釋

@Version   //樂觀鎖Version注解
private Integer version;

3.注冊組件

@EnableTransactionManagement
@Configuration
@MapperScan("com.zc.mapper")
public class MyBatisPlusConfig {
    //注冊樂觀鎖插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

4.測試樂觀鎖

成功:

@Test
public void optimisticlocker_success(){
    //1、查詢用戶信息
    User user = userMapper.selectById(1L);
    //2、修改用戶信息
    user.setName("zc");
    user.setEmail("update@qq.com");
    //3、執行更新操作
    userMapper.updateById(user);
}

失敗:

@Test
public void optimisticlocker_fail(){
    //線程A
    User user = userMapper.selectById(1L);
    user.setName("zc");
    user.setEmail("A@qq.com");
    //線程B
    User user1 = userMapper.selectById(1L);
    user.setName("zc1");
    user.setEmail("B@qq.com");

    userMapper.updateById(user1);
    userMapper.updateById(user);
}

我們使,線程B 先於 線程A 進行更新。

這時會發現,雖然可以運行,version字段也會增加,但是並不會進行更新。

個人博客為:
MoYu's HomePage
MoYu's Gitee Blog


免責聲明!

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



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