MyBatis-Plus


MyBatis-Plus 基礎

簡介

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

  • 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑。

  • 損耗小:啟動即會自動注入基本 CURD,性能基本無損耗,直接面向對象操作。

  • 強大的 CRUD 操作:內置通用 Mapper、通用 Service,僅僅通過少量配置即可實現單表大部分 CRUD 操作,更有強大的條件構造器,滿足各類使用需求。

  • 支持 Lambda 形式調用:通過 Lambda 表達式,方便的編寫各類查詢條件,無需再擔心字段寫錯。

  • 支持主鍵自動生成:支持多達 4 種主鍵策略(內含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問題。

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式調用,實體類只需繼承 Model 類即可進行強大的 CRUD 操作。

  • 內置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來使用。

  • 內置分頁插件:基於 MyBatis 物理分頁,開發者無需關心具體操作,配置好插件之后,寫分頁等同於普通 List 查詢。

  • 分頁插件支持多種數據庫:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多種數據庫。

  • 內置性能分析插件:可輸出 SQL 語句以及其執行時間,建議開發測試時啟用該功能,能快速揪出慢查詢。

  • 內置全局攔截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規則,預防誤操作。


入門

創建數據庫和表

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `user_id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用戶名',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密碼',
  `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '0-正常,1-刪除',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改時間',
  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '創建者',
  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
  `version` int NULL DEFAULT NULL COMMENT '樂觀鎖',
  PRIMARY KEY (`user_id`) USING BTREE,
  INDEX `user_idx1_username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用戶表' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', '123456', '0', '2021-08-29 00:38:43', '2021-08-29 00:38:45', 'admin', 'admin', 1);
INSERT INTO `sys_user` VALUES (2, 'loner', '123456', '0', '2021-08-29 00:38:43', '2021-08-29 00:38:45', 'admin', 'admin', 1);
INSERT INTO `sys_user` VALUES (3, 'Bob', '123456', '0', '2021-08-29 00:38:43', '2021-08-29 00:38:45', 'admin', 'admin', 1);
INSERT INTO `sys_user` VALUES (4, 'Jack', '123456', '0', '2021-08-29 00:38:43', '2021-08-29 00:38:45', 'admin', 'admin', 1);

導入相關依賴

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.3</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--MyBatis-Plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-extension</artifactId>
        <version>3.4.3</version>
    </dependency>
    <!--Druid數據源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>
    <!--MySQL驅動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--Lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

編寫配置文件

# 數據源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.11:3306/mybatis-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
    username: root
    password: MaH00...
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

使用和測試

// 創建用戶實體表
@Data
@TableName("sys_user")
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 用戶ID
     */
    @TableId(value = "user_id", type = IdType.AUTO)
    private Long userId;

    /**
     * 用戶賬號
     */
    private String username;

    /**
     * 密碼
     */
    private String password;

    /**
     * 刪除標志(0代表存在 2代表刪除)
     */
    @TableLogic
    private String delFlag;

    /**
     * 創建者
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;

    /**
     * 創建時間
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    /**
     * 更新者
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;

    /**
     * 更新時間
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    /**
     * 樂觀鎖
     */
    private Integer version;

}


// 繼承BaseMapper接口
public interface UserMapper extends BaseMapper<User> {
}

// 編寫測試代碼
@SpringBootTest
public class UserTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test() {
        // 直接使用BaseMapper定義好的查詢方法
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

}

整個MP(簡稱)的使用過程,就是如此的簡單,當我們繼承完 BaseMapper 接口后,我們就完成了 CRUD 操作,剩余的只是針對不同的業務進行封裝使用罷了。


MyBatis-Plus 核心

CRUD 接口

Mapper CRUD 接口

通用 CRUD 封裝 BaseMapper (opens new window)接口,為 Mybatis-Plus 啟動時自動解析實體表關系映射轉換為 Mybatis 內部對象注入容器,泛型 T 為任意實體對象,參數 Serializable 為任意類型主鍵,Mybatis-Plus 不推薦使用復合主鍵約定每一張表都有自己的唯一 id 主鍵。

// 插入一條記錄
int insert(T entity);

// 根據 ID 刪除
int deleteById(Serializable id);
// 根據 entity 條件,刪除記錄
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 刪除(根據ID 批量刪除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根據 columnMap 條件,刪除記錄
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

// 根據 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);
// 根據 whereWrapper 條件,更新記錄
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);

// 根據 ID 查詢
T selectById(Serializable id);
// 根據 entity 條件,查詢一條記錄
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查詢(根據ID 批量查詢)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根據 entity 條件,查詢全部記錄
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查詢(根據 columnMap 條件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根據 Wrapper 條件,查詢全部記錄
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根據 Wrapper 條件,查詢全部記錄。注意: 只返回第一個字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根據 entity 條件,查詢全部記錄(並翻頁)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根據 Wrapper 條件,查詢全部記錄(並翻頁)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根據 Wrapper 條件,查詢總記錄數
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);


Service CRUD 接口

通用 Service CRUD 封裝 IService (opens new window)接口,進一步封裝 CRUD,采用 get 查詢單行,remove 刪除 ,list 查詢集合,page 分頁,前綴命名方式區分 Mapper 層避免混淆,泛型 T 為任意實體對象。

建議如果存在自定義通用 Service 方法的可能,請創建自己的 IBaseService 繼承 Mybatis-Plus 提供的基類
對象 Wrapper 為 條件構造器。

Save

// 插入一條記錄(選擇字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量),bathSize指插入批次數量
boolean saveBatch(Collection<T> entityList, int batchSize);

Remove

// 根據 entity 條件,刪除記錄
boolean remove(Wrapper<T> queryWrapper);
// 根據 ID 刪除
boolean removeById(Serializable id);
// 根據 columnMap 條件,刪除記錄
boolean removeByMap(Map<String, Object> columnMap);
// 刪除(根據ID 批量刪除)
boolean removeByIds(Collection<? extends Serializable> idList);

Update

// 根據 UpdateWrapper 條件,更新記錄 需要設置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根據 whereWrapper 條件,更新記錄
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根據 ID 選擇修改
boolean updateById(T entity);
// 根據ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根據ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);

SaveOrUpdate

// TableId 注解存在更新記錄,否插入一條記錄
boolean saveOrUpdate(T entity);
// 根據updateWrapper嘗試更新,否繼續執行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

Get

// 根據 ID 查詢
T getById(Serializable id);
// 根據 Wrapper,查詢一條記錄。結果集,如果是多個會拋出異常,隨機取一條加上限制條件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根據 Wrapper,查詢一條記錄
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根據 Wrapper,查詢一條記錄
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根據 Wrapper,查詢一條記錄
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

List

// 查詢所有
List<T> list();
// 查詢列表
List<T> list(Wrapper<T> queryWrapper);
// 查詢(根據ID 批量查詢)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查詢(根據 columnMap 條件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查詢所有列表
List<Map<String, Object>> listMaps();
// 查詢列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查詢全部記錄
List<Object> listObjs();
// 查詢全部記錄
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根據 Wrapper 條件,查詢全部記錄
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根據 Wrapper 條件,查詢全部記錄
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

Page

// 無條件分頁查詢
IPage<T> page(IPage<T> page);
// 條件分頁查詢
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 無條件分頁查詢
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 條件分頁查詢
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

條件構造器

基本條件

// 等於,例如 eq("name", "老王") 等價於 name = '老王'
eq(R column, Object val)
// 不等於,例如 ne("name", "老王") 等價於 name <> '老王'
ne(R column, Object val)

// 大於,例如 gt("age", 18) 等價於 age > 18
gt(R column, Object val)
// 小於,例如 lt("age", 18) 等價於 age < 18
lt(R column, Object val)

// 大於等於,例如 ge("age", 18) 等價於 age >= 18
ge(R column, Object val)
// 小於等於,例如 le("age", 18) 等價於 age <= 18
le(R column, Object val)

// BETWEEN 值1 AND 值2,例如 between("age", 18, 30) 等價於 age between 18 and 30
between(R column, Object val1, Object val2))
// NOT BETWEEN 值1 AND 值2,例如 notBetween("age", 18, 30) 等價於 age not between 18 and 30
notBetween(R column, Object val1, Object val2)

// LIKE '%值%',例如 like("name", "王") 等價於 name like '%王%'
like(R column, Object val)
// NOT LIKE '%值%',例如 notLike("name", "王") 等價於 name not like '%王%'
notLike(R column, Object val)
// LIKE '%值',例如 likeLeft("name", "王") 等價於 name like '%王'
likeLeft(R column, Object val)
// LIKE '值%',例如 likeRight("name", "王") 等價於 name like '王%'
likeRight(R column, Object val)

// 字段 IS NULL,例如 isNull("name") 等價於 name is null
isNull(R column)
// 字段 IS NOT NULL,例如 isNotNull("name") 等價於 name is not null
isNotNull(R column)

拼接嵌套

// 字段 IN (v0, v1, ...)
in(R column, Object... values) // in("age", 1, 2, 3) 等價於 age in (1,2,3)
// 字段 IN (value.get(0), value.get(1), ...)
in(R column, Collection<?> value) // 例如 in("age",{1,2,3}) 等價於 age in (1,2,3)

// 字段 NOT IN (v0, v1, ...)
notIn(R column, Object... values) // 例如 notIn("age", 1, 2, 3) 等價於 age not in (1,2,3)
// 字段 NOT IN (value.get(0), value.get(1), ...)
notIn(R column, Collection<?> value) // 例如 notIn("age",{1,2,3}) 等價於 age not in (1,2,3)

// 字段 IN ( sql語句 ),同理還有 字段 NOT IN (SQL 語句)
inSql(R column, String inValue) // // 例如:inSql("id", "select id from table where id < 3") 等價於 id in (id < 3)

// 拼接 OR,注意:主動調用or表示緊接着下一個方法不是用and連接!(不調用or則默認為使用and連接)
or(boolean condition) // 例如:eq("id",1).or().eq("name","老王") 等價於 id = 1 or name = '老王'
// OR 嵌套
or(Consumer<Param> consumer) // 例如:or(i -> i.eq("name", "李白").ne("status", "活着"))  等價於 or (name = '李白' and status <> '活着')
// AND 嵌套
and(Consumer<Param> consumer) // 例如:and(i -> i.eq("name", "李白").ne("status", "活着")) 等價於 and (name = '李白' and status <> '活着')
// 正常嵌套 不帶 AND 或者 OR
nested(Consumer<Param> consumer) // 例如:nested(i -> i.eq("name", "李白").ne("status", "活着")) 等價於 (name = '李白' and status <> '活着')

// 無視優化規則直接拼接到 sql 的最后,只能調用一次,多次調用以最后一次為准,有sql注入的風險,請謹慎使用
last(String lastSql) // 例如:last("limit 1"),這是唯一的可以在SQL中使用limit語句的,並且只能是QueryWrapper構造器。
last(boolean condition, String lastSql)

// 拼接 EXISTS ( sql語句 )
exists(String existsSql) // 例如:exists("select id from table where age = 1") 等價於 exists (select id from table where age = 1)
// 拼接 NOT EXISTS ( sql語句 )
notExists(String notExistsSql) // 例如:notExists("select id from table where age = 1") 等價於 not exists (select id from table where age = 1)

// 拼接 SQL,該方法可用於數據庫函數,動態入參的params對應前面applySql內部的{index}部分,這樣是不會有sql注入風險的,反之會有!
apply(String applySql, Object... params)
// 例如:apply("id = 1") 等價於 id = 1
// 例如:apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")等價於 date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
// 例如:apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08") 等價於 date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

分組排序

// 分組:GROUP BY 字段, ...
groupBy(R... columns) // 例如:groupBy("id", "name") 等價於 group by id,name

// 排序:ORDER BY 字段, ... [規則]
orderByAsc(R... columns) // 例如:orderByAsc("id", "name") 等價於 order by id ASC,name ASC
orderByDesc(R... columns) // 例如:orderByDesc("id", "name") 等價於 order by id DESC,name DESC
orderBy(boolean condition, boolean isAsc, R... columns) // 例如:orderBy(true, true, "id", "name") 等價於 order by id ASC,name ASC

// HAVING ( sql語句 )
having(String sqlHaving, Object... params) // 例如:having("sum(age) > 10") 等價於 having sum(age) > 10

// 函數方法,主要方便在出現if...else下調用不同方法能不斷鏈
func(Consumer<Children> consumer) // 例如: func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})

常用注解

@TableName:表名注解

屬性 類型 描述 默認值 是否必須指定
value String 表名 ""
schema String 模式 ""
resultMap String xml 中 resultMap 的 id ""
autoResultMap Boolean 是否自動構建 resultMap 並使用,與 resultMap 互斥 false
excludeProperty String[] 需要排除的屬性名(@since 3.3.1) {}
keepGlobalPrefix Boolean 是否保持使用全局的 tablePrefix 的值 false

關於autoResultMap的說明:

MP 會自動構建一個 ResultMap 並注入到 MyBatis 里,因為 MP 底層是 MyBatis,只會注入常用的 CRUD 到 MyBatis 里,注入之前可以說是動態的(根據你entity的字段以及注解變化而變化),但是注入之后是靜態的(等於你寫在xml的東西), 而對於直接指定 typeHandler,MyBatis 只支持你寫在2個地方:

  • 定義在 resultMap 里,只作用於 select 查詢的返回結果封裝。

  • 定義在 insert 和 update SQL 中 #{property} 里的 property 后面(例:#{property,typehandler=xxx.xxx.xxx}),只作用於設置值。而除了這兩種直接指定 typeHandler,MyBatis 有一個全局的掃描你自己的 typeHandler 包的配置,這是根據你的 property 的類型去找 typeHandler 並使用。


@TableId:主鍵注解

屬性 類型 描述 默認值 是否必須指定
value String 主鍵字段名 ""
type Enum 主鍵類型 IdType.NON

IdType

描述
AUTO 數據庫自增ID
NONE 無狀態,該類型為未設置主鍵類型(注解里等於跟隨全局,全局里約等於 INPUT)
INPUT insert 前自行 set 主鍵值
ASSIGN_ID 雪花ID,主鍵類型 Number 或 String,使用接口 IdentifierGenerator 的方法 nextId 實現
ASSIGN_UUID UUID,主鍵類型為 String,使用接口 IdentifierGenerator 的默認方法 nextUUID 實現

@TableField:非主鍵字段注解

屬性 類型 描述 默認值 是否必須指定
value String 數據庫字段名 ""
exist boolean 是否為數據庫表字段 true
fill Enum 字段自動填充策略 FieldFill.DEFAULT
select boolean 是否進行 select 查詢 true

FieldFill

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

@TableLogic:表字段邏輯刪除,@Version:樂觀鎖注解

注解示例

@TableName("sys_table")
public class Entity {

    /**
     * 實體主鍵
     */
    @TableId(value = "table_id", type = IdType.AUTO)
    private Long configId;


    /**
     * 創建時間
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    /**
     * 更新時間
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    /**
     * 樂觀鎖
     */
    @Version
    private Integer version;

    /**
     * 邏輯刪除
     */
    @TableLogic
    private Long delFlag;

}


免責聲明!

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



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