這一篇從一個入門的基本體驗介紹,再到對於 CRUD 的一個詳細介紹,在介紹過程當中將涉及到的一些問題,例如逐漸策略,自動填充,樂觀鎖等內容說了一下,只選了一些重要的內容,還有一些沒說起到,具體能夠參考官網,簡單的看完,其實會發現,若是遇到單表的 CRUD ,直接用 MP 確定舒服,若是寫多表,仍是用 Mybatis 多點,畢竟直接寫 SQL 會直觀一點,MP 給個人感受,就是方法封裝了不少,還有一些算比較是用的插件,可是可讀性會稍微差一點,不過我的有我的的見解哇,祝你們國慶快樂 ~html
最初的 JDBC,咱們須要寫大量的代碼來完成與基本的 CRUD ,或許會在必定程度上使用 Spring 的 JdbcTemplate 或者 Apache 的 DBUtils ,這樣一些對 JDBC 的簡單封裝的工具類。前端
再到後再使用 Mybatis 等一些優秀的持久層框架,大大的簡化了開發,咱們只須要使用必定的 XML 或者註解就能夠完成原來的工做java
JDBC --> Mybatis 無疑簡化了開發者的工做,而今天咱們所講額 MyBatis-Plus 就是在 MyBatis 的基礎上,更加的簡化開發,來一塊兒看看吧!mysql
下列介紹來自官網:web
MyBatis-Plus(簡稱 MP)是一個 MyBatis 的加強工具,在 MyBatis 的基礎上只作加強不作改變,爲簡化開發、提升效率而生。算法
咱們的願景是成爲 MyBatis 最好的搭檔,就像魂鬥羅中的 1P、2P,基友搭配,效率翻倍。spring
總之一句話:MyBatis-Plus —— 爲簡化開發而生!sql
按照官網的案例簡單試一下 ,注:官網是基於 Springboot 的示例數據庫
@Repository public interface UserMapper extends BaseMapper<User> { }
@Repository public interface UserMapper extends BaseMapper<User> { }
自行建立一個數據庫便可,而後導入官網給出的案例表,而後插入以下數據apache
-- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名', `age` int(11) NULL DEFAULT NULL COMMENT '年齡', `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '郵箱', PRIMARY KEY (`id`) USING BTREE ); -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@baomidou.com'); INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com'); INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com'); INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com'); INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@baomidou.com');
若是沒有接觸過 SpringBoot,使用常規的 SSM 也是能夠的,爲了演示方便,這裏仍是使用了SpringBoot,若是想在 SSM 中使用,一個注意依賴的修改,還一個就須要修改 xml 中的一些配置
引入 MyBatis-Plus-boot-starter 確定是沒什麼疑問的,一樣咱們還須要引入,數據庫鏈接的驅動依賴,還能夠看須要引入 lombok,這裏爲了簡便因此使用了它,若是不想使用,手動生成構造方法和 get set 便可
<!-- 數據庫驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- MyBatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
如何進行數據庫相關的信息在之前的SpringBoot文章已經說過了,這裏強調一下:
mysql 5 驅動:com.mysql.jdbc.Driver
mysql 8 驅動:com.mysql.cj.jdbc.Driver
、還須要增長時區的配置
serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root99 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
根據數據庫字段建立出對應實體屬性就好了,仍是提一下:上方三個註解,主要是使用了 lombok 自動的生成那些 get set 等方法,不想用的同窗直接本身按原來的方法顯式的寫出來就能夠了~
@Data @AllArgsConstructor @NoArgsConstructor public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; }
代碼以下,能夠看到,咱們額外的繼承了 BaseMapper,同時指定了泛型爲 User
@Repository public interface UserMapper extends BaseMapper<User> { }
其實點進去 BaseMapper 看一下,你會發現,在其中已經定義了關於 CRUD 一些基本方法還有一些涉及到配合條件實現更復雜的操做,同時泛型中指定的實體,會在增刪改查的方法中被調用
照這樣說的話,好像啥東西都被寫好了,若是如今想要進行一個簡單的增刪改查,是否是直接使用就好了
首先在測試類中注入 UserMapper,這裏演示一個查詢全部的方法,因此使用了 selectList
,其參數是一個條件,這裏先置爲空。
若是有哪些方法的使用不明確,咱們能夠先點到 BaseMapper 中去看一下,down 下源碼之後,會有一些註釋說明
/** * 根據 entity 條件,查詢所有記錄 * * @param queryWrapper 實體對象封裝操做類(能夠爲 null) */ List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
下面是測試查詢全部的全代碼
@SpringBootTest class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { List<User> userList = userMapper.selectList(null); for (User user : userList) { System.out.println(user); } } }
控制檯輸出以下
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=Billie, age=24, email=test5@baomidou.com)
通過一個簡單的測試,感受仍是很香的,而之前在 Mybatis 中咱們執行sql語句時,是能夠看到控制檯打印的日誌的,而這裏顯然沒有,其實經過一行簡單的配置就能夠了
其實只須要在配置文件中加入短短的一行就能夠了
MyBatis-Plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
打印以下:
固然你還能夠經過一些 MyBatis Log 的插件,來快速的查看本身所執行的 sql
首先,先試試插入一個實體的操做,咱們選擇使用了 insert 這個方法,下面是其定義:
/** * 插入一條記錄 * * @param entity 實體對象 */ int insert(T entity);
@Test public void testInsert() { // 擬一個對象 User user = new User(); user.setName("理想二旬不止"); user.setAge(30); user.setEmail("ideal_bwh@163.com"); // 插入操做 int count = userMapper.insert(user); System.out.println(count); System.out.println(user); }
結果:
根據結果看到,插入確實成功了,可是一個發矇的問題出現了,爲啥 id 變成了一個 long 類型的值
對於主鍵的生成,官網有以下的一句話:
自3.3.0開始,默認使用雪花算法+UUID(不含中劃線)
也就是說,由於上面咱們沒有作任何的處理,因此它使用了默認的算法來當作主鍵 id
snowflake是Twitter開源的分佈式ID生成算法,結果是一個long型的ID。其核心思想是:使用41bit做爲毫秒數,10bit做爲機器的ID(5個bit是數據中心,5個bit的機器ID),12bit做爲毫秒內的流水號(意味着每一個節點在每毫秒能夠產生 4096 個 ID),最後還有一個符號位,永遠是0。
雪花算法 + UUID 因此基本是能夠保證惟一的
固然除了雪花算法爲,咱們還有一些別的主鍵生成的策略,例如 Redis、數據庫自增
對於咱們以前經常使用的一種主鍵生成方式,通常都會用到數據庫id自增
@TableId(type = IdType.AUTO)
再次插入,發現 id 已經實現了自增
@TableId 註解中的屬性 Type 的值來自於 IdType 這個枚舉類,其中我把每一項簡單解釋一下
AUTO(0)
:數據庫 ID 自增(MySQL 正常,Oracle 未測試)
MyBatis-Plus.global-config.db-config.id-type=auto
NONE(1)
:該類型爲未設置主鍵類型(註解裏等於跟隨全局,全局裏約等於 INPUT)
INPUT(2)
:用戶輸入 ID,也能夠自定義輸入策略,內置策略以下
使用時:
先添加 @Bean,而後實體類配置主鍵 Sequence,指定主鍵策略爲 IdType.INPUT 便可,重點不說這個,有須要能夠直接扒官網
@Bean public IKeyGenerator keyGenerator() { return new H2KeyGenerator(); }
ASSIGN_ID(3)
:雪花算法
ASSIGN_UUID(4)
:不含中劃線的UUID
3.3.0 後,ID_WORKER(3)、ID_WORKER_STR(3)、UUID(4) 就已經被棄用了,前兩個可使用 ASSIGN_ID(3)代替,最後一個使用 ASSIGN_UUID(4)代替
// 根據 whereEntity 條件,更新記錄 int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); // 根據 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);
MyBatis-Plus 中的更新操做也是很是方便
舉一種比較常見的一種狀況,經過 id 值修改某些字段
傳統作法會傳一個修改後的對象,而後經過 #{}
設置具體更新的值和 id
<update id="updateById"> UPDATE user SET name=#{name}, portrait=#{portrait}, gender=#{gender}, telephone=#{telephone}, email=#{email} WHERE id=#{id} </update>
MyBatis-Plus 方式:
@Test public void testUpdate() { // 擬一個對象 User user = new User(); user.setId(1L); user.setName("理想二旬不止"); user.setAge(20); int i = userMapper.updateById(user); System.out.println(i); }
首先咱們給定了 id 值,同時又修改了姓名和年齡這兩個字段,可是並非所有字段,來看一下執行效果
神奇的發現,咱們不須要在 sql 中進行設置了,全部的配置都被自動作好了,更新的內容和 id 都被自動填充好了
自動填充是填充什麼內容呢?首先咱們須要知道,通常來講表中的建立時間,修改時間,咱們老是但願可以給根據插入或者修改的時間自動填充,而不須要咱們手動的去更新
可能之前的項目不是特別綜合或須要等緣由,有時候也不會去設置建立時間等字段,寫這部分是由於,在阿里巴巴的Java開發手冊(第5章 MySQL 數據庫 - 5.1 建表規約 - 第 9 條 )有明確指出:
【強制】表必備三字段:id, create_time, update_time。
說明:其中 id 必爲主鍵,類型爲 bigint unsigned、單表時自增、步長爲 1。create_time, update_time
的類型均爲 datetime 類型,前者如今時表示主動式建立,後者過去分詞表示被動式更新
咱們能夠經過直接修改數據庫中對應字段的默認值,來實現數據庫級別的自動添加語句
例如上圖中我首先添加了 create_time, update_time 兩個字段,而後將類型選擇爲 datetime
,又設置其默認值爲 CURRENT_TIMESTAMP
注:更新時間字段中要勾選 On Update Current_Timestamp ,插入不用,使用 SQLYog 沒問題,在 Navicat 某個版本下直接經過可視化操做可能會報錯,沒有此默認值,這種狀況就把表先導出來,而後修改SQL,在SQL 中修改語句
create_time` datetime(0) DEFAULT CURRENT_TIMESTAMP(0) COMMENT '建立時間', update_time` datetime(0) DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改時間',
根據官網的自動填充功能的說明,其實咱們須要作的只有兩點:
注:開始前,別忘了刪除剛纔數據庫級別測試時的字段默認值等喔
首先填充字段註解:
@TableField(fill = FieldFill.INSERT)
@TableField(fill = FieldFill.INSERT_UPDATE)
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
FieldFill 說明:
public enum FieldFill { /** * 默認不處理 */ DEFAULT, /** * 插入時填充字段 */ INSERT, /** * 更新時填充字段 */ UPDATE, /** * 插入和更新時填充字段 */ INSERT_UPDATE }
接着建立自定義實現類 MyMetaObjectHandler,讓其實現MetaObjectHandler,重寫其 insertFill 和 updateFill 方法,打印日誌就不說了,經過 setFieldValByName 就能夠對字段進行賦值,源碼中這個方法有三個參數
/** * 通用填充 * * @param fieldName java bean property name * @param fieldVal java bean property value * @param metaObject meta object parameter */ default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {...}
@Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); setFieldValByName("createTime", new Date(), metaObject); setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); setFieldValByName("updateTime", new Date(), metaObject); } }
查看一下效果:
下面還有一些注意事項:
注意事項:
- 填充原理是直接給
entity
的屬性設置值!!!- 註解則是指定該屬性在對應狀況下必有值,若是無值則入庫會是
null
MetaObjectHandler
提供的默認方法的策略均爲:若是屬性有值則不覆蓋,若是填充值爲null
則不填充- 字段必須聲明
TableField
註解,屬性fill
選擇對應策略,該聲明告知MyBatis-Plus
須要預留注入SQL
字段- 填充處理器
MyMetaObjectHandler
在 Spring Boot 中須要聲明@Component
或@Bean
注入- 要想根據註解
FieldFill.xxx
和字段名
以及字段類型
來區分必須使用父類的strictInsertFill
或者strictUpdateFill
方法- 不須要根據任何來區分可使用父類的
fillStrategy
方法
演示樂觀鎖插件前,首先補充一些基礎概念:
打個比方,一張電影票價格爲 30,老闆告訴員工 A ,把價格上調到 50,員工 A 由於有事耽擱了兩個小時,可是老闆想了一會以爲提價過高了,就想着訂價 40 好了,正好碰到員工 B,就讓員工 B 將價格下降 10 塊
當正好兩個員工都在操做後臺系統時,兩人同時取出當前價格,即 30 元,員工A 先操做後 價格變成了 50元,可是員工 B 又將30 - 10 ,即 變成20塊,執行了更新操做,此時員工 B 的更新操做就會把前面的 50 元覆蓋掉,即最終成爲了 20元,雖然我心裏毫無波瀾,但老闆卻虧的一匹
意圖:
當要更新一條記錄的時候,但願這條記錄沒有被別人更新
樂觀鎖實現方式:
實現這個功能,只須要兩步:
@Version // 樂觀鎖的Version註解 private Integer version;
// 掃描 mapper 文件夾 @MapperScan("cn.ideal.mapper") @EnableTransactionManagement // 表明配置類 @Configuration public class MyBatisPlusConfig { // 樂觀鎖插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
說明:剛開始例如掃描 mapper 這樣的註解就放在了啓動類中,如今有了配置類,因此把它也移過來了
測試一下:
首先1號和2號獲取到的數據是同樣的,可是在1號尚未執行到更新的時候,2號搶先提交了更新操做,也就是說,當前真實數據已是被2號修改過的了,與1號前面獲取到的不一致了
若是沒有樂觀鎖,那麼2號提交的更新會被1號的更新數據覆蓋
// 測試更新 @Test public void testUpdate() { // 1號取得了數據 User user1 = userMapper.selectById(1L); user1.setName("樂觀鎖1號"); user1.setAge(20); user1.setEmail("ideal_bwh@xxx.com"); // 2號取得了數據 User user2 = userMapper.selectById(1L); user2.setName("樂觀鎖2號"); user2.setAge(30); user2.setEmail("ideal@xxx.com"); // 2號提交更新 userMapper.updateById(user2); // 1號提交更新 userMapper.updateById(user1); }
能夠看到,在2號搶先執行後,1號就沒有成功執行了
一樣數據庫中表的其 version 也從1變成了1
// 根據 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);
@Test public void testSelectById(){ User user = userMapper.selectById(1L); System.out.println(user); }
說明:我這裏使用的仍是最基本的寫法,例如 List 能夠用工具類建立 如:Arrays.asList(1, 2, 3)
遍歷也徹底能夠這樣 users.forEach(System.out::println);
@Test public void testSelectByBatchId(){ List list = new ArrayList(); list.add(1); list.add(2); list.add(3); List<User> users = userMapper.selectBatchIds(list); for (User user : users){ System.out.println(user); } }
@Test public void testSelectByMap(){ HashMap<String, Object> map = new HashMap<>(); // 自定義要查詢的字段和值 map.put("name","理想二旬不止"); map.put("age",30); List<User> users = userMapper.selectByMap(map); for (User user : users){ System.out.println(user); } }
經過日誌的打印能夠看到,它根據咱們的選擇自動拼出了 SQL 的條件
==> Preparing: SELECT id,name,age,email,version,create_time,update_time FROM user WHERE name = ? AND age = ? ==> Parameters: 理想二旬不止(String), 30(Integer)
JavaWeb 階段,你們都應該有手寫過度頁,配合 SQL 的 limit 進行分頁,後面在 Mybatis 就會用一些例如 pageHelper 的插件,而 MyBatis-Plus 中也有一個內置的分頁插件
使用前只須要進行一個小小的配置,在剛纔配置類中,加入分頁插件的配置代碼
// 掃描咱們的mapper 文件夾 @MapperScan("cn.ideal.mapper") @EnableTransactionManagement @Configuration // 配置類 public class MyBatisPlusConfig { // 樂觀鎖插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } // 分頁插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
接着就能夠測試分頁了
@Test public void testPage(){ // Page 參數: 參數1:當前頁 ,參數1:頁面大小 Page<User> page = new Page<>(2,3); userMapper.selectPage(page,null); List<User> users = page.getRecords(); for (User user : users){ System.out.println(user); } System.out.println(page.getTotal()); }
執行結果日誌:
==> Preparing: SELECT id,name,age,email,version,create_time,update_time FROM user LIMIT ?,? ==> Parameters: 3(Long), 3(Long) <== Columns: id, name, age, email, version, create_time, update_time <== Row: 4, Sandy, 21, test4@baomidou.com, 1, null, null <== Row: 5, Billie, 24, test5@baomidou.com, 1, null, null <== Row: 1308952901602811906, 理想二旬不止, 30, ideal_bwh@163.com, 1, null, null <== Total: 3
如何實現一些條件相對複雜的查詢呢?MyBatis-Plus 也給咱們提供了一些用法,幫助咱們方便的構造各類條件
其實前面你們應該就注意到了,在查詢操做的可用方法中,參數中每每帶有一個名叫 Wrapper<T> queryWrapper
的內容,這就是咱們要構造條件的重點
查詢中最經常使用的就是 QueryWrapper
說明:
繼承自 AbstractWrapper ,自身的內部屬性 entity 也用於生成 where 條件
及 LambdaQueryWrapper, 能夠經過 new QueryWrapper().lambda() 方法獲取
實例化一個 QueryWrapper 後,經過調用一些內置的方法,就能夠實現條件構造
例如咱們想要構造這樣一個條件:查詢郵箱不爲空,且年齡小於 25 歲的用戶
@Test void contextLoads() { // 實例化一個 QueryWrapper 對象 QueryWrapper<User> wrapper = new QueryWrapper<>(); // 進行具體條件構造 wrapper .isNotNull("email") .lt("age", 25); // 執行具體的查詢方法,同時將 wrapper 條件做爲參數傳入 List<User> users = userMapper.selectList(wrapper); for (User user : users){ System.out.println(user); } }
看一下打印的日誌:
==> Preparing: SELECT id,name,age,email,version,deleted,create_time,update_time FROM user WHERE deleted=0 AND (email IS NOT NULL AND age < ?) ==> Parameters: 25(Integer) <== Columns: id, name, age, email, version, deleted, create_time, update_time <== Row: 2, 理想, 22, ideal_bwh@xxx.com, 1, 0, 2020-09-26 15:06:09, 2020-09-26 21:21:52 <== Row: 4, Sandy, 21, test4@baomidou.com, 1, 0, null, null <== Row: 5, Billie, 24, test5@baomidou.com, 1, 0, null, null <== Total: 3
能夠看到條件都被自動在 SQL 中構造出來了
使用的方式就這麼簡單,經過各類巧妙的構造就行了
下面是從官網摘取的各類構造方式:
allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
個別參數說明:
params
: key
爲數據庫字段名,value
爲字段值
null2IsNull
: 爲true
則在map
的value
爲null
時調用 isNull 方法,爲false
時則忽略value
爲null
的
allEq({id:1,name:"老王",age:null})
--->id = 1 and name = '老王' and age is null
allEq({id:1,name:"老王",age:null}, false)
--->id = 1 and name = '老王'
allEq(BiPredicate<R, V> filter, Map<R, V> params) allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
個別參數說明:
filter
: 過濾函數,是否容許字段傳入比對條件中
params
與 null2IsNull
: 同上
allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})
--->name = '老王' and age is null
allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)
--->name = '老王'
eq(R column, Object val) eq(boolean condition, R column, Object val)
eq("name", "老王")
--->name = '老王'
ne(R column, Object val) ne(boolean condition, R column, Object val)
ne("name", "老王")
--->name <> '老王'
gt(R column, Object val) gt(boolean condition, R column, Object val)
gt("age", 18)
--->age > 18
ge(R column, Object val) ge(boolean condition, R column, Object val)
ge("age", 18)
--->age >= 18
lt(R column, Object val) lt(boolean condition, R column, Object val)
lt("age", 18)
--->age < 18
le(R column, Object val) le(boolean condition, R column, Object val)
le("age", 18)
--->age <= 18
between(R column, Object val1, Object val2) between(boolean condition, R column, Object val1, Object val2)
between("age", 18, 30)
--->age between 18 and 30
notBetween(R column, Object val1, Object val2) notBetween(boolean condition, R column, Object val1, Object val2)
notBetween("age", 18, 30)
--->age not between 18 and 30
like(R column, Object val) like(boolean condition, R column, Object val)
like("name", "王")
--->name like '%王%'
notLike(R column, Object val) notLike(boolean condition, R column, Object val)
notLike("name", "王")
--->name not like '%王%'
likeLeft(R column, Object val) likeLeft(boolean condition, R column, Object val)
likeLeft("name", "王")
--->name like '%王'
likeRight(R column, Object val) likeRight(boolean condition, R column, Object val)
likeRight("name", "王")
--->name like '王%'
isNull(R column) isNull(boolean condition, R column)
isNull("name")
--->name is null
isNotNull(R column) isNotNull(boolean condition, R column)
isNotNull("name")
--->name is not null
in(R column, Collection<?> value) in(boolean condition, R column, Collection<?> value)
in("age",{1,2,3})
--->age in (1,2,3)
in(R column, Object... values) in(boolean condition, R column, Object... values)
in("age", 1, 2, 3)
--->age in (1,2,3)
notIn(R column, Collection<?> value) notIn(boolean condition, R column, Collection<?> value)
notIn("age",{1,2,3})
--->age not in (1,2,3)
notIn(R column, Object... values) notIn(boolean condition, R column, Object... values)
notIn("age", 1, 2, 3)
--->age not in (1,2,3)
inSql(R column, String inValue) inSql(boolean condition, R column, String inValue)
inSql("age", "1,2,3,4,5,6")
--->age in (1,2,3,4,5,6)
inSql("id", "select id from table where id < 3")
--->id in (select id from table where id < 3)
notInSql(R column, String inValue) notInSql(boolean condition, R column, String inValue)
notInSql("age", "1,2,3,4,5,6")
--->age not in (1,2,3,4,5,6)
notInSql("id", "select id from table where id < 3")
--->id not in (select id from table where id < 3)
groupBy(R... columns) groupBy(boolean condition, R... columns)
groupBy("id", "name")
--->group by id,name
orderByAsc(R... columns) orderByAsc(boolean condition, R... columns)
orderByAsc("id", "name")
--->order by id ASC,name ASC
orderByDesc(R... columns) orderByDesc(boolean condition, 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(String sqlHaving, Object... params) having(boolean condition, String sqlHaving, Object... params)
having("sum(age) > 10")
--->having sum(age) > 10
having("sum(age) > {0}", 11)
--->having sum(age) > 11
func
func(Consumer<Children> consumer) func(boolean condition, Consumer<Children> consumer)
func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
or() or(boolean condition)
注意事項:
主動調用or
表示緊接着下一個方法不是用and
鏈接!(不調用or
則默認爲使用and
鏈接)
eq("id",1).or().eq("name","老王")
--->id = 1 or name = '老王'
or(Consumer<Param> consumer) or(boolean condition, Consumer<Param> consumer)
or(i -> i.eq("name", "李白").ne("status", "活着"))
--->or (name = '李白' and status <> '活着')
and(Consumer<Param> consumer) and(boolean condition, Consumer<Param> consumer)
and(i -> i.eq("name", "李白").ne("status", "活着"))
--->and (name = '李白' and status <> '活着')
nested(Consumer<Param> consumer) nested(boolean condition, Consumer<Param> consumer)
nested(i -> i.eq("name", "李白").ne("status", "活着"))
--->(name = '李白' and status <> '活着')
apply(String applySql, Object... params) apply(boolean condition, String applySql, Object... params)
注意事項:
該方法可用於數據庫函數 動態入參的params
對應前面applySql
內部的{index}
部分.這樣是不會有sql注入風險的,反之會有!
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'")
last(String lastSql) last(boolean condition, String lastSql)
注意事項:
只能調用一次,屢次調用以最後一次爲準 有sql注入的風險,請謹慎使用
last("limit 1")
exists(String existsSql) exists(boolean condition, String existsSql)
exists("select id from table where age = 1")
--->exists (select id from table where age = 1)
notExists(String notExistsSql) notExists(boolean condition, String notExistsSql)
notExists("select id from table where age = 1")
--->not exists (select id from table where age = 1)
// 根據 entity 條件,刪除記錄 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 刪除(根據ID 批量刪除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根據 ID 刪除 int deleteById(Serializable id); // 根據 columnMap 條件,刪除記錄 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
@Test public void testDeleteById(){ userMapper.deleteById(1L); }
@Test public void testDeleteBatchIds(){ List list = new ArrayList(); list.add(1308952901602811906L); list.add(1308952901602811907L); list.add(1308952901602811908L); list.add(1308952901602811909L); userMapper.deleteBatchIds(list); }
@Test public void testDeleteMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","理想二旬不止"); userMapper.deleteByMap(map); }
刪除這塊再補充一下邏輯刪除的概念,物理刪除很好理解,就是實實在在的在數據庫中刪沒了,可是邏輯刪除,顧名思義只是邏輯上被刪除了,實際上並無,只是經過增長一個字段讓其失效而已,例如 deleted = 0 => deleted = 1
能夠
應用的場景就是管理員想查看刪除記錄,在錯誤刪除下,能夠有逆轉的機會等等
首先數據庫增長 deleted 字段,同時建立其實體和註解
@TableLogic // 邏輯刪除 private Integer deleted;
接着只須要在全局配置中配置便可
application.properties
# 配置邏輯刪除 MyBatis-Plus.global-config.db-config.logic-delete-value=1 MyBatis-Plus.global-config.db-config.logic-not-delete-value=0
application.yml
MyBatis-Plus: global-config: db-config: logic-delete-field: flag # 全局邏輯刪除的實體字段名(since 3.3.0,配置後能夠忽略不配置步驟2) logic-delete-value: 1 # 邏輯已刪除值(默認爲 1) logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
效果以下:
你會發現,邏輯刪除會走一個更新操做,經過修改指定字段 deleted 的值爲 0 實現咱們想要的效果
MyBatis-Plus 提供了一個很是便捷,有意思的內容,那就是代碼的自動生成,咱們經過一些配置,就能夠自動的生成 controller、service、mapper、pojo 的內容,而且接口或者註解等內容都會按照配置指定的格式生成。(提早準備好數據庫和表)
首先除了 MyBatis-Plus 的依賴之外,還須要引入 swagger 和 velocity 的依賴,可是這二者實際上是可選的,能夠選擇不配置就不用引入了,默認使用 velocity 這個模板引擎,你們還能夠換成別的
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
例如:
Velocity(默認):
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>latest-velocity-version</version> </dependency>
Freemarker:
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>latest-freemarker-version</version> </dependency>
Beetl:
<dependency> <groupId>com.ibeetl</groupId> <artifactId>beetl</artifactId> <version>latest-beetl-version</version> </dependency>
注意!若是您選擇了非默認引擎,須要在 AutoGenerator 中 設置模板引擎。
AutoGenerator generator = new AutoGenerator(); // set freemarker engine generator.setTemplateEngine(new FreemarkerTemplateEngine()); // set beetl engine generator.setTemplateEngine(new BeetlTemplateEngine()); // set custom engine (reference class is your custom engine class) generator.setTemplateEngine(new CustomTemplateEngine()); // other config ...
下面就是一個主配置了,修改其中的數據庫鏈接等信息,以及包的名稱等等等執行就能夠了
package cn.ideal; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.po.TableFill; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import java.util.ArrayList; /** * @ClassName: AutomaticCodeGenerate * @Description: TODO * @Author: BWH_Steven * @Date: 2020/10/2 21:29 * @Version: 1.0 */ public class AutomaticCodeGenerate { public static void main(String[] args) { // 須要構建一個代碼自動生成器對象 AutoGenerator mpg = new AutoGenerator(); // 配置策略 // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setAuthor("BWH_Steven"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setOpen(false); gc.setFileOverride(false); // 是否覆蓋 gc.setServiceName("%sService"); // 去Service的I前綴 gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); mpg.setGlobalConfig(gc); // 設置數據源 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding" + "=utf-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root99"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 包的配置 PackageConfig pc = new PackageConfig(); pc.setModuleName("test"); pc.setParent("cn.ideal"); pc.setEntity("entity"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("user"); // 設置要映射的表名 strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); // 自動lombok; strategy.setLogicDeleteFieldName("deleted"); // 自動填充配置 TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT); TableFill gmtModified = new TableFill("update_time", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmtCreate); tableFills.add(gmtModified); strategy.setTableFillList(tableFills); // 樂觀鎖 strategy.setVersionFieldName("version"); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true); mpg.setStrategy(strategy); mpg.execute(); //執行 } }
生成結構效果以下:
我簡單貼兩段生成的內容:
controller
package cn.ideal.test.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <p> * 前端控制器 * </p> * * @author BWH_Steven * @since 2020-10-02 */ @RestController @RequestMapping("/test/user") public class UserController { }
entity
package cn.ideal.test.entity; import com.baomidou.mybatisplus.annotation.IdType; import java.util.Date; import com.baomidou.mybatisplus.annotation.Version; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableField; import java.io.Serializable; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * <p> * * </p> * * @author BWH_Steven * @since 2020-10-02 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="User對象", description="") public class User implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "主鍵ID") @TableId(value = "id", type = IdType.AUTO) private Long id; @ApiModelProperty(value = "姓名") private String name; @ApiModelProperty(value = "年齡") private Integer age; @ApiModelProperty(value = "郵箱") private String email; @ApiModelProperty(value = "版本") @Version private Integer version; @TableLogic private Integer deleted; @ApiModelProperty(value = "建立時間") @TableField(fill = FieldFill.INSERT) private Date createTime; @ApiModelProperty(value = "修改時間") @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; }
service
package cn.ideal.test.service; import cn.ideal.test.entity.User; import com.baomidou.mybatisplus.extension.service.IService; /** * <p> * 服務類 * </p> * * @author BWH_Steven * @since 2020-10-02 */ public interface UserService extends IService<User> { }
service 實現類
package cn.ideal.test.service.impl; import cn.ideal.test.entity.User; import cn.ideal.test.mapper.UserMapper; import cn.ideal.test.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** * <p> * 服務實現類 * </p> * * @author BWH_Steven * @since 2020-10-02 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
mapper
package cn.ideal.test.mapper; import cn.ideal.test.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * <p> * Mapper 接口 * </p> * * @author BWH_Steven * @since 2020-10-02 */ public interface UserMapper extends BaseMapper<User> { }
mapper 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="cn.ideal.test.mapper.UserMapper"> </mapper>
若是文章中有什麼不足,歡迎你們留言交流,感謝朋友們的支持!
若是能幫到你的話,那就來關注我吧!若是您更喜歡微信文章的閱讀方式,能夠關注個人公衆號
在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤
一個堅持推送原創開發技術文章的公衆號:理想二旬不止