MyBatis-Plus是一個 Mybatis 的增強工具,在 Mybatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生;
- MyBatis-Plus是怎么增強的呢?
已經封裝好了一些crud方法,我們不需要再寫xml了,直接調用這些方法就行,類似JPA但優於JPA;
簡介:[https://mp.baomidou.com/guide/]
- 導入依賴
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis plus和springboot整合--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency>
- MyBatis-Plus配置控制台打印日志
#配置mybatis plus打印sql日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
- BaseMapper使用
Mapper 繼承該接口后,無需編寫 mapper.xml 文件,即可獲得CRUD功能;
API使用參考源碼:com.baomidou.mybatisplus.core.mapper.BaseMapper
- QueryWrapper使用
查詢包裝類,可以封裝多數查詢條件,泛型指定返回的實體類;
可以封裝sql對象,包括where條件,order by排序,select哪些字段等等;
核心API:
eq:等於 ne:不等於 gt:大於 ge:大於等於 lt:小於 le:小於等於 or:拼接or between:兩個值中間 notBetween:不在兩個值中間 like:模糊匹配 notLike:不像 likeLeft:左匹配,eg: like '%a' likeRight:右邊匹配,eg:like 'a%' isNull:字段為空 in:in范圍查詢 groupBy:分組 orderByAsc:升序 orderByDesc:降序 having:having查詢
API使用參考源碼:com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
- MyBatis-Plus 常用注解
- @TableName 用於定義表名
-
@TableId 用於定義表的主鍵
- 屬性
value 用於定義主鍵字段名 type 用於定義主鍵類型(主鍵策略 IdType)
-
-
- 主鍵策略
-
IdType.AUTO 主鍵自增,系統分配,不需要手動輸入 IdType.NONE 未設置主鍵 IdType.INPUT 需要自己輸入 主鍵值 IdType.ASSIGN_ID 系統分配 ID,用於數值型數據(Long,對應 mysql 中 BIGINT 類型) IdType.ASSIGN_UUID 系統分配 UUID,用於字符串型數據(String,對應 mysql 中 varchar(32) 類型)
-
- @TableField 用於定義表的非主鍵字段
-
-
- 屬性
-
value:用於定義非主鍵字段名,用於別名匹配,假如Java對象屬性和數據庫屬性不一樣 exist:用於指明是否為數據表的字段, true 表示是,false 為不是,假如某個java屬性在數據庫沒對應的字段則要標記為false fill:用於指定字段填充策略(FieldFill,用的不多) 字段填充策略:一般用於填充 創建時間、修改時間等字段 FieldFill.DEFAULT 默認不填充 FieldFill.INSERT 插入時填充 FieldFill.UPDATE 更新時填充 FieldFill.INSERT_UPDATE 插入、更新時填充
- 分頁配置
- 舊版本寫法(MyBatis-Plus 3.4.0之前)
@Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
參考源碼:com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor
-
- 新版本寫法(MyBatis-Plus 3.4.0之后)
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
-
- 測試分頁:
建表sql
CREATE TABLE `banner` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `img` varchar(524) DEFAULT NULL COMMENT '圖片', `url` varchar(524) DEFAULT NULL COMMENT '跳轉地址', `weight` int(11) DEFAULT NULL COMMENT '權重', `version` int(11) DEFAULT '1', `deleted` int(11) DEFAULT '0' COMMENT '0是未刪除,1是已經刪除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
實體類
@Data @TableName("banner") public class BannerDO { @TableId(value = "id",type = IdType.AUTO) private Integer id; private String img; private String url; @TableField("weight") private Integer weight; @TableField(exist = false) private Date createTime; /** * 樂觀鎖版本號 */ @Version private Integer version; /** * 邏輯刪除標識位 */ private Integer deleted; }
分頁測試:
@Test public void testPage1() { // 第1頁,每頁2條 Page<BannerDO> page = new Page<>(1, 2); IPage<BannerDO> bannerDOIPage = bannerMapper.selectPage(page, null); log.info("總條數" + bannerDOIPage.getTotal()); log.info("總頁數" + bannerDOIPage.getPages()); log.info("當前頁數:" + bannerDOIPage.getCurrent()); // 獲取當前數據 log.info(bannerDOIPage.getRecords().toString()); }
執行結果如下:
啟用分頁插件,MyBatis-Plus會先進行select count(*)對當前表進行統計之后再分頁;
- XML自定義分頁的寫法
自定義Mapper
public interface BannerMapper extends BaseMapper<BannerDO> { IPage<BannerDO> customizeSelectPage(Page<?> page, @Param("weight") int weight); }
自定義XML
<select id="customizeSelectPage" resultType="BannerDO"> select id, img, url, weight, version, deleted from banner where weight=#{weight} </select>
測試分頁:
@Test public void testPage2() { // 第1頁,每頁2條 Page<BannerDO> page = new Page<>(1, 2); IPage<BannerDO> bannerDOIPage = bannerMapper.customizeSelectPage(page, 2); log.info("總條數" + bannerDOIPage.getTotal()); log.info("總頁數" + bannerDOIPage.getPages()); log.info("當前頁數:" + bannerDOIPage.getCurrent()); // 獲取當前數據 log.info(bannerDOIPage.getRecords().toString()); }
執行結果如下:
參考:[https://mp.baomidou.com/guide/page.html]
- MyBatis-Plus 自定義xml
配置mapper路徑,如果采用默認路徑可以不配
#默認配置路徑 mybatis-plus.mapper-locations=classpath*:/mapper/*Mapper.xml
- MyBatis-Plus 全局配置文件
#配置最新全局配置文件 mybatis-plus.config-location = classpath*:mybatis-config.xml
-
- 注意:config-location和configuration不能同時出現,需要注釋配置文件里的相關配置
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!--控制台輸出日志--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> </configuration>
- 配置文件配置 自定義xml映射的包掃描路徑
eg:
#配置文件配置 自定義sql的包掃描 實體類的路徑 mybatis-plus.type-aliases-package=org.example.manager.model
- MyBatis-Plus下划線轉駝峰配置,默認就是true
mybatis-plus.configuration.map-underscore-to-camel-case=true
- MyBatis-Plus配置全局默認主鍵類型,實體類就不用加 @TableId(value = "id", type = IdType.AUTO)
mybatis-plus.global-config.db-config.id-type=auto
- MyBatis-Plus指定查詢字段
eg:
List<BannerDO> bannerDOList = bannerMapper.selectList(new QueryWrapper<BannerDO>().select("id", "url"));
題外話,關於SELECT * 與 SELECT指定字段的區別:
-
- 網絡I/O
SELECT * 會查出所有的字段,有些是不需要的,當應用程序和服務器不在同一個局域網時,字段過多會影響網絡傳輸的性能
-
- 索引
在指定字段有索引的情況下,MySql是可以不用讀磁盤的,直接使用索引里面的值就返回結果的;
但用了SELECT *,這就會有其他列需要從磁盤中讀取才會返回結果,這樣就造成了額外的性能開銷;
- MyBatis-Plus樂觀鎖使用
樂觀鎖就是每次去拿數據的時候都認為別人不會修改,更新的時候會通過版本來判斷別人是否修改了數據,如果數據被修改了就拒絕更新;
java中的AtomicXXX原子類是通過CAS實現,CAS即比較並更新,屬於樂觀鎖,性能較悲觀鎖有很大的提高;
悲觀鎖適合寫操作多的場景,樂觀鎖適合讀操作多的場景,樂觀鎖的吞吐量會比悲觀鎖多;
數據庫的樂觀鎖大多是基於數據版本 (Version)記錄機制實現;數據版本即為數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來 實現。 讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一;此時,將提交數據的版本數據與數據,庫表對應記錄的當前版本信息進行比對,如果提交的數據 版本號大於數據庫表當前版本號,則予以更新,否則認為是過期數據;
使用:
實體類增加version屬性配置
@Version private Integer version;
增加樂觀鎖插件
//樂觀鎖插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
測試代碼
@Test public void testOptimisticLocker1() { BannerDO bannerDO = new BannerDO(); //舊版本號,即查詢出來的版本號需要做對比的 bannerDO.setVersion(1); bannerDO.setId(8); bannerDO.setUrl("www.baidu.com"); bannerMapper.updateById(bannerDO); } public void testOptimisticLocker2() { BannerDO bannerDO = new BannerDO(); //舊版本號,即查詢出來的版本號需要做對比的 bannerDO.setVersion(1); bannerDO.setId(8); bannerDO.setUrl("www.baidu.com"); bannerMapper.updateById(bannerDO); }
testOptimisticLocker1執行前,數據庫表中的數據;
執行testOptimisticLocker1
執行testOptimisticLocker2,更新失敗
注意:
-
- 樂觀鎖數據類型支持int,integer,long,timestamp;
- 僅支持updateById和update方法
- MyBatis-Plus 邏輯刪除配置使用
邏輯刪除只不過是更新了標記,不會真正的物理刪除;
使用方式:
-
- 數據庫增加邏輯刪除的標識字段,如deleted,0是未刪除,1表示刪除;
- 實體類增加屬性配置@TableLogic 或者 在配置文件增加指定;
@TableLogic private Integer deleted;
-
- 配置文件新增配置
#刪除是1 mybatis-plus.global-config.db-config.logic-delete-value=1 #未刪除是0 mybatis-plus.global-config.db-config.logic-not-delete-value=0 #邏輯刪除的全局標識字段,如果java實體類沒加注解@TableLogic,則可以配置這個 mybatis-plus.global-config.db-config.logic-delete-field=deleted
測試代碼:
public void testDeleteById() { int rtn = bannerMapper.deleteById(8); log.info("rtn:{}", rtn); }
執行結果:
打印的sql為update,而不是delete;
- Mybatis-Plus-Generator 代碼自動生成工具使用
AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率;
底層是模板引擎技術,可以自定義生成的java類模板;
導入依賴
<!-- 代碼自動生成依賴 begin --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!-- velocity --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency> <!-- 代碼自動生成依賴 end-->
使用如下,配置按需修改:
public class MyBatisPlusGenerator { public static void main(String[] args) { //1. 全局配置 GlobalConfig config = new GlobalConfig(); // 是否支持AR模式 config.setActiveRecord(true) // 作者 .setAuthor("") // 生成路徑,最好使用絕對路徑 .setOutputDir("") // 文件覆蓋 .setFileOverride(true) // 主鍵策略 .setIdType(IdType.AUTO) // 數據庫時間類型 .setDateType(DateType.ONLY_DATE) // 設置生成的service接口的名字的首字母是否為I,默認Service是以I開頭的 .setServiceName("%sService") // 實體類結尾名稱 .setEntityName("%sDO") // 生成基本的resultMap .setBaseResultMap(true) // 不使用AR模式 .setActiveRecord(false) // 生成基本的SQL片段 .setBaseColumnList(true); //2. 數據源配置 DataSourceConfig dsConfig = new DataSourceConfig(); // 設置數據庫類型 dsConfig.setDbType(DbType.MYSQL) .setDriverName("com.mysql.cj.jdbc.Driver") .setUrl("") .setUsername("") .setPassword(""); //3. 策略配置globalConfiguration中 StrategyConfig stConfig = new StrategyConfig(); //全局大寫命名 stConfig.setCapitalMode(true) // 數據庫表映射到實體的命名策略 .setNaming(NamingStrategy.underline_to_camel) // 使用lombok .setEntityLombokModel(true) // 使用restcontroller注解 .setRestControllerStyle(true) // 生成的表, 支持多表一起生成,以數組形式填寫 // 兩個方式,直接寫,或者使用命令行輸入 //.setInclude(""); .setInclude(scanner("表名,多個英文逗號分割").split(",")); //4. 包名策略配置 PackageConfig pkConfig = new PackageConfig(); pkConfig.setParent("org.example.manager") .setMapper("mapper") .setService("service") .setController("controller") .setEntity("model") .setXml("mapper"); //5. 整合配置 AutoGenerator ag = new AutoGenerator(); ag.setGlobalConfig(config) .setDataSource(dsConfig) .setStrategy(stConfig) .setPackageInfo(pkConfig); //6. 執行操作 ag.execute(); System.out.println("======= 代碼生成完畢 ========"); } /** * <p> * 讀取控制台內容 * </p> */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("請輸入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException("請輸入正確的" + tip + "!"); } }