簡介
- 是什麼? MyBatis 本來就是簡化 JDBC 操作的!
- 官網:Redirect MyBatis Plus,簡化 MyBatis !

特性
無侵入:隻做增強不做改變,引入它不會對現有工程産生影響,如絲般順滑
損耗小:啟動即會自動注入基本 CURD,性能基本無損耗,直接面向對象操作, BaseMapper
強大的 CRUD 操作:内置通用 Mapper、通用 Service,僅僅通過少量配置即可實作單表大部分
CRUD 操作,更有強大的條件構造器,滿足各類使用需求, 以後簡單的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 操作智能分析阻斷,也可自定義攔截規則,預防誤
操作
極速入門
位址:https://mp.baomidou.com/guide/quick-start.html#初始化工程
使用第三方元件:
1、導入對應的依賴
2、研究依賴如何配置
3、代碼如何編寫
4、提高擴充技術能力!
步驟
- 建立資料庫mybatis_plus
create database mybatis_plus charset = "utf8" ;
- 建立表,并注入基本資料
use mybatis_plus;
drop table if exists user;
create table user(
id BIGINT(20) not null comment '主鍵ID',
name VARBINARY(30) null DEFAULT null comment '姓名' ,
age INT(11) null default null comment '年齡' ,
email VARBINARY(50) null default null comment '郵箱' ,
primary key (id)
);
INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
- 配置依賴
<!--web場景啟動器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.1</version>
</dependency>
<!--test啟動器,并且排除vitage的junit-->
<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>
<!--資料庫連結驅動資料庫版本5.7 這裡使用6.0.6-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!--加入mybatis-plus的場景啟動器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
⚠️ Tips:說明:我們使用 mybatis-plus 可以節省我們大量的代碼,盡量不要同時導入 mybatis 和 mybatisplus!版本的差異!
- 配置yaml連結資料庫,這裡分mysql版本,注意要加時區
spring:
profiles:
active: mysql5
---
# mysql5.7 com.mysql.jdbc.Driver
spring:
config:
activate:
on-profile: mysql5
datasource:
username: root
password: 125697
url: jdbc:mysql://47.111.237.28:3388/mybatis_plus?useSSL=false&useUnicode=true&charasetEnCoding=utf-8&severTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
#配置mybatis-plus的日志,這裡直接輸出控制台
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
---
# mysql8.x com.mysql.cj.jdbc.Driver 必須要加serverTimezone=Asia/Shanghai、Asia/HongKong
spring:
config:
activate:
on-profile: mysql8
datasource:
username: root
password: 125697
url: jdbc:mysql://47.111.237.28:3388/mybatis_plus?useSSL=false&useUnicode=true&charasetEnCoding=utf-8&severTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
- 傳統方式pojo-dao(連接配接mybatis,配置mapper.xml檔案)-service-controller
- 使用了Mybatis—plus之後
- pojo
/**
* @author starrysky
* @title: User
* @projectName mybaits_plus_final
* @description: pojo-user
* @date 2021/1/3119:30
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
- mapper,繼承mybatis-plus的BaseMapper
/**
* @author starrysky
* @title: UserMapper
* @projectName mybaits_plus_final
* @description: TODO
* @date 2021/1/3119:31
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
}
- ⚠️ Tips:注意點,我們需要在主啟動類上去掃描我們的mapper包下的所有接口@MapperScan("icu.lookyousmileface.mapper")
- 測試類中測試
@SpringBootTest
class MybaitsPlusFinalApplicationTests {
@Autowired
private UserMapper userMapper;
/**
* 查詢所有的使用者
*/
@Test
void contextLoads() {
List<User> users = userMapper.selectList(null);
users.stream().forEach(System.out::println);
}
}
配置mybatis-plus的日志
#這裡直接輸出到控制台即可
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
插入
- Insert 插入
/**
* 插入使用者
*/
@Test
void userInster(){
user.setName("skystartx");
user.setAge(88);
user.setEmail("[email protected]");
int insert = userMapper.insert(user);//自動生産主鍵
System.out.println(insert);//影響行數
System.out.println(user);//顯示插入的使用者
}
⚠️ 預設插入的id主鍵預設值(IdType.NONE):是全局唯一
主鍵生成政策
- 預設 ID_WORKER 全局唯一id
- 分布式系統唯一id生成:分布式系統唯一ID生成方案彙總 - nick hao - 部落格園
-
雪花算法:
snowflflake是Twitter開源的分布式ID生成算法,結果是一個long型的ID。其核心思想是:使用41bit作為毫秒數,10bit作為機器的ID(5個bit是資料中心,5個bit的機器ID),12bit作為毫秒内的流水号(意味着每個節點在每毫秒可以産生 4096 個 ID),最後還有一個符号位,永遠是0。可以保證幾乎全球唯一!
- 可以通過@TableId 注解 選擇主鍵id的插入模式,可選的如下:
@Getter
public enum IdType {
//自增id
AUTO(0),
//預設類型全局唯一
NONE(1),
//使用者自動組冊填充插件填充
INPUT(2),
//雪花算法實作全局唯一,主鍵類型為number或者Long
ASSIGN_ID(3),
//配置設定UUID,主鍵類型為string類型
ASSIGN_UUID(4),
//過時,預設的全局唯一id
@Deprecated
ID_WORKER(3),
//過時,預設的全局唯一id string類型
@Deprecated
ID_WORKER_STR(3),
//過時,UUID
@Deprecated
UUID(4);
}
⚠️ Tips:主鍵自增,需要設定資料庫内的表的字段也是自增才可以
- 例如:設定主鍵的為雪花算法填充
- User
/**
* @author starrysky
* @title: User
* @projectName mybaits_plus_final
* @description: pojo-user
* @date 2021/1/3119:30
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
private Integer age;
private String email;
}
- 測試方法:
/**
* 插入使用者 主鍵模式為雪花算法
*/
@Test
void userInster2(){
user.setName("mantianx");
user.setAge(99);
user.setEmail("[email protected]");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println(user);
}
更新操作
- 例子:
/**
* 更新操作
*/
@Test
void userUpdate(){
user.setId(5L);
user.setAge(1024);
user.setName("哈哈哈哈哈");
int i = userMapper.updateById(user);
System.out.println(i);
System.out.println(user);
}
⚠️ Tips:updateById會根據目前設定的Id修改對應使用者的資訊。
自動填充
- 阿裡巴巴開發手冊:所有的資料庫表:gmt_create、gmt_modifified幾乎所有的表都要配置上!而且需要自動化!
- 方式一:資料庫級别(工作中不允許你修改資料庫)
- 在表中新增兩個字段create_time、update_time
alter table user add create_time datetime default CURRENT_TIMESTAMP comment '建立時間';
alter table user add update_time datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP comment '更新時間';
- 在pojo中加入這兩個屬性
private Date createTime;
private Date updateTime;
- 檢視效果
- 方式二:代碼級别
- 現将之前資料庫中的字段恢複沒有update on和預設值的兩個字段
- 在和資料庫中對應的字段上加上注解,也就是設定自動填充
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
- 編寫處理器,需要實作MetaObjectHandler接口的兩個方法
/**
* @author starrysky
* @title: MyMataHandler
* @projectName mybaits_plus_final
* @description: Meta處理器,當對應pojo的字段上設定@TableField值時,
* 觸發了inster/update時間都會更新createTime或者updateTime兩個字段
* @date 2021/1/3123:28
*/
@Slf4j
@Component
public class MyMataHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("有資料開始插入....開始更新建立時間戳");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("有資料被操作...開始更新最新的時間戳");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
樂觀鎖
-
概念:
樂觀鎖 : 故名思意十分樂觀,它總是認為不會出現問題,無論幹什麼不去上鎖!如果出現了問題,再次更新值測試
悲觀鎖:故名思意十分悲觀,它總是認為總是出現問題,無論幹什麼都會上鎖!再去操作!
- 樂觀鎖實作方式:
樂觀鎖:1、先查詢,獲得版本号 version = 1
-- A update user set name = "queue", version = version + 1 where id = 2 and version = 1
-- B 線程搶先完成,這個時候 version = 2,會導緻 A 修改失敗! update user set name = "queue", version = version + 1 where id = 2 and version = 1
- 取出記錄時,擷取目前 version
- 更新時,帶上這個version
- 執行更新時, set version = newVersion where version = oldVersion
- 如果version不對,就更新失敗
- 測試一下MP的樂觀鎖插件
- 給資料庫中增加version字段!
alter table user add version INT(11) default 1 after age;
- 我們實體類加對應的字段
@Version
private Integer version;
- 注冊元件
//開啟事務注解
@EnableTransactionManagement
@MapperScan("icu.lookyousmileface.mapper")
@Configuration
public class MybatisPlusConfig {
//樂觀鎖
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
- 測試一下!
/**
* 樂觀鎖測試
*/
@Test
void IntLock(){
User user = userMapper.selectById(1L);
user.setName("趙子龍");
user.setAge(9000);
userMapper.updateById(user);
}
/**
* 多線程下測試樂觀鎖
*/
@Test
void RunableLock(){
User user1 = userMapper.selectById(2L);
user1.setName("Thread3-changed=>success!");
//假裝有線程插隊
User user2 = userMapper.selectById(2L);
user2.setName("Thread4-changed=>success!");
userMapper.updateById(user2);
//自旋鎖多次嘗試
userMapper.updateById(user1);//沒有樂觀鎖就會被覆寫調
}
}
查詢操作
/**
* 測試查詢
*/
@Test
void selectTest(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
/**
* 批量查詢,根據id
*/
@Test
void latchBatchSelet(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
users.stream().forEach(System.out::println);
}
/**
* 按條件查詢之一使用map操作
*/
@Test
void selectMap(){
Map<String,Object> map = new ConcurrentHashMap<>();
/**
* 這兩個條件是使用And與在一起的不是分開查詢的兩個語句
*/
map.put("name","Sandy");
map.put("age",21);
List<User> users = userMapper.selectByMap(map);
users.stream().forEach(System.out::println);
}
分頁查詢
- 分頁在網站使用的十分之多!
-
分頁:
1、原始的 limit 進行分頁
2、pageHelper 第三方插件
3、MP 其實也内置了分頁插件!
- 使用:
- 配置攔截器元件
/**
* mybatis-plus的分頁插件
* @return new PaginationInterceptor()
*/
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
- 直接使用Page對象即可!
/**
* MP的分頁插件測試
*/
@Test
void limitPages(){
Page<User> page = new Page<>(3,4);
userMapper.selectPage(page,null);
page.getRecords().stream().forEach(System.out::println);
System.out.println(page.getTotal());
}
删除操作
/**
* 測試删除
*/
@Test
void deleteTest() {
int i = userMapper.deleteById(1L);
System.out.println(i);
}
/**
* 批量删除
*/
@Test
void deleteBatch() {
int i = userMapper.deleteBatchIds(Arrays.asList(3L, 1355902835564224514L));
System.out.println(i);
}
/**
* 按條件删除map使用
*/
@Test
void deleteMap() {
Map<String, Object> map = new ConcurrentHashMap<>(16, 0.75F);
map.put("age", 1024);
map.put("name", "哈哈哈哈哈2");
int i = userMapper.deleteByMap(map);
System.out.println("影響行數:"+i);
}
邏輯删除
-
概念:
實體删除 :從資料庫中直接移除
邏輯删除 :再資料庫中沒有被移除,而是通過一個變量來讓他失效! deleted = 0 => deleted = 1
管理者可以檢視被删除的記錄!防止資料的丢失,類似于資源回收筒!
- 使用:
- 資料庫中增加deleted字段預設為0,即沒有删除
alter table user add deleted INT(11) default 0 comment '邏輯删除' after age ;
- 在pojo中添加deleted字段并且标上邏輯删除的注解
@TableLogic
private Long deleted;
- 配置bean、yml
/**
* 邏輯删除插件
*/
@Bean
public ISqlInjector injector(){
return new DefaultSqlInjector();
}
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
- 再測試一下删除,會發現資料庫中還存在,但是查不到了,其實也就是在之後的查詢語句加上deleted=0罷了
性能分析插件
- P6Spy
- 使用:
- 配置p6spy的依賴
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
- 替換jdbc的驅動
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
- 修改spring.datasource.url
url: jdbc:p6spy:mariadb://127.0.0.1:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
- 添加配置檔案 spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定義日志列印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志輸出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系統記錄 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 設定 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL字首
useprefix=true
# 配置記錄 Log 例外,可去掉的結果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 實際驅動可多個
#driverlist=org.h2.Driver
# 是否開啟慢SQL記錄
outagedetection=true
# 慢SQL記錄标準 2 秒
outagedetectioninterval=2
條件構造器
- 十分重要:Wrapper
- 我們寫一些複雜的sql就可以使用它來替代!
/**
* 條件構造器,isNotNull非空、get大于等于
*/
@Test
void queryWrapper1 () {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("age")
.isNotNull("email")
.ge("age", 22);
List<User> users = userMapper.selectList(wrapper);
users.stream().forEach(System.out::println);
}
/**
* 條件構造器,eq、selectOne
*/
@Test
void queryWrapper2(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","Sandy");
User user = userMapper.selectOne(wrapper);// 查詢一個資料,出現多個結果使用List 或者 Map
System.out.println(user);
}
/**
* 條件構造器 ,between
*/
@Test
void queryWrapper3(){
QueryWrapper<User> wrappper = new QueryWrapper<>();
wrappper.between("age",21,99);
List<User> users = userMapper.selectList(wrappper);
users.stream().forEach(System.out::println);
}
/**
* 條件構造器,like比對
*/
@Test
void queryWrapper4() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
/**
* %{}%類型
*/
// QueryWrapper<User> like = queryWrapper.like("name", "這");
/**
* %{}%類型,比對到就排出比對到到反向選擇
*/
// QueryWrapper<User> wrapper = queryWrapper.notLike("name", "這");
/**
* {}%類型,從右邊比對
*/
QueryWrapper<User> wrapper = queryWrapper.likeRight("name", "這");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
for (Map<String, Object> value : maps) {
System.out.println(value);
}
}
/**
* 模糊查詢,子查詢
*/
@Test
void queryWrapper5(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id" , "select id from user where id>50");
// List<User> users = userMapper.selectList(wrapper);
/**
* 隻傳回第一個字段的值
*/
List<Object> objects = userMapper.selectObjs(queryWrapper);
objects.forEach(System.out::println);
}
/**
* 排序 ASC正序,DESC倒叙
*/
@Test
void queryWrapper6(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.orderByAsc("id");
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}