本文是經過慕課網相關課程學習MyBatisPlus整理的筆記。
MyBatisPlus入門 : - ) 老師講的挺好的,還不會MyBatisPlus的小夥伴門能夠聽一下。
MyBatisPlus官網
MyBatisPlus源碼地址
#建立用戶表 CREATE TABLE user ( id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主鍵', name VARCHAR(30) DEFAULT NULL COMMENT '姓名', age INT(11) DEFAULT NULL COMMENT '年齡', email VARCHAR(50) DEFAULT NULL COMMENT '郵箱', manager_id BIGINT(20) DEFAULT NULL COMMENT '直屬上級id', create_time DATETIME DEFAULT NULL COMMENT '建立時間', CONSTRAINT manager_fk FOREIGN KEY (manager_id) REFERENCES user (id) ) ENGINE=INNODB CHARSET=UTF8; #初始化數據: INSERT INTO user (id, name, age, email, manager_id , create_time) VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL , '2019-01-11 14:20:20'), (1088248166370832385, '王天風', 25, 'wtf@baomidou.com', 1087982257332887553 , '2019-02-05 11:12:22'), (1088250446457389058, '李藝偉', 28, 'lyw@baomidou.com', 1088248166370832385 , '2019-02-14 08:31:16'), (1094590409767661570, '張雨琪', 31, 'zjq@baomidou.com', 1088248166370832385 , '2019-01-14 09:15:15'), (1094592041087729666, '劉紅雨', 32, 'lhm@baomidou.com', 1088248166370832385 , '2019-01-14 09:48:16');
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.2</version> </dependency>
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true logging: level: root: warn org.ywb.demo.dao: trace pattern: console: '%p%m%n'
@Data public class User { private Long id; private String name; private Integer age; private String email; private String managerId; private LocalDateTime createTime; }
public interface UserMapper extends BaseMapper<User> { }
@MapperScan
掃描dao層接口@MapperScan("org.ywb.demo.dao") @SpringBootApplication public class MybatisPlusDemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusDemoApplication.class, args); } }
8.編寫測試類java
@RunWith(SpringRunner.class) @SpringBootTest public class MybatisPlusDemoApplicationTests { @Resource private UserMapper userMapper; @Test public void select(){ List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
運行結果:
mysql
MyBatisPlus提供了一些註解供咱們在實體類和表信息出現不對應的時候使用。經過使用註解完成邏輯上匹配。git
註解名稱 | 說明 |
---|---|
@TableName |
實體類的類名和數據庫表名不一致 |
@TableId |
實體類的主鍵名稱和表中主鍵名稱不一致 |
@TableField |
實體類中的成員名稱和表中字段名稱不一致 |
@Data @TableName("t_user") public class User { @TableId("user_id") private Long id; @TableField("real_name") private String name; private Integer age; private String email; private Long managerId; private LocalDateTime createTime; }
transient
關鍵字修飾非表字段,可是被transient
修飾後,沒法進行序列化。static
關鍵字,由於咱們使用的是lombok框架生成的get/set方法,因此對於靜態變量,咱們須要手動生成get/set方法。@TableField(exist = false)
註解BaseMapper中封裝了不少關於增刪該查的方法,後期自動生成,咱們直接調用接口中的相關方法便可完成相應的操做。BaseMapper
部分代碼github
public interface BaseMapper<T> extends Mapper<T> { int insert(T entity); int deleteById(Serializable id); int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); int updateById(@Param(Constants.ENTITY) T entity); ... }
插入一條記錄測試:web
@Test public void insert(){ User user = new User(); user.setAge(31); user.setManagerId(1088250446457389058L); user.setCreateTime(LocalDateTime.now()); int insert = userMapper.insert(user); System.out.println("影像記錄數:"+insert); }
除了BaseMapper
中提供簡單的增刪改查方法以外,還提供了不少關於區間查詢,多表鏈接查詢,分組等等查詢功能,實現的類圖以下所示:
經過觀察類圖可知,咱們須要這些功能時,只須要建立QueryWrapper
對象便可。spring
/** * 查詢名字中包含'雨'而且年齡小於40 * where name like '%雨%' and age < 40 */ @Test public void selectByWrapper(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name","雨").lt("age",40); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * 建立日期爲2019年2月14日而且直屬上級姓名爲王姓 * date_format(create_time,'%Y-%m-%d') and manager_id in (select id from user where name like '王%') */ @Test public void selectByWrapper2(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.apply("date_format(create_time,'%Y-%m-%d')={0}","2019-02-14") .inSql("manager_id","select id from user where name like '王%'"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
注意
上面的日期查詢使用的是佔位符的形式進行查詢,目的就是爲了防止SQL注入的風險。
apply方法的源碼sql
/** * 拼接 sql * <p>!! 會有 sql 注入風險 !!</p> * <p>例1: apply("id = 1")</p> * <p>例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")</p> * <p>例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())</p> * * @param condition 執行條件 * @return children */ Children apply(boolean condition, String applySql, Object... value);
SQL 注入的例子:數據庫
queryWrapper.apply("date_format(create_time,'%Y-%m-%d')=2019-02-14 or true=true") .inSql("manager_id","select id from user where name like '王%'");
/** * 名字爲王姓,(年齡小於40或者郵箱不爲空) */ @Test public void selectByWrapper3(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.likeRight("name","王").and(wq-> wq.lt("age",40).or().isNotNull("email")); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * 名字爲王姓,(年齡小於40,而且年齡大於20,而且郵箱不爲空) */ @Test public void selectWrapper4(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.likeRight("name", "王").and(wq -> wq.between("age", 20, 40).and(wqq -> wqq.isNotNull("email"))); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * (年齡小於40或者郵箱不爲空)而且名字爲王姓 * (age<40 or email is not null)and name like '王%' */ @Test public void selectWrapper5(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email")).likeRight("name","王"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * 年齡爲30,31,35,34的員工 */ @Test public void selectWrapper6(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.in("age", Arrays.asList(30,31,34,35)); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * 無視優化規則直接拼接到 sql 的最後(有sql注入的風險,請謹慎使用) * <p>例: last("limit 1")</p> * <p>注意只能調用一次,屢次調用以最後一次爲準</p> * * @param condition 執行條件 * @param lastSql sql語句 * @return children */ Children last(boolean condition, String lastSql);
/** * 只返回知足條件的一條語句便可 * limit 1 */ @Test public void selectWrapper7(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.in("age", Arrays.asList(30,31,34,35)).last("limit 1"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * 查找爲王姓的員工的姓名和年齡 */ @Test public void selectWrapper8(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select("name","age").likeRight("name","王"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
/** * 查詢全部員工信息除了建立時間和員工ID列 */ @Test public void selectWrapper9(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select(User.class,info->!info.getColumn().equals("create_time") &&!info.getColumn().equals("manager_id")); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
在咱們調用的查詢語句中,經過查看源碼(這裏以apply
方法爲例)能夠看出,每一個查詢方法的第一個參數都是boolean類型的參數,重載方法中默認給咱們傳入的都是true。編程
default Children apply(String applySql, Object... value) { return apply(true, applySql, value); } Children apply(boolean condition, String applySql, Object... value);
這個condition的做用是爲true時,執行其中的SQL條件,爲false時,忽略設置的SQL條件。segmentfault
在web開發中,controller層經常會傳遞給咱們一個用戶的對象,好比經過用戶姓名和用戶年齡查詢用戶列表。
咱們能夠將傳遞過來的對象直接以構造參數的形式傳遞給QueryWrapper
,MyBatisPlus會自動根據實體對象中的屬性自動構建相應查詢的SQL語句。
@Test public void selectWrapper10(){ User user = new User(); user.setName("劉紅雨"); user.setAge(32); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
若是想經過對象中某些屬性進行模糊查詢,咱們能夠在跟數據庫表對應的實體類中相應的屬性標註註解便可。
好比咱們想經過姓名進行模糊查詢用戶列表。
@TableField(condition = SqlCondition.LIKE) private String name;
@Test public void selectWrapper10(){ User user = new User(); user.setName("紅"); user.setAge(32); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
MybatisPlus提供了4種方式建立lambda條件構造器,前三種分別是這樣的
LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda(); LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();
@Test public void lambdaSelect(){ LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery(); lambdaQueryWrapper.like(User::getName,"雨").lt(User::getAge,40); List<User> userList = userMapper.selectList(lambdaQueryWrapper); userList.forEach(System.out::println); }
QueryWrapper類已經提供了很強大的功能,而lambda條件構造器作的和QueryWrapper的事也是相同的爲何要冗餘的存在lambda條件構造器呢?
QueryWrapper是經過本身寫表中相應的屬性進行構造where條件的,容易發生拼寫錯誤,在編譯時不會報錯,只有運行時纔會報錯,而lambda條件構造器是經過調用實體類中的方法,若是方法名稱寫錯,直接進行報錯,因此lambda的糾錯功能比QueryWrapper要提早不少。
舉個例子:
查找姓名中包含「雨」字的員工信息。
使用QueryWrapperqueryWrapper.like("name","雨");使用lambda
lambdaQueryWrapper.like(User::getName,"雨");若是在拼寫name的時候不當心,寫成了naem,程序並不會報錯,可是若是把方法名寫成了getNaem程序當即報錯。
第四種lambda構造器
細心的人都會發現不管是以前的lambda構造器仍是queryWrapper,每次編寫完條件構造語句後都要將對象傳遞給mapper 的selectList方法,比較麻煩,MyBatisPlus提供了第四種函數式編程方式,不用每次都傳。
@Test public void lambdaSelect(){ List<User> userList = new LambdaQueryChainWrapper<>(userMapper).like(User::getName, "雨").ge(User::getAge, 20).list(); userList.forEach(System.out::println); }
mybatis-plus: mapper-locations: mapper/*.xml
UserMapper
public interface UserMapper extends BaseMapper<User> { /** * 查詢全部用戶信息 * @return list */ List<User> selectAll(); }
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="org.ywb.demo.dao.UserMapper"> <select id="selectAll" resultType="org.ywb.demo.pojo.User"> select * from user </select> </mapper>
MyBatis分頁提供的是邏輯分頁,每次將全部數據查詢出來,存儲到內存中,而後根據頁容量,逐頁返回。若是表很大,無疑是一種災難!
MyBatisPlus物理分頁插件
PaginationInterceptor
對象@Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } }
@Test public void selectPage(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.ge("age",20); //設置當前頁和頁容量 Page<User> page = new Page<>(1, 2); IPage<User> userIPage = userMapper.selectPage(page, queryWrapper); System.out.println("總頁數:"+userIPage.getPages()); System.out.println("總記錄數:"+userIPage.getTotal()); userIPage.getRecords().forEach(System.out::println); }
IPage類的構造參數提供了參數的重載,第三個參數爲false時,不會查詢總記錄數。
public Page(long current, long size, boolean isSearchCount) { this(current, size, 0, isSearchCount); } ~~· ## 更新 1. 經過userMapper提供的方法更新用戶信息
@Test public void updateTest1(){ User user = new User(); user.setId(1088250446457389058L); user.setEmail("update@email"); int rows = userMapper.updateById(user); System.out.println(rows); }
![image.png](https://upload-images.jianshu.io/upload_images/11774306-f9daecdc4b339612.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 2. 使用UpdateWrapper更新數據(至關於使用聯合主鍵)
@Test public void updateTest2(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("name","李藝偉").eq("age",26); User user = new User(); user.setEmail("update2@email"); int rows = userMapper.update(user, updateWrapper); System.out.println(rows); }
![image.png](https://upload-images.jianshu.io/upload_images/11774306-74dbadc4c7b22a31.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 3. 當咱們更新少許用戶信息的時候,能夠不用建立對象,直接經過調用set方法更新屬性便可。
@Test public void updateTest3(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("name","李藝偉").eq("age",26).set("email","update3@email.com"); userMapper.update(null,updateWrapper); }
![image.png](https://upload-images.jianshu.io/upload_images/11774306-b786482a228c6fb5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 4. 使用lambda更新數據
@Test public void updateByLambda(){ LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate(); lambdaUpdateWrapper.eq(User::getName,"李藝偉").eq(User::getAge,26).set(User::getAge,27); userMapper.update(null,lambdaUpdateWrapper); }
![image.png](https://upload-images.jianshu.io/upload_images/11774306-32e7875e86108986.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 刪除 刪除方式和update極其相似。 ## AR模式(Active Record) 直接經過實體類完成對數據的增刪改查。 1. 實體類繼承Model類
@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {
private Long id; @TableField(condition = SqlCondition.LIKE) private String name; private Integer age; private String email; private Long managerId; private LocalDateTime createTime;
}
Model類中封裝了不少增刪改查方法,不用使用UserMapper便可完成對數據的增刪改查。 1. 查詢全部用戶信息
@Test public void test(){ User user = new User(); user.selectAll().forEach(System.out::println); }
![image.png](https://upload-images.jianshu.io/upload_images/11774306-6acd5af7b02fb310.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 主鍵策略 MyBatisPlus的主鍵策略封裝在`IdType`枚舉類中。
@Getter
public enum IdType {
/** * 數據庫ID自增 */ AUTO(0), /** * 該類型爲未設置主鍵類型(將跟隨全局) */ NONE(1), /** * 用戶輸入ID * <p>該類型能夠經過本身註冊自動填充插件進行填充</p> */ INPUT(2), /* 如下3種類型、只有當插入對象ID 爲空,才自動填充。 */ /** * 全局惟一ID (idWorker) */ ID_WORKER(3), /** * 全局惟一ID (UUID) */ UUID(4), /** * 字符串全局惟一ID (idWorker 的字符串表示) */ ID_WORKER_STR(5); private final int key; IdType(int key) { this.key = key; }
}
在實體類中對應數據庫中的主鍵id屬性上標註註解`TableId(type='xxx')`便可完成主鍵配置。
@TableId(type = IdType.AUTO) private Long id;
這種配置方式的主鍵策略只能在該表中生效,可是其餘表還須要進行配置,爲了不冗餘,麻煩,MybatisPlus提供了全局配置,在配置文件中配置主鍵策略便可實現。
mybatis-plus:
mapper-locations: mapper/*.xml
global-config:
db-config: id-type: auto
若是全局策略和局部策略全都設置,局部策略優先。 ## 基本配置 [MyBatisPlus官方文檔](https://baomidou.gitee.io/mybatis-plus-doc/#/api?id=globalconfiguration)
mybatis-plus:
mapper-locations: mapper/*.xml
global-config:
db-config: # 主鍵策略 id-type: auto # 表名前綴 table-prefix: t # 表名是否使用下劃線間隔,默認:是 table-underline: true
# 添加mybatis配置文件路徑
config-location: mybatis-config.xml
# 配置實體類包地址
type-aliases-package: org.ywb.demo.pojo
# 駝峯轉下劃線
configuration:
map-underscore-to-camel-case: true
- 附錄 1. mybatisPlus進階功能請戳[ MyBatisPlus學習整理(二)](https://www.jianshu.com/p/5c9e6acf9a70) 2. 源碼地址:[https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo](https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo)