簡介
MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生。
特性
- 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
- 損耗小:啓動即會自動注入基本 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 操作智能分析阻斷,也可自定義攔截規則,預防誤操作
快速上手
首先需要一個springboot 項目
引入MyBatis-Plus 的maven依賴(舊項目注意刪除mybatis 相關的依賴):
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
在application.yml 裏面配置你的mysql 連接信息和Mybatis-Plus 配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/central_certification?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
mybatis-plus:
//自定義sql 路徑
mapper-locations: classpath*:/mapper/**/*.xml
我們在對應數據庫新建一個user 表:
CREATE TABLE `central_certification`.`user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '用戶表自增id',
`user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用戶名稱',
`mail` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用戶郵箱',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '用戶密碼',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否刪除(0 正常 1 刪除)',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '創建時間',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新時間',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_mail_password`(`mail`) USING BTREE COMMENT '郵箱索引'
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
// 插入數據
INSERT INTO `user` VALUES (5, 'wt', '[email protected]', '123456', 0, '2019-12-27 16:37:46', '2019-12-30 12:43:56');
INSERT INTO `user` VALUES (6, '唐江席', '[email protected]', '123456', 0, '2019-12-27 17:22:23', '2019-12-27 17:22:23');
在bean 目錄下新建User 類:
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Builder
@TableName("user")
public class User extends Model<User> {
@TableId(type = IdType.AUTO)
private Long id;
private String userName;
private String password;
private String mail;
private Integer deleted;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@TableName 用於指定類對應的數據庫
@TableId 用於指定自增id 及其id 生產規則:
// 可指定的規則
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4),
/** @deprecated */
@Deprecated
ID_WORKER(3),
/** @deprecated */
@Deprecated
ID_WORKER_STR(3),
/** @deprecated */
@Deprecated
UUID(4);
Model<T> 類是Mybtais-Plus 的特性支持之一
支持 ActiveRecord 模式:支持 ActiveRecord 形式調用,實體類只需繼承 Model 類即可進行強大的 CRUD 操作
什麼意思呢,就是說我們可以直接使用類的對象來實現CRUD 操作,可以看看Model<T> 類中的方法:
這些方法不但方便還做了比較完備的檢查,比如insertOrUpdate
// 根據id 來判斷是否存在,存在更新,否則新增
public boolean insertOrUpdate() {
return !StringUtils.checkValNull(this.pkVal()) && !Objects.isNull(this.selectById(this.pkVal())) ? this.updateById() : this.insert();
}
我們來測試一下:
@org.junit.Test
public void test2(){
User user = User.builder()
.userName("test")
.mail("testMail")
.password("test")
.build();
user.insert();
System.out.println(userMapper.queryByMail("testMail"));
}
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
除了繼承的方式,我們還可以通過接口來實現,在mapper 裏面新建UserMapper:
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
@Mapper 可以聲明一個mapper ,除此之外可以在啓動類指定掃描:
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
BaseMapper 也定義了一些常用的方法可以使用
@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {
@Autowired
private UserMapper userMapper;
@org.junit.Test
public void test(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(v -> {
System.out.println(v);
});
}
}
User(id=5, userName=wt, password=123456, [email protected], deleted=0, createTime=2019-12-27T16:37:46, updateTime=2019-12-30T12:43:56)
User(id=6, userName=唐江席, password=123456, [email protected], deleted=0, createTime=2019-12-27T17:22:23, updateTime=2019-12-27T17:22:23)
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
自定義sql
如果想自己寫sql ,就和mybatis 一樣
@Mapper
public interface UserMapper extends BaseMapper<User> {
User queryByMail(@Param("mail") String mail);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="queryByMail" resultType="com.example.demo.bean.User">
select *
from user
where mail = #{mail}
</select>
</mapper>
前面我們第一次測試的時候就用了這個方法,這裏就不重複展示了。
記得在application.yml 裏面配置映射
mybatis-plus:
//自定義sql 路徑
mapper-locations: classpath*:/mapper/**/*.xml
批量操作
前面的兩種方式我們發現這些方法多是些單體方法,當然還有批量的操作,在service 目錄新建一個UserServiceImpl 實現:
package com.example.demo.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.bean.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {
}
直接測試吧:
package com.example.demo.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.bean.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(SpringRunner.class)
@SpringBootTest
class UserServiceImplTest {
@Autowired
private UserMapper userMapper;
@Autowired
private UserServiceImpl userService;
@Test
public void test() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println("更新前:");
userList.forEach(v -> {
System.out.println(v);
});
// 批量更新userList
userList = userList.stream().map(user -> {
user.setUserName(user.getUserName() + "1");
return user;
}).collect(Collectors.toList());
userService.updateBatchById(userList);
List<User> userUpdateList = userMapper.selectList(queryWrapper);
System.out.println("更新後:");
userUpdateList.forEach(v -> {
System.out.println(v);
});
}
}
更新前:
User(id=5, userName=wt, password=123456, [email protected], deleted=0, createTime=2019-12-27T16:37:46, updateTime=2019-12-30T12:43:56)
User(id=6, userName=唐江席, password=123456, [email protected], deleted=0, createTime=2019-12-27T17:22:23, updateTime=2019-12-27T17:22:23)
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
更新後:
User(id=5, userName=wt1, password=123456, [email protected], deleted=0, createTime=2019-12-27T02:37:46, updateTime=2019-12-29T22:43:56)
User(id=6, userName=唐江席1, password=123456, [email protected], deleted=0, createTime=2019-12-27T03:22:23, updateTime=2019-12-27T03:22:23)
User(id=7, userName=test1, password=test, mail=testMail, deleted=0, createTime=2020-02-01T06:11:37, updateTime=2020-02-01T06:11:37)
分頁操作
使用分頁需要先引入配置,不然分頁不會生效:
//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 設置請求的頁面大於最大頁後操作, true調回到首頁,false 繼續請求 默認false
// paginationInterceptor.setOverflow(false);
// 設置最大單頁限制數量,默認 500 條,-1 不受限制
// paginationInterceptor.setLimit(500);
// 開啓 count 的 join 優化,只針對部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
XML 自定義分頁
UserMapper.java 方法內容
public interface UserMapper {//可以繼承或者不繼承BaseMapper
/**
* <p>
* 查詢 : 根據state狀態查詢用戶列表,分頁顯示
* </p>
*
* @param page 分頁對象,xml中可以從裏面進行取值,傳遞參數 Page 即自動分頁,必須放在第一位(你可以繼承Page實現自己的分頁對象)
* @param state 狀態
* @return 分頁對象
*/
IPage<User> selectPageVo(Page<?> page, Integer state);
}
UserMapper.xml 等同於編寫一個普通 list 查詢,mybatis-plus 自動替你分頁
<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
SELECT id,name FROM user WHERE state=#{state}
</select>
Mybatis-Plus 分頁
使用Mybatis-Plus 分頁可能會報錯提示無法代理
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.service.UserServiceImplTest':
Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userServiceImpl' is expected to be of type
'com.example.demo.service.UserServiceImpl' but was actually of type 'com.sun.proxy.$Proxy76'
需要在啓動類指定使用cglib
/**
* proxy-target-class屬性值決定是基於接口的還是基於類的代理被創建。
* 如果proxy-target-class 屬性值被設置爲true,那麼基於類的代理將起作用
* (這時需要cglib庫)。如果proxy-target-class屬值被設置爲false或者這個
* 屬性被省略,那麼標準的JDK 基於接口的代理
*
*/
@EnableAsync(proxyTargetClass = true)
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Test
public void pageTest(){
int pageNum = 1;
int pageSize = 2;
Page<User> page = new Page<>(pageNum, pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
System.out.println("記錄:");
userPage.getRecords().forEach(v -> {
System.out.println(v);
});
System.out.println("當前頁碼" + userPage.getCurrent());
System.out.println("總數" + userPage.getTotal());
}
記錄:
User(id=5, userName=wt1, password=123456, [email protected], deleted=0, createTime=2019-12-27T02:37:46, updateTime=2019-12-29T22:43:56)
User(id=6, userName=唐江席1, password=123456, [email protected], deleted=0, createTime=2019-12-27T03:22:23, updateTime=2019-12-27T03:22:23)
當前頁碼1
總數3
可見總數爲3,只展示2 條記錄,分頁成功。
可以看一下Page 裏的參數:
總結
總的來說MyBatis-Plus 就是一個增強版的mybatis,它整合很多比較好用的功能,比如類似Example 的動態sql、generate 的自動生成各個層級的代碼、還有防攻擊等新功能,最關鍵是MyBatis-Plus 是非侵入的,引入不需要修改舊代碼,推薦一試。
Mybatis-Plus 的其他功能後續會繼續嘗試更新。