目錄html
此Demo 主要應用SpringBoot 來展現Mybatis-Plus 特性, 以及在開發過程當中可能應用到的插件的演示。java
源碼:https://github.com/wunian7yulian/MybatisPlusDemomysql
本文:https://wunian7yulian.github.io/MybatisPlusDemo/git
相同於 MyBatis官方指南 中有了詳細介紹github
不一樣於 實踐演示spring
目的:sql
主要藉此作爲突破口, 一是將自我學習成文記錄下來, 二是將Demo 慢慢作成一個本身或者面向大衆的後端腳手架工具。數據庫
規劃:apache
分享-實踐-填坑-總結-腳手架-分享-實踐......windows
Mybatis-Plus(簡稱MP)是一個Mybatis的加強工具,在 Mybatis 的基礎上只作加強不作改變,爲簡化開發而生!
只作加強不作改變,引入它不會對現有工程產生影響。
只需簡單配置,便可快速進行 CRUD 操做,從而節省大量時間。
熱加載、代碼生成、分頁、性能分析等功能包羅萬象。
MyBatis-Plus 榮獲【2018年度開源中國最受歡迎的中國軟件】 TOP5
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.0.7.1</version> </dependency>
官方簡介說明 MP 和Mybatis 就像是 遊戲中的p1 和p2 同樣 兄弟搭配 幹活不累 、
我在使用中明顯感受到 其實他更像是 馬里奧和蘑菇 吃了蘑菇 咱們跳的高度更高了一些。
內含分佈式惟一 ID 生成器 - Sequence
),可自由配置,完美解決主鍵問題支持 XML 熱加載:Mapper 對應的 XML 支持熱加載,對於簡單的 CRUD 操做,甚至能夠無 XML 啓動
內置 Sql 注入剝離器:支持 Sql 注入剝離,有效預防 Sql 注入攻擊
https://github.com/wunian7yulian/MybatisPlusDemo/tree/master/simpledemo
windows 7
jdk 1.8.0.40
idea Ultimate
maven 3.3.9
建立數據庫mp_demo_db
設置字符集 utf-8
-- 建立簡單表格 DROP TABLE IF EXISTS user; CREATE TABLE `user` ( `id` bigint(20) PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `age` int(11) DEFAULT NULL COMMENT '年齡', `email` varchar(50) DEFAULT NULL COMMENT '郵箱' ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 初始化數據 DELETE FROM user; INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');
爲了方便快捷 選用 SpringBoot 工程做爲Demo支撐
輸入項目包名 並添加mysql模塊 建立完畢。
<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> <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> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.7.1</version> </dependency> <!--手動添加模板引擎--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.20</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.0.6</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-core</artifactId> <version>3.0.7.1</version> </dependency>
在 application.yml
配置文件中添加相關配置:
# DataSource Config spring: datasource: # 這裏若是有錯誤是由於 maven mysql包 選擇了 runtime 形式的 scope 能夠不用管它 繼續下一步就好 driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mp_demo_db?characterEncoding=utf8 username: root password: 123456
@SpringBootApplication @MapperScan("com.lynwood.mp.simpledemo.mapper") public class SimpledemoApplication { public static void main(String[] args) { SpringApplication.run(SimpledemoApplication.class, args); } }
pojo:
import lombok.Data; @Data public class User { private Long id; private String name; private Integer age; private String email; }
mapper:
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.lynwood.mp.simpledemo.model.User; public interface UserMapper extends BaseMapper<User> { }
@RunWith(SpringRunner.class) @SpringBootTest public class SampleTest { @Autowired private UserMapper userMapper; @Test public void testSelect() { List<User> userList = userMapper.selectList(null); Assert.assertEquals(5, userList.size()); userList.forEach(System.out::println); } }
運行結果:
User{id=1, name='Jone', age=18, email='test1@baomidou.com'} User{id=2, name='Jack', age=20, email='test2@baomidou.com'} User{id=3, name='Tom', age=28, email='test3@baomidou.com'} User{id=4, name='Sandy', age=21,email='test4@baomidou.com'} User{id=5, name='Bill', age=24,email='test5@baomidou.com'}
AutoGenerator 是 MyBatis-Plus 的代碼生成器,經過 AutoGenerator 能夠快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提高了開發效率。
https://github.com/wunian7yulian/MybatisPlusDemo/tree/master/simpledemo
同上
建立了autogenerator_demo模塊(記得添加mysql模塊 )做爲演示代碼生成器功能配置。
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.7.1</version> </dependency> <!--手動添加模板引擎--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.20</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.0.6</version> </dependency>
注意:MP 3.0.3
以後移除了自動模板引擎依賴,須要手動添加對應模板引擎
直接複製就行啦!
package com.lynwood.mp.autogenerator_demo; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.ArrayList; import java.util.List; import java.util.Scanner; //注意引入GlobalConfig 使用 import com.baomidou.mybatisplus.generator.config.*; public class GeneratorCode { /** * 讀取控制檯內容 */ 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.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("請輸入正確的" + tip + "!"); } public static void main(String[] args) { // 代碼生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("Lynwood"); gc.setOpen(false); mpg.setGlobalConfig(gc); // 數據源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp_demo_db?useUnicode=true&useSSL=false&characterEncoding=utf8"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("123456"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模塊名")); pc.setParent("com.lynwood.mp.autogenerator_demo"); mpg.setPackageInfo(pc); // 自定義配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 若是模板引擎是 freemarker String templatePath = "/templates/mapper.xml.ftl"; // 若是模板引擎是 velocity // String templatePath = "/templates/mapper.xml.vm"; // 自定義輸出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定義配置會被優先輸出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定義輸出文件名 return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); // 配置自定義輸出模板 // templateConfig.setEntity(); // templateConfig.setService(); // templateConfig.setController(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setSuperEntityClass(null); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); strategy.setSuperControllerClass(null); strategy.setInclude(scanner("表名")); strategy.setSuperEntityColumns(null); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }
在控制檯輸入:
就能夠生成代碼啦!
都包含 :
緣由是:在代碼的全局配置中 String projectPath = System.getProperty("user.dir");
獲取Working Directory時 返回的是項目路徑,並不是模塊路徑!
咱們能夠設定運行參數選項
將 Working Directory 調整爲 當前模塊目錄 再次運行就ok了!
由於沒有引入mvc 模塊 以致於@Controller 會飄紅 再Demo中就沒有將生成代碼傳入 大可拉取代碼本地使用!
相關代碼生成器的配置:官方生成器配置 可配置項過多沒法詳細介紹 有相關使用會說起
https://github.com/wunian7yulian/MybatisPlusDemo/tree/master/mp_crud_demo
Mybatis-Plus 爲咱們提供了豐富的 增刪改查接口
咱們能夠分爲三類 Mapper的CRUD接口、Service的CRUD接口和mapper層選裝件接口:
確實比較豐富 , 下面會以具備表明性的例子來使用 演示 做爲Demo主要內容
建立mp_crud_demo模塊 選擇mysql
由於須要生成表對應Pojo 還須要代碼生成器
而後咱們將上面的直接拷貝一下
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.7.1</version> </dependency> <!--手動添加模板引擎--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.20</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.0.6</version> </dependency>
再添加 lombok 依賴(因:生成代碼中的實體默認是使用@Data 等註解的)
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
pc.setParent("com.lynwood.mp.mp_crud_demo");
設定mybatis掃描位置
在MpCrudDemoApplication
上添加註解:@MapperScan("com.lynwood.mp.mp_crud_demo.*.mapper**")
注意 掃描包的位置!
配置數據源:
# DataSource Config spring: datasource: # 這裏若是有錯誤是由於 maven mysql包 選擇了 runtime 形式的 scope 能夠不用管它 繼續下一步就好 driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mp_demo_db?characterEncoding=utf-8 username: root password: 123456
以userMapper
做爲範例
查看UserMapper
:
打開UserMapper.java
源代碼:
public interface UserMapper extends BaseMapper<User> { }
不難發現它繼承了BaseMapper<User>
接口
打開com.baomidou.mybatisplus.core.mapper.BaseMapper<T>
查看當前接口的結構(Structure):
原來是MP 將以前的mybatis裏面每一個mapper的全部方法 通過泛型進行提煉到了一個BaseMapper 接口中,咱們只須要將本身的mapper 繼承此接口且將泛型指定即可得到強大的CRUD功能!
再去查看UserMapper.xml
文件 :
<?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.lynwood.mp.mp_crud_demo.business.mapper.UserMapper"> </mapper>
徹底丟棄了Mybatis
中字段映射以及一段段複雜的配置!
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.7.1</version> </dependency>
咱們進入mybatis-plus-boot-starter
的pom 發現他幫咱們裝配了對應版本的mybatis-plus
而且依賴了其餘 例如: spring-boot-autoconfigure``spring-boot-starter-jdbc
等;
由於瞭解SpringBoot
中配置入口是一個@Configuration
那麼打開mybatis-plus-boot-starter
的jar
包 看到:MybatisPlusAutoConfiguration.java
:
... public class MybatisPlusAutoConfiguration { ... @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { ... if (this.applicationContext.getBeanNamesForType(ISqlInjector.class, false, false).length > 0) { ISqlInjector iSqlInjector = (ISqlInjector)this.applicationContext.getBean(ISqlInjector.class); globalConfig.setSqlInjector(iSqlInjector); } factory.setGlobalConfig(globalConfig); return factory.getObject(); } ... }
看到它在設置sqlSessionFactory
的時候爲咱們指定了一個com.baomidou.mybatisplus.core.injector.ISqlInjector.class
做爲factory.globalConfig.sqlInjector
(SQL注入器)
而後 咱們打開:ISqlInjector
的實現類AbstractSqlInjector
... public abstract class AbstractSqlInjector implements ISqlInjector { ... public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { ... List<AbstractMethod> methodList = this.getMethodList(); Assert.notEmpty(methodList, "No effective injection method was found.", new Object[0]); methodList.forEach((m) -> { m.inject(builderAssistant, mapperClass); }); ... } }
methodList
是全部的方法的一個集合 其元素類型都是AbstractMethod.class
類型的, 而且對每一個方法都進行了inject(...)
,
那麼查看inject()
方法源碼:
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { ... this.injectMappedStatement(mapperClass, modelClass, tableInfo); } }
原來最終調用了injectMappedStatement()
方法
然而
public abstract MappedStatement injectMappedStatement(Class<?> var1, Class<?> var2, TableInfo var3);
是抽象的 須要本身的子類去實現的
咱們以其中一個 SelectById.class
做爲例子查看
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID; SqlSource sqlSource = new RawSqlSource(this.configuration, String.format(sqlMethod.getSql(), this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty()), Object.class); return this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo); }
再去查看 SqlMethod.SELECT_BY_ID;
:
SELECT_BY_ID("selectById", "根據ID 查詢一條數據", "SELECT %s FROM %s WHERE %s=#{ %s}"),
soga~
結束 最後實際上 MP將SQL語句 封裝了固定的模板 com.baomidou.mybatisplus.core.enums.SqlMethod
從而提供給了咱們便捷的使用!
咱們使用非Wrapper的具備表明性的接口做爲Demo的演示
Wrapper 在後面會有單獨的Demo演示
@RunWith(SpringRunner.class) @SpringBootTest public class MpCrudDemoApplicationTests { @Autowired private UserMapper userMapper; @Test public void simplenessMapperCURD() { //增長 User addUser = new User(); addUser.setAge(18); addUser.setEmail("wunian_@hotmail.com"); addUser.setName("Lynwood"); userMapper.insert(addUser); // insert 以後是將id裝配到實體對象裏的 System.out.println("add:\n" + addUser); // User(id=1082883152404103169, name=Lynwood, age=18, email=wunian_@hotmail.com) // id = 1082883152404103169 之因此這麼長是由於 MP底層給咱們本身以uuid 的形式添加了 user對象的id屬性 //修改 User updateUser = new User(); updateUser.setId(1082883152404103169L); updateUser.setName("ok?"); userMapper.updateById(updateUser); System.out.println("update:\n" + updateUser); // User(id=1082883152404103169, name=ok?, age=null, email=null) // 刷新數據庫 更改爲功 可是沒有講其餘對象進行裝配 //查詢 User selectUser = userMapper.selectById(1082883152404103169L); System.out.println("select:\n" + selectUser); //User(id=1082883152404103169, name=ok?, age=18, email=wunian_@hotmail.com) //刪除 int i = userMapper.deleteById(1082883152404103169L); if (i==1){ System.out.println("delete:\n" + "刪除成功!"); //刷新庫 刪除成功 } } }
MP提供的主鍵策略有:
MP的主鍵策略默認使用的是ID_WORKER
(詳情:https://mybatis.plus/config/#idtype)
在User 中設定 id字段爲@TableId(type = IdType.AUTO)
或者 全局設置 使用主鍵策略,在yaml 添加:
mybatis-plus: global-config: db-config: id-type: auto
重要: 由於剛纔的測試插入了id爲:1082883152404103169
的 數據 咱們須要將自增序列首先恢復正常! 不然下一個id爲1082883152404103170 看上去也是亂的!! 當心坑哈~
delete from user; alter table user auto_increment= 1;
嘗試 增長操做 輸出:
add: User(id=1, name=Lynwood, age=18, email=wunian_@hotmail.com)
填充測試數據:
DELETE FROM user; INSERT INTO user ( name, age, email) VALUES ( 'Lynwood',18,'wunian_@hotmail.com') ( 'Jone', 18, 'test1@baomidou.com'), ( 'Jack', 20, 'test2@baomidou.com'), ( 'Tom', 28, 'test3@baomidou.com'), ( 'Sandy', 21, 'test4@baomidou.com'), ( 'Billie', 24, 'test5@baomidou.com');
測試:
@Test public void batchMapperCURD() { // 多id 查詢 List<Long> idList = new ArrayList<>(); idList.add(1L); idList.add(3L); List<User> userList = userMapper.selectBatchIds(idList);// id的多個查詢 System.out.println("selectBatch:" ); userList.forEach(System.out::println); //User(id=1, name=Lynwood, age=18, email=wunian_@hotmail.com) //User(id=3, name=Jack, age=20, email=test2@baomidou.com) // 多條件 查詢 Map<String,Object> stringObjectMap = new HashMap<>(); stringObjectMap.put("age",18); stringObjectMap.put("id",2); List<User> selectByMap = userMapper.selectByMap(stringObjectMap);// 字段-值 鍵值對集合 做爲 '且' 關係 System.out.println("selectByMap:" ); selectByMap.forEach(System.out::println); //User(id=2, name=Jone, age=18, email=test1@baomidou.com) // 多id 刪除 int deleteCount = userMapper.deleteBatchIds(idList);// id的多個刪除 System.out.println("deleteBatch:\n"+ deleteCount ); // 2 // 多條件 刪除 int deleteCount2 = userMapper.deleteByMap(stringObjectMap);// id的多個查詢 System.out.println("deleteByMap:\n"+ deleteCount2 ); // 1 }
注意使用*ByMap()
方法時 條件是 且關係就ok了!
查看了MP做者說的:
做者在源碼註釋中是這麼寫的....:
/** * <p> 批量新增數據,自選字段 insert </p> * <p> 不一樣的數據庫支持度不同!!! 只在 mysql 下測試過!!! 只在 mysql 下測試過!!! 只在 mysql 下測試過!!! </p> * <p> 除了主鍵是 <strong> 數據庫自增的未測試 </strong> 外理論上均可以使用!!! </p> * <p> 若是你使用自增有報錯或主鍵值沒法回寫到entity,就不要跑來問爲何了,由於我也不知道!!! </p> * <p> * 本身的通用 mapper 以下使用: * int insertBatchSomeColumn(List<T> entityList); * * <li> 注意1: 不要加任何註解 !! </li> * <li> 注意2: 自選字段 insert !!,若是個別字段在 entity 裏爲 null 可是數據庫中有配置默認值, insert 後數據庫字段是爲 null 而不是默認值 </li> * * <p> * 經常使用的構造入參: * </p> */
┓( ´∀` )┏ ~
而後分析其做用 以爲既然有wrapper的強大條件構造器 決定再也不分析 想了解能夠點擊:
不過 案例 說明了MP的一些能夠本身擴展的一個流程 :
第一步: 在UserMapper
添加方法
/** 清空表數據 */ void clearTable();
第二步:自定義實現
在於business
同級下建立mp_injector/methods
目錄並建立類名CLearTable.java
要與添加的方法名 相同!
且要實現com.baomidou.mybatisplus.core.injector.AbstractMethod
完成自定義擴展
public class ClearTable extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { /* 執行 SQL */ String sql = "delete from " + tableInfo.getTableName(); /* mapper 接口方法名一致 */ String method = "clearTable"; SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addDeleteMappedStatement(mapperClass, method, sqlSource); } }
第三步:將自定義實現 添加到MP方法列表
在mp_injector
下建立MySqlInjector.java
來進行對MP的擴展操做:
須要繼承com.baomidou.mybatisplus.core.injector.DefaultSqlInjector
而且重寫 MP獲取方法列表的方法
@Component public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList() { List<AbstractMethod> methodList = super.getMethodList(); //增長了 自定義方法 methodList.add(new ClearTable()); return methodList; } }
第四步:測試
@Test public void myInjectorMapperCURD() { userMapper.clearTable(); }
查庫 所有刪除了
總結:
不難發現其實際上就是 在上文 對MP探個究竟 中探索到的 全部方法的原理
非Wrapper部分基本與Mapper 接口一致 只是將接口提到了service層.
略~
https://github.com/wunian7yulian/MybatisPlusDemo/tree/master/mp_wrapper_demo
注意更改生成器中的 包名模塊名
複製yaml 時注意增長 MP主鍵策略配置:
mybatis-plus: global-config: db-config: id-type: auto
備:關於 java8 lambda 表達式 默認爲熟悉
由於MP 對外已經有了兩個大的版本 2.x 和3.x版本
在2.x版本中 EntityWrapper
做爲Wrapper 的主要繼承實現,
例:
EntityWrapper<User> ew = new EntityWrapper<User>(); ew.setEntity(new User(1)); ew.where("user_name={0}", "'zhangsan'").and("id=1") .orNew("user_status={0}", "0").or("status=1") .notLike("user_nickname", "notvalue") .andNew("new=xx").like("hhh", "ddd") .andNew("pwd=11").isNotNull("n1,n2").isNull("n3") .groupBy("x1").groupBy("x2,x3") .having("x1=11").having("x3=433") .orderBy("dd").orderBy("d1,d2"); System.out.println(ew.getSqlSegment());
實際上 此包裝其實是使用的是 數據庫字段 不是Pojo 裏面的成員變量
在3.x 升級中對Wrapper進行了改動:
全面支持了jdk8的Lambda的使用
Wrapper<T>
實現類的改動
1.EntityWrapper<T>
改名爲QueryWrapper<T>
2.新增一個實現類UpdateWrapper<T>
用於update
方法
BaseMapper<T>
的改動
1.去除了insertAllColumn(T entity)
方法
2.去除了updateAllColumn(T entity)
方法
3.新增update(T entity, Wrapper<T> updateWrapper)
方法
在3.x 中將 全部的操做劃分紅 查詢QueryWrapper
和UpdateWrapper
而且將其共有的方法作了一層抽離放到了AbstractWrapper
中
咱們查看四個實現類:QueryWrapper
、UpdateWrapper
、LambdaQueryWrapper
、LambdaUpdateWrapper
的一個整體抽象: AbstractWrapper
它將共有的提高到這一層並作了實現,
其實主要是對SQL語言全部DML語句中公用的一些關鍵字作了統一的接口
爲了方便查看最後MP封裝轉換的最終SQL,在yml配置文件中添加配置:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Demo 中有 連接:https://github.com/wunian7yulian/MybatisPlusDemo/blob/master/mp_wrapper_demo/src/test/java/com/lynwood/mp/mp_wrapper_demo/MpWrapperDemoApplicationAbstractWrapperTests.java
... @RunWith(SpringRunner.class) @SpringBootTest public class MpWrapperDemoApplicationAbstractWrapperTests { @Autowired private UserMapper userMapper; /** * 測試 allEq() 等同於 WHERE name = ? AND age = ? * + * 使用 selectList() 返回多個指定類型對象 的集合 * */ @Test public void abstractWrapperTest_allEq() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); HashMap<String,Object> param = new HashMap<>(); param.put("name","Lynwood"); param.put("age","18"); userQueryWrapper.allEq(param); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: * ==> Preparing: SELECT id,name,age,email FROM user WHERE name = ? AND age = ? * ==> Parameters: Lynwood(String), 18(String) */ } /** * 測試 eq() 等同於 WHERE name = ? * + * 使用selectOne() * 當返回多條會報錯 */ @Test public void abstractWrapperTest_eq() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.eq("name","Lynwood"); User user = userMapper.selectOne(userQueryWrapper); System.out.println(user); /** * 輸出: * Preparing: SELECT id,name,age,email FROM user WHERE name = ? * Parameters: Lynwood(String) */ } /** not equals 縮寫 ~~ * * 測試 ne() 等同於 WHERE age <> ? * + * 使用 selectObjs() 返回多個 非指定類型對象 的集合 * */ @Test public void abstractWrapperTest_ne() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.ne("age", 18); List<Object> objectList = userMapper.selectObjs(userQueryWrapper); objectList.forEach(System.out::println); /** * 輸出: * ==> Preparing: SELECT id,name,age,email FROM user WHERE age <> ? * ==> Parameters: 18(Integer) */ } /***************************************************** 多接口連用 * 默認AND 關係*******************************/ /** 測試 * gt() : greater than 等同於 > * ge() : greater equals 等同於 >= * lt() : less than 等同於 < * le() : less equals 等同於 <= * * 使用 selectMaps() 返回多個 指定到Map作封裝 的集合 * */ @Test public void abstractWrapperTest_gt_ge_lt_le() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.ge("age", 21); userQueryWrapper.le("age", 24); List<Map<String, Object>> mapList = userMapper.selectMaps(userQueryWrapper); mapList.forEach(System.out::println); /** * 輸出: * ==> Preparing: SELECT id,name,age,email FROM user WHERE age >= ? AND age <= ? * ==> Parameters: 21(Integer), 24(Integer) */ } /** * 測試 * between() 等同於 WHERE age between ? and ? * notBetween() 等同於 WHERE age not between ? and ? * + * 使用 selectList() 返回多個 非指定類型對象 的集合 * */ @Test public void abstractWrapperTest_between() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .between("age", 18,24) .notBetween("id",0,11);// 支持鏈式調用 List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE age BETWEEN ? AND ? AND id NOT BETWEEN ? AND ? ==> Parameters: 18(Integer), 24(Integer), 0(Integer), 11(Integer) */ } /** * 測試 * like() 等同於 LIKE '%?%' * notLike() 等同於 NOT LIKE '%?%' * likeLeft() 等同於 LIKE '%?' * likeRight() 等同於 LIKE '?%' * + * 使用 selectCount() 返回查詢到的條數 */ @Test public void abstractWrapperTest_like() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .like("email", "test") .notLike("email","test4") .likeRight("name","J") // J% .likeLeft("name","e"); //%e Integer selectCount = userMapper.selectCount(userQueryWrapper); System.out.println(selectCount); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE email LIKE ? AND email NOT LIKE ? AND name LIKE ? AND name LIKE ? ==> Parameters: %test%(String), %test4%(String), J%(String), %e(String) */ } /** * 測試 * null() 等同於 is null * isNotNull() 等同於 is not null * */ @Test public void abstractWrapperTest_null() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .isNotNull("name"); Integer selectCount = userMapper.selectCount(userQueryWrapper); System.out.println(selectCount); /** * 輸出: ==> Preparing: SELECT COUNT(1) FROM user WHERE name IS NOT NULL ==> Parameters: <== Columns: COUNT(1) */ } /** * 測試 * in() 等同於 IN (?,?) * notIn() 等同於 NOT IN (?,?) * inSql() 等同於 IN (sql) * notInSql() 等同於 NOT IN (sql) */ @Test public void abstractWrapperTest_in() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); List<Long> idLists = new ArrayList<>(); idLists.add(11L); idLists.add(13L); userQueryWrapper // .in("id",idLists); .inSql("id","select id from user where id<=15"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE id IN (select id from user where id<=15) ==> Parameters: */ } /** * 測試 * groupBy() .. 支持多參數 等同於 GROUP BY age,name,id */ @Test public void abstractWrapperTest_group() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .groupBy("age","name","id"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user GROUP BY age,name,id ==> Parameters: */ } /** * 測試 * orderBy() .. 支持多參數 等同於 關於 condition * orderByDesc() .. 支持多參數 等同於 ORDER BY age DESC , id DESC * orderByAsc() .. 支持多參數 等同於 ORDER BY age ASC , id ASC */ @Test public void abstractWrapperTest_order() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .orderByAsc("age","id"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user ORDER BY age ASC , id ASC ==> Parameters: */ } /** * 測試 * having() .. 支持多參數 等同於 HAVING sum(id)> ? */ @Test public void abstractWrapperTest_having() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .groupBy("age") .having("sum(id)>{0}",20);// 替換裏面的參數 List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user GROUP BY age HAVING sum(id)>? ==> Parameters: 20(Integer) */ } /** * 測試 * or() 等同於 or * and() 等同於 and */ @Test public void abstractWrapperTest_or_and() { // 簡單 or 和and QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .eq("id",11) .or() // 默認and() // .and() // 兩個不能連用 .eq("id",13); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE id = ? OR id = ? ==> Parameters: 11(Integer), 13(Integer) */ // 嵌套的 or 和and QueryWrapper<User> userQueryWrapper1 = new QueryWrapper<>(); userQueryWrapper1 .eq("id",14) .or(i -> i.eq("name", "Lynwood").ne("age", "18")); List<User> userList1 = userMapper.selectList(userQueryWrapper1); userList1.forEach(System.out::println); /**輸出: * * ==> Preparing: SELECT id,name,age,email FROM user WHERE id = ? OR ( name = ? AND age <> ? ) * ==> Parameters: 14(Integer), Lynwood(String), 18(String) */ /**!!!!!!!!!!!! .or(i -> i.eq("name", "Lynwood").ne("age", "18")); 解釋 見下文發現問題*/ } /** * 測試 * apply() 動態傳參防止 sql注入 佔位符 替換 以值的形式添加 */ @Test public void abstractWrapperTest_apply() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .apply("id>{0}",13); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE id>? ==> Parameters: 13(Integer) */ } /** * 測試 * last() 在sql 最後追加 * * 最經常使用 last("limit 1") */ @Test public void abstractWrapperTest_last() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .last("limit 2"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user limit 2 ==> Parameters: */ } /** * 測試 * exists() 拼接 EXISTS ( sql語句 ) * notExists() 拼接 NOT EXISTS ( sql語句 ) */ @Test public void abstractWrapperTest_exists() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .exists("SELECT id,name,age,email FROM user where id= 222"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE EXISTS (SELECT id,name,age,email FROM user where id= 222) ==> Parameters: <== Total: 0 */ } /** * 測試 * nested() 正常嵌套 (無 or and 模式嵌套) */ @Test public void abstractWrapperTest_nested() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .nested(i -> i.eq("name", "Lynwood").eq("age", "18")); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); /** * 輸出: ==> Preparing: SELECT id,name,age,email FROM user WHERE ( name = ? AND age = ? ) ==> Parameters: Lynwood(String), 18(String) */ } }
boolean condition
、R column
和 Function<This, This> func
三個參數相關問題 Demo 中有 測試連接:https://github.com/wunian7yulian/MybatisPlusDemo/blob/master/mp_wrapper_demo/src/test/java/com/lynwood/mp/mp_wrapper_demo/MpWrapperDemoApplicationAbstractWrapperParamTests.java
boolean condition
- 如下出現的第一個入參
boolean condition
表示該條件是否加入最後生成的sql中,默認是true
使用ne()
方法舉例 源碼 :
@Override public This ne(boolean condition, R column, Object val) { return addCondition(condition, column, NE, val); }
測試編寫:
/** * 測試 boolean condition */ @Test public void abstractWrapperTest_Condition() { Integer age = new Random().nextInt(25); System.out.println("年齡: " + age); QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); boolean condition = age<=18; userQueryWrapper.ne(condition,"name","Lynwood"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); }
生成的隨機數age 小於18歲的 不要讓他查到name="Lynwood"
的信息
再此看來等同於*mapper.xml
配置
WHERE 1=1 <if test="age <= 18"> AND name <> #{name} </if>
幫咱們提入到了每一個方法中,而且省去咱們關係 是否寫AND
關鍵字或者 必須有 WHERE 1=1
這些難看卻又沒必要要的配置
R column
- 如下方法在入參中出現的
R
爲泛型,在普通wrapper中是String
,在LambdaWrapper中是函數(例:Entity::getId
,Entity
爲實體類,getId
爲字段id
的getMethod)- 如下方法入參中的
R column
均表示數據庫字段,當R
爲String
時則爲數據庫字段名(字段名是數據庫關鍵字的本身用轉義符包裹!)!而不是實體類數據字段名!!!
查看源碼 發現實際上 eq()、ne()...
等須要傳入R column
的方法實際最後都是調用了addCondition(...)
方法
對 R column
參數作了 columnToString(column)
操做
而 AbstractWrapper
實際中的此方法是抽象的:
/** * 獲取 columnName */ protected abstract String columnToString(R column);
而咱們再去查看咱們使用的 QueryWrapper
與UpdateWrapper
中的具體實現都爲:
@Override protected String columnToString(String column) { return column; }
其實是轉換成了String
類型, 那麼爲何使用了泛型呢?
咱們再返回去查看他的另外一個實現類AbstractLambdaWrapper
中的實現:
@Override protected String columnToString(SFunction<T, ?> column) { return getColumn(LambdaUtils.resolve(column)); }
原來這裏使用泛型 爲了擴展Lambda方式使用!
lambda
- 獲取
LambdaWrapper
在QueryWrapper
中是獲取LambdaQueryWrapper
在UpdateWrapper
中是獲取LambdaUpdateWrapper
測試:
/** * 測試 R column */ @Test public void abstractWrapperTest_Column() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); // userQueryWrapper.ne("name","Lynwood"); userQueryWrapper.lambda().eq(User::getName,"Lynwood"); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); }
Function<This, This> func
其實再測試AbstractWrapper中的 or/and 嵌套中已經使用過了
以or()
舉例
須要傳入的是 Function
的實現 而Function
的定義是:
/** * Represents a function that accepts one argument and produces a result. * 表示接受一個參數執行生成結果的函數對象。 * @since 1.8 */ @FunctionalInterface public interface Function<T, R> {
在咱們使用過程當中 每每是爲了嵌套 咱們傳入的多是另外一個Wrapper 讓他和當前的Wrapper去連接/嵌套起來
那麼在:
userQueryWrapper.ne("name","Lynwood") .or(i -> i.eq("age", "18"));
中 咱們實際上傳入了一個匿名的Wrapper對象,那麼這個匿名的Wrapper對象怎麼符合了Function的要求呢
咱們查看這個i
變量 :
他的類型是與調用對象一致的 查看 QueryWrapper<User>
的繼承關係:
實際上咱們使用的全部接口都去間接繼承了ISqlSegment
接口 ,而它:
package com.baomidou.mybatisplus.core.conditions; @FunctionalInterface public interface ISqlSegment extends Serializable {
是被@FunctionalInterface 標識的 與:
package java.util.function; @FunctionalInterface public interface Function<T, R> {
是同樣的, 因此咱們使用它能夠這樣簡便的使用啦:
測試:
@Test public void abstractWrapperTest_Func() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.ne("name","Lynwood"); userQueryWrapper.or(new Function<QueryWrapper<User>, QueryWrapper<User>>() { @Override public QueryWrapper<User> apply(QueryWrapper<User> userQueryWrapper) { return userQueryWrapper.eq("age", "18"); } }); // 等同與 // userQueryWrapper.ne("name","Lynwood") // .or(i -> i.eq("age", "18")); List<User> userList = userMapper.selectList(userQueryWrapper); userList.forEach(System.out::println); }
Demo 中有 連接:https://github.com/wunian7yulian/MybatisPlusDemo/blob/master/mp_wrapper_demo/src/test/java/com/lynwood/mp/mp_wrapper_demo/MpWrapperDemoApplicationQueryAndUpdateWrapperTests.java
其實在上面的全部測試中其返回值都是徹底的裝配到了實體對象中,可是有的時候 好比有個字段很大 很消耗內存,同時也用不到,那麼我如何把他取消掉.設置返回值爲咱們須要的幾個字段就好
select()
測試
lambda()
使用 lambda特性時!/** * 測試 QueryWrapper.select() * */ @Test public void abstractWrapperTest_Select() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); // userQueryWrapper.eq("age",18) // .select("id","name"); userQueryWrapper.eq("age",18) .select(User.class,i -> false);//只返回id List<User> userList = userMapper.selectList(userQueryWrapper); // SELECT id FROM user WHERE age = ? userList.forEach(System.out::println); //前方有坑 注意: 注意!!!!!!!!!!! //關於LambdaQueryWrapper的使用這樣用是沒問題的 LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda(); lambdaQueryWrapper.select(User::getId,User::getAge); List<User> userList1 = userMapper.selectList(lambdaQueryWrapper); //SELECT id,age FROM user userList1.forEach(System.out::println); // 這樣設置是無效的!!!! QueryWrapper<User> userQueryWrapper2 = new QueryWrapper<>(); userQueryWrapper2.lambda().select(User::getId,User::getAge); List<User> userList2 = userMapper.selectList(userQueryWrapper2); // SELECT id,name,age,email FROM user userList2.forEach(System.out::println); }
set() setSql()
測試
/** * 測試 UpdateWrapper.set() * 測試 UpdateWrapper.setSql() */ @Test public void abstractWrapperTest_Set() { UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>(); // 更新id==11 的age 爲19 userUpdateWrapper.set("age",19).eq("id",11); // 附加更新內容 name 設置 User user = new User(); user.setName("Lynwood1"); userMapper.update(user,userUpdateWrapper); /** * ==> Preparing: UPDATE user SET name=?, age=? WHERE id = ? * ==> Parameters: Lynwood1(String), 19(Integer), 11(Integer) * <== Updates: 1 */ UpdateWrapper<User> userUpdateWrapper1 = new UpdateWrapper<>(); userUpdateWrapper1.setSql("name='wunian7yulian'").eq("id",11); // 附加更新內容爲空時 不能傳入null 須要傳入空實體對象 userMapper.update(new User(),userUpdateWrapper1); /** * ==> Preparing: UPDATE user SET name='wunian7yulian' WHERE id = ? * ==> Parameters: 11(Integer) * <== Updates: 1 */ }
https://github.com/wunian7yulian/MybatisPlusDemo/tree/master/mp_plugin_demo
初始化:
由於下面演示須要其餘字段,因此從新建立一個便於Demo的合適的表格:
-- 建立簡單表格 DROP TABLE IF EXISTS user; CREATE TABLE `user` ( `id` bigint(20) PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `age` int(11) DEFAULT NULL COMMENT '年齡', `email` varchar(50) DEFAULT NULL COMMENT '郵箱' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO user ( name, age, email) VALUES ('Jone', 18, 'test1@test.com'), ('Jack', 20, 'test2@test.com'), ('Tom', 28, 'test3@test.com'), ('Sandy', 21, 'test4@test.com'), ('Billie', 24, 'test5@test.com'), ('Lynwood', 18, 'wunian_@hotmail.com');
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mp_demo_db?characterEncoding=utf-8 username: root password: 123456 mybatis-plus: global-config: db-config: id-type: auto configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
以前作列表或者報表功能時使用最多的是pageHelper
插件作Mybatis的分頁查詢,MP爲了方便將分頁作了一個相關的模塊com.baomidou.mybatisplus.extension.plugins.pagination
供咱們分頁查詢時去使用.
在business目錄下建立config目錄 並在該目錄下建立MybatisPlusConfig.java
添加@Configuration
註解聲明
@Configuration public class MybatisPlusConfig { }
@Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
@RunWith(SpringRunner.class) @SpringBootTest public class MpPluginDemoApplicationTests { @Autowired private UserMapper userMapper; /** * 測試分頁插件 */ @Test public void test_page() { IPage<User> userIPage = userMapper.selectPage( new Page<User>() .setCurrent(1) //設置當前查詢頁 .setSize(3) // 設置每頁條數 .setDesc("age"),//使用page 進行排序 new QueryWrapper<User>() .lambda() .likeRight(User::getEmail, "test") .select(User::getId, User::getName, User::getAge) ); List<User> records = userIPage.getRecords(); records.forEach(System.out::println); /** * ==> Preparing: SELECT id,name,age FROM user WHERE email LIKE ? ORDER BY age DESC LIMIT ?,? * ==> Parameters: test%(String), 0(Long), 3(Long) */ } }
完美! 不過由於page
對象在3.0.6
版本尚未支持lambda方式 page上在設置排序字段時 仍是會有一些魔法值出現,不過咱們也是能夠直接將 排序 寫在wrapper裏的. 有強迫症的童鞋等待3.1.0
版本吧.
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mp_demo_db?characterEncoding=utf-8 username: root password: 123456 mybatis-plus: global-config: db-config: id-type: auto logic-delete-value: 1 # 邏輯已刪除值(默認爲 1) logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0) configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
在MybatisPlusConfig中添加插件bean
@Configuration public class MybatisPlusConfig { ... @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); } }
在User
中的 del字段上添加@TableLogic
註解
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class User implements Serializable { ... /** * 邏輯刪除 0-未刪除 1-刪除 */ @TableLogic private Integer del; ... }
/** * 測試邏輯刪除插件 */ @Test public void test_logic_delete() { userMapper.delete(new QueryWrapper<User>() .lambda() .notLike(User::getEmail, "test") .select(User::getId, User::getName, User::getAge)); /** * ==> Preparing: UPDATE user SET del=1 WHERE del=0 AND email NOT LIKE ? * ==> Parameters: %test%(String) */ //那麼查詢呢? List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); //發現查詢也幫咱們設置了過濾字段 del = 0 /** * ==> Preparing: SELECT id,name,age,email,del,version FROM user WHERE del=0 * ==> Parameters: */ //那麼我想查出這個刪除的用戶 更改成 正常呢? List<Map<String, Object>> mapList = userMapper.selectMaps( new QueryWrapper<User>() .lambda() .eq(User::getDel, 1) .select(User.class,i -> false) ); Assert.assertNotNull(mapList); Assert.assertEquals(mapList.size(),1); /** select id from user where del = 0 and del = 1 * java.lang.AssertionError: * Expected :0 * Actual :1 */ // 失敗了 // }
詢問MP開發者後 開發者說明 由於大部分狀況在刪除以後不會恢復 因此沒有設定相關恢復或者跳過 當前 篩選的 接口
當要更新一條記錄的時候,但願這條記錄沒有被別人更新
特別說明:
newVersion = oldVersion + 1
newVersion
會回寫到 entity
中updateById(id)
與 update(entity, wrapper)
方法樂觀鎖實現方式:
取出記錄時,獲取當前version
更新時,帶上這個version
執行更新時, set version = newVersion where version = oldVersion
若是version不對,就更新失敗
一、配置樂觀鎖插件:
在MybatisPlusConfig
添加:
@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
二、添加樂觀鎖版本字段註解:
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class User implements Serializable { ... /** * 樂觀鎖版本字段 */ @Version private Integer version; }
三、編寫測試類:
/** * 測試樂觀鎖插件 */ @Test public void test_Optimistic_Locking() throws InterruptedException { User oldUser = userMapper.selectById(1); oldUser.setAge(19); System.out.println("模擬中途有人更改"+ "begin...."); Thread.sleep(10000); System.out.println("模擬中途有人更改"+ "end...."); if(userMapper.updateById(oldUser)==1){ System.out.println("success \n"+ oldUser); //success //User(id=1, name=Jone, age=19, email=test1@test.com, del=0, version=2) }else { System.out.println("fail \n"); //==> Preparing: UPDATE user SET name=?, age=?, email=?, version=? WHERE id=? AND version=? AND del=0 //==> Parameters: Jone(String), 19(Integer), test1@test.com(String), 3(Integer), 1(Long), 2(Integer) //<== Updates: 0 //fail } }
其餘插件 如 熱加載等 若有須要 再作更新
MP優秀於簡化了Mybatis大部分XML配置 將他歸總到起來生成一個強大的Wrapper
現有MP也是有了兩個版本大的版本2.x與3.x ,酌情使用比較合理 3.x雖然有了Lambda的支持可是還不完善,
後續版本的更新應該也會去查漏現版本的缺陷.
對於插件,像樂觀鎖分頁邏輯刪真的是貼心,不過也有相對應場景不適用問題,好比上面說到的邏輯刪除的我還要查是查不到的,仍是須要去自定義sql的
我相信 有了mp這種強大的利器,對於一個微型項目 或者微服務 來講真的是爽,寫起來真的是很是有效率
我的--
做爲頭一個被我收拾的應用層框架,看到了開發者的用心和初心, 看來排上名並非那麼絕非偶然,也並不是那麼"功力深厚",只要你有想法開始作,總有一個好的結果.
下一框架尋找中...
MyBatis-Plus 官方文檔 : https://mp.baomidou.com/
MyBatis-Plus 配置進階 : https://mp.baomidou.com/config/
MyBatis-Plus 代碼生成器配置 : https://mp.baomidou.com/config/generator-config.html
MyBatis-Plus sql注入原理 : https://www.liangzl.com/get-article-detail-19831.html
Mybatis-Plus 使用全解 : https://www.jianshu.com/p/7299cba2adec