Mybatis-plus 上
簡介
1.什么是Mybatis-plus
MyBatis-Plus(簡稱 MP)是一個 MyBatis的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。
願景
我們的願景是成為 MyBatis 最好的搭檔,就像魂斗羅中的 1P、2P,基友搭配,效率翻倍。

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 | |
---|---|---|---|
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可以節省我們大量的代碼,盡量不要同時導入 mybatis 和 mybatis-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;
}
}

4.配置日志
在配置文件中進行配置:
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
測試運行:

插入操作
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);
}

我們從上圖可以看出:主鍵自動生成
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。可以保證幾乎全球唯一!

-
第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 生成過程
-
若某個服務需要生成一個唯一id 則發送一個請求給部署了SnowFlake算法的系統(前提是該SnowFlake算法系統知道自己所在的機房和機器的編號)
-
SnowFlake算法系統接收到該請求后 使用二進制位運算的方式生成一個64bit的long型id 當然 第一個bit是無意義的
-
接着41個bit使用當前時間戳(單位為毫秒) 然后的5bit設為該機房的id 剩余5bit設為機器的id
-
最后 再判斷當前機房的該機器在這一毫秒內是第幾個請求 給本次生成id的請求后再累加一個序號 作為id最后的12個bit
至此 就得到了一個64bit的唯一id 這就是雪花算法
3.主鍵自增
需要配置主鍵自增:
-
開啟數據庫 主鍵自增
-
實體類主鍵字段上 @TableId(type=IdType.AUTO)
-
再次測試
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);
}

mybatis-plus 中都是自動化,自動拼接動態Sql
自動填充
創建時間、修改時間,這些操作一般都是自動化完成的,不希望手動更新
阿里巴巴開發手冊:所有的數據庫表:gmt_create,gmt_modified幾乎所有的表都要配置上,而且需要自動化
1.數據庫級別
如果你使用的Navicat Premium,在mysql5.5以上已經不支持兩個字段自動更新
如果覺得很麻煩,可以直接看第二種代碼級別自動填充
1、在表中新增字段create_time,update_time
因為不支持兩個列為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.更新測試
數據庫中的更新時間也會進行更新
2.代碼級別
1.在表中新增字段create_time,update_time
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);
}

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

樂觀鎖
樂觀鎖:故名思意十分樂觀,總是認為不會出現問題,無論干什么不去上鎖,先進行事務,如果出現了問題,再次更新值測試
悲觀鎖:故名思意十分悲觀,總是認為總是出現問題,無論干什么都會上鎖,再去操作
樂觀鎖實現方式:
- 取出記錄時,獲取當前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 即可
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