MyBatis-Plus實(shí)用篇超完整教程
一、基礎(chǔ)組件
簡(jiǎn)介
- MyBatis-Plus (opens new window)(簡(jiǎn)稱(chēng) MP)是一個(gè) MyBatis (opens new window)的
增強(qiáng)工具
- 在MyBatis 的基礎(chǔ)上
只做增強(qiáng)不做改變
,為簡(jiǎn)化開(kāi)發(fā)、提高效率而生
1、BaseMapper接口API
- BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型為操作的實(shí)體類(lèi)型
- Mapper繼承該接口后,無(wú)需編寫(xiě) mapper.xml 文件,即可獲得CRUD功能
- BaseMapper接口,增刪改返回影響數(shù)據(jù)條數(shù)的Integer
BaseMapper中提供的CRUD方法
增加:insert
刪除:delete
- 修改:update
- update方法:entity實(shí)體對(duì)象某屬性為null,則不會(huì)修改此屬性
查詢(xún):select
- selectObjs方法:只返回第一個(gè)字段的值
- selectPage方法:分頁(yè)需要添加分頁(yè)插件,否則不生效
2、IService接口API
- 封裝IService接口,進(jìn)一步封裝CRUD采用
get查詢(xún)單行、remove刪除、list查詢(xún)集合、page分頁(yè)前綴
命名方式 - IService接口,增刪改返回是否操作成功的boolean
- 泛型 T 為任意實(shí)體對(duì)象
IService中提供的CRUD方法
增加:save
刪除:remove
修改:update
新增或修改:主鍵存在則根據(jù)主鍵修改,主鍵不存在則新增
- 查詢(xún):?jiǎn)蝹€(gè)get,集合listgetOne方法,多個(gè)拋出異常,第二個(gè)參數(shù)
throwEx設(shè)置為false
則獲取第一條數(shù)據(jù)
查詢(xún)記錄數(shù):count
分頁(yè)查詢(xún):page
- 集合查詢(xún):listlistObjs方法,可以將查詢(xún)結(jié)果T類(lèi)型轉(zhuǎn)換成V類(lèi)型返回List<V>
3、創(chuàng)建Service層操作數(shù)據(jù)
Mapper接口:創(chuàng)建UserMapper對(duì)象并繼承BaseMapper
@Mapper public interface UserMapper extends BaseMapper<User> {}
Service接口:創(chuàng)建UserService并繼承IService
/** * UserService繼承IService模板提供的基礎(chǔ)功能 */ public interface UserService extends IService<User> {}
Service實(shí)現(xiàn)類(lèi):創(chuàng)建UserService的實(shí)現(xiàn)類(lèi)并繼承ServiceImpl
/** * ServiceImpl實(shí)現(xiàn)了IService,提供了IService中基礎(chǔ)功能的實(shí)現(xiàn) * 若ServiceImpl無(wú)法滿足業(yè)務(wù)需求,則可以使用自定的UserService定義方法,并在實(shí)現(xiàn)類(lèi)中實(shí)現(xiàn) */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{}
二、常用注解
1、@TableName
- 在實(shí)體類(lèi)類(lèi)型上添加@TableName(“t_user”),標(biāo)識(shí)實(shí)體類(lèi)對(duì)應(yīng)的表
- 不加注解,默認(rèn)駝峰轉(zhuǎn)下劃線則是表明,如實(shí)體OrderInfo,則默認(rèn)對(duì)應(yīng)表名order_info
@Data @TableName("t_user") public class User { private Long id; private String name; private Integer age; private String email; }
為實(shí)體類(lèi)所對(duì)應(yīng)的表名設(shè)置默認(rèn)的前綴,那么就不需要在每個(gè)實(shí)體類(lèi)上通過(guò)@TableName標(biāo)識(shí)實(shí)體類(lèi)對(duì)應(yīng)的表
mybatis-plus: global-config: db-config: # 設(shè)置實(shí)體類(lèi)所對(duì)應(yīng)的表的統(tǒng)一前綴 table-prefix: t_
2、@TableId
2.1、value屬性
不加注解,默認(rèn)情況Long id就是主鍵如果想其他字段作為主鍵,且實(shí)體與數(shù)據(jù)庫(kù)字段不一致,則添加@TableId(value = “tid”)
@Data public class User { @TableId(value = "tid") private Long uid; private String name; private Integer age; private String email; }
2.2、type屬性
常用主鍵策略:
枚舉值 | 描述 |
---|---|
IdType.AUTO | 數(shù)據(jù)庫(kù)ID自增;該類(lèi)型請(qǐng)確保數(shù)據(jù)庫(kù)設(shè)置了ID自增,否則無(wú)效 |
IdType.INPUT | 用戶(hù)輸入ID;該類(lèi)型可以通過(guò)自己注冊(cè)自動(dòng)填充插件進(jìn)行填充 |
IdType.ASSIGN_ID(默認(rèn)) | 主鍵類(lèi)型為number或string的雪花算法;只有當(dāng)插入對(duì)象ID 為空,才自動(dòng)填充 |
IdType.ASSIGN_UUID | 主鍵類(lèi)型為string,UUID.replace(“-”,“”);只有當(dāng)插入對(duì)象ID 為空,才自動(dòng)填充 |
IdType.NONE | 沒(méi)有設(shè)置主鍵類(lèi)型;跟隨全局;全局的主鍵策略如果沒(méi)有設(shè)置,默認(rèn)是雪花算法 |
@Data public class User { @TableId(type = IdType.AUTO) private Long uid; private String name; private Integer age; private String email; }
Mybatis-plus中的內(nèi)置雪花算法
- 在某些情況下,我們想提前獲取這個(gè)ID,調(diào)用
com.baomidou.mybatisplus.core.toolkit.IdWorker.getId()
方法即可
3、@TableField
3.1、value屬性
- 解決對(duì)象中字段名和數(shù)據(jù)庫(kù)不匹配(沒(méi)有遵循小駝峰或者完全不匹配)
//指定數(shù)據(jù)庫(kù)字段名稱(chēng) @TableField(value = "email") private string mail
解決關(guān)鍵字報(bào)錯(cuò)
// 執(zhí)行的sql語(yǔ)句就變成`關(guān)鍵字`,這樣sql就不會(huì)報(bào)錯(cuò)了 @TableField(value = "`desc`") private string desc
3.2、exist屬性
解決對(duì)象中的屬性字段在表中不存在的問(wèn)題
@TableField(exist = false) private Boolean checked;
3.3、select屬性
在查詢(xún)操作中某個(gè)字段值不想被查詢(xún)展示出來(lái)(比如密碼password),可使用此注解
@TableField(select = false) private String password;
3.4、condition屬性
預(yù)處理where查詢(xún)條件
// 實(shí)體注解-實(shí)現(xiàn)模糊查詢(xún) @TableField(condition = SqlCondition.LIKE) private String name; // 業(yè)務(wù)代碼 User user = new User(); user.setName("張"); userService.lambdaQuery(user).list(); // 打印sql SELECT id,user_name,age FROM user WHERE name LIKE CONCAT('%',?,'%')
鏈?zhǔn)讲樵?xún)不生效(沒(méi)有模糊查詢(xún))
userService.lambdaQuery().eq(User::getName,"張").list();
3.5、update屬性
- 預(yù)處理update set 部分
- 方式一:@TableField(update="%s+1") update 表 set 字段=字段+1 where …
- 方式二:@TableField(update="now()") 使用數(shù)據(jù)庫(kù)時(shí)間,update 表 set 字段=now() where …
// 方式三: // 實(shí)體注解-實(shí)現(xiàn)修改字段時(shí)候,前后添加“**” 其中 %s 會(huì)填充為字段 @TableField(update = "CONCAT('**',%s,'**')") private String name; // 業(yè)務(wù)代碼 User user = new User(); user.setId(1); user.setName("張三"); userService.updateById(user); // 打印sql UPDATE user SET name=CONCAT('**',name,'**') WHERE id=?
鏈?zhǔn)讲樵?xún)不生效(前后沒(méi)有填充**)
userService.lambdaQuery().eq(User::getName,"張").list();
3.6、fill屬性
解決每個(gè)數(shù)據(jù)庫(kù)表都有create_time 和 update_time字段,我們可以使用自動(dòng)填充功能維護(hù)這兩個(gè)字段
// 實(shí)體字段注解 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; // 枚舉類(lèi) public enum FieldFill { /** * 默認(rèn)不處理 */ DEFAULT, /** * 插入填充字段 */ INSERT, /** * 更新填充字段 */ UPDATE, /** * 插入和更新填充字段 */ INSERT_UPDATE }
創(chuàng)建MyMetaObjectHandler配置類(lèi),實(shí)現(xiàn)MetaObjectHandler
接口
@Component public class MyMetaObjectHandler implements MetaObjectHandler { Date date = new Date(); @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", Date.class, date); this.strictInsertFill(metaObject, "updateTime", Date.class, date); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", Date.class, date); } }
3.7、insertStrategy、updateStrategy、whereStrategy屬性
- insertStrategy:當(dāng)insert操作時(shí),該字段拼接insert語(yǔ)句時(shí)的策略
- updateStrategy:當(dāng)更新操作時(shí),該字段拼接set語(yǔ)句時(shí)的策略
- whereStrategy:表示該字段在拼接where條件時(shí)的策略
public enum FieldStrategy { /** * 忽略判斷,所有字段都進(jìn)行更新和插入 * 無(wú)論什么值,直接拼接,沒(méi)有值,則拼接null */ IGNORED, /** * 只更新和插入非NULL值 * 相當(dāng)于添加if判斷,不為null才操作 * <if test="columnProperty != null">column=#{columnProperty}</if> */ NOT_NULL, /** * 只更新和插入非NULL值且非空字符串 * 相當(dāng)于添加if判斷,不為null而且不是空字符串才操作 * <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> */ NOT_EMPTY, /** * 默認(rèn)NOT_NULL */ DEFAULT, /** * 永遠(yuǎn)不進(jìn)行更新和插入 */ NEVER }
3.8、typeHandler屬性
- 類(lèi)型處理器,設(shè)置存入數(shù)據(jù)庫(kù)的類(lèi)型
- 設(shè)置mysql數(shù)據(jù)庫(kù)字段info為json類(lèi)型,@TableField(typeHandler = JacksonTypeHandler.class)表示將UserInfo對(duì)象轉(zhuǎn)為json對(duì)象入庫(kù)
- 此時(shí)user出現(xiàn)對(duì)象嵌套List<Address>,需要設(shè)置resultMap響應(yīng)對(duì)象,@TableName(autoResultMap = true)表示自動(dòng)映射resultMap
4、@TableLogic
- 物理刪除:真實(shí)刪除,將對(duì)應(yīng)數(shù)據(jù)從數(shù)據(jù)庫(kù)中刪除,之后查詢(xún)不到此條被刪除的數(shù)據(jù)
- 邏輯刪除:假刪除,將對(duì)應(yīng)數(shù)據(jù)中代表是否被刪除字段的狀態(tài)修改為“被刪除狀態(tài)”,之后在數(shù)據(jù)庫(kù)中仍舊能看到此條數(shù)據(jù)記錄
實(shí)現(xiàn)邏輯刪除
數(shù)據(jù)庫(kù)中創(chuàng)建邏輯刪除狀態(tài)列,設(shè)置默認(rèn)值為0
實(shí)體類(lèi)中添加邏輯刪除屬性
測(cè)試刪除功能,真正執(zhí)行的是修改
public void testDeleteById(){ int result = userMapper.deleteById(1527472864163348482L); System.out.println(result > 0 ? "刪除成功!" : "刪除失?。?); System.out.println("受影響的行數(shù)為:" + result); }
此時(shí)執(zhí)行查詢(xún)方法,查詢(xún)的結(jié)果為自動(dòng)添加條件is_deleted=0
public void testSelectList(){ List<User> users = userMapper.selectList(null); }
三、條件構(gòu)造器
1、wapper介紹
- Wrapper:條件構(gòu)造抽象類(lèi),最頂端父類(lèi)
- AbstractWrapper:用于查詢(xún)條件封裝,生成sql的where條件
- QueryWrapper:查詢(xún)條件封裝
- UpdateWrapper:update條件封裝
- AbstractLambdaWrapper:使用Lambda語(yǔ)法
- LambdaQueryWrapper:用于Lambda語(yǔ)法使用的查詢(xún)Wrapper
- LambdaUpdateWrapper:用于Lambda語(yǔ)法更新Wrapper
- AbstractWrapper:用于查詢(xún)條件封裝,生成sql的where條件
2、構(gòu)造器常用方法
函數(shù)名 | 說(shuō)明 | 說(shuō)明/例子 |
---|---|---|
eq | 等于= | 例:eq(“name”,“zhangsan”) —> name = ‘zhangsan’ |
ne | 不等于<> | 例:ne(“name”,“zhangsan”) —> name <> ‘zhangsan’ |
gt | 大于> | 例:gt(“age”,18) —> age > 18 |
ge | 大于等于>= | 例:ge(“age”,18) —> age >= 18 |
lt | 小于< | 例:lt(“age”,18) —> age < 18 |
le | 小于等于<= | 例:le(“age”,18) —> age <= 18 |
between | between 值1 and 值2 | 例:between(“age”,10,20) —> age between 10 and 20 |
notBetween | not between 值1 and 值2 | 例:notBetween(“age”,10,20) —> age not between 10 and 20 |
like | like ‘%值%’ | 例:like(“name”,“強(qiáng)”) —> name like ‘%強(qiáng)%’ |
notLike | not like ‘%值%’ | 例:notLike(“name”,“強(qiáng)”) —> name not like ‘%強(qiáng)%’ |
likeLeft | like ‘%值’ | 例:like(“name”,“飛”) —> name like ‘%強(qiáng)’ |
likeRight | like ‘值%’ | 例:like(“name”,“王”) —> name like ‘王%’ |
isNull | 字段 is null | 例:isNull(“emal”) —> email is null |
isNotNull | 字段 is not null | 例:isNotNull(“emal”) —> email is not null |
in | 字段 in (值1,值2…) | 例:in(“age”,{10,18,30}) —> age in (10,18,30) |
notIn | 字段 not in (值1,值2…) | 例:notIn(“age”,{10,18,30}) —> age not in (10,18,30) |
inSql | 字段 in ( sql語(yǔ)句 ) | inSql(“id”, “select id from table where name like ‘%J%’”) —> id in (select id from table where name like ‘%J%’) |
notInSql | 字段 not in ( sql語(yǔ)句 ) | notInSql(“id”, “select id from table where name like ‘%J%’”) —> id not in (select id from table where name like ‘%J%’) |
groupBy | 分組 group by 字段,… | 例:groupBy(“id”,“name”) —> group by id,name ?? 注意:如果不查詢(xún)聚合函數(shù),默認(rèn)返回第一條數(shù)據(jù) |
orderBy | 排序 ordery by 字段,… | 例:orderBy(true,true,“id”,“name”) —> order by id asc,name asc |
orderByAsc | 升排序 order by 字段,… asc | 例:orderByAsc(“id”,“name”) —> order by id,name |
orderByDesc | 降排序 order by 字段,… desc | 例:orderByDesc(“id”,“name”) —> order by id desc,name desc |
having | having (sql語(yǔ)句) | having(“sum(age) > {0}”,18) —> having sum(age) > 18 |
or | 拼接or | 例:eq(“id”,1).or().eq(“name”,“老王”) —> id =1 or name = ‘老王’ |
and | and 嵌套 | 例:and(i -> i.eq(“name”,“李白”).ne(“status”,“活著”)) —> and (name = ’李白‘ and status <> ‘活著’) |
apply | 拼接sql | **例:apply(“date_format(date_time,‘%Y-%m-%d’) = {0}”,“2002-08-08”) —> date_fromat(date_time,‘%Y-%m-%d’) = ‘2008-08-08’ ?? 注意:動(dòng)態(tài)入?yún)?duì)應(yīng){index}部分,直接寫(xiě)sql語(yǔ)句,有sql注入風(fēng)險(xiǎn) |
last | 拼接到sql的最后 | 例:last(“limit 5”) 注意事項(xiàng):只能調(diào)用一次,多次調(diào)用以最后一次為準(zhǔn),有sql注入風(fēng)險(xiǎn) |
exists | 拼接exists (sql語(yǔ)句) | 例:exists(“select id from table where age = 1”) |
not exists | 拼接not exists (sql語(yǔ)句) | 例:not exists(“select id from table where age = 1”) |
nested | 正常嵌套 不帶and和or | 例:nested(i-> i.eq(“name”,“李華”).gt(“age”,20)) —> (name = “李華” and age > 20) |
3、組裝條件示例
條件的優(yōu)先級(jí)
sql語(yǔ)句規(guī)則:and和or一起用,and優(yōu)先級(jí)高
/** 組裝修改條件 將(年齡大于20并且用戶(hù)名中包含有a)或郵箱為null的用戶(hù)信息修改 **/ @Test public void test01() { /* UPDATE t_user SET user_name=?, email=? age > ? AND user_name LIKE ? OR email IS NOT NULL */ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.gt("age", 20) .like("user_name", "a") .or() .isNotNull("email"); User user = new User(); user.setName("小明"); user.setEmail("test@atguigu.com"); int result = userMapper.update(user, queryWrapper); System.out.println("result = " + result); } /** 條件優(yōu)先級(jí) 將用戶(hù)名中包含a并且(年齡大于20或郵箱為null)的用戶(hù)信息修改 **/ @Test public void test02() { // lambda中條件優(yōu)先級(jí) /* UPDATE t_user SET user_name=?, email=? WHERE user_name LIKE ? AND (age > ? OR email IS NULL) */ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("user_name", "a") .and(i -> i.gt("age", 20).or().isNull("email")); User user = new User(); user.setName("小紅"); user.setEmail("test@atguigu.com"); int result = userMapper.update(user, queryWrapper); System.out.println("result = " + result); }
Lambda表達(dá)式
@Test public void test3() { /* SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE user_name LIKE ? AND age <= ? */ String username = "a"; Integer ageBegin = null; Integer ageEnd = 30; //組裝set子句 LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); //避免使用字符串表示字段,防止運(yùn)行時(shí)錯(cuò)誤 queryWrapper.like(StringUtils.isNotBlank(username), User::getName, username) .gt(ageBegin != null, User::getAge, ageBegin) .le(ageEnd != null, User::getAge, ageEnd); List<User> list = userMapper.selectList(queryWrapper); list.forEach(System.out::println); } @Test public void test4() { /* UPDATE t_user SET user_name=?,email=? WHERE user_name LIKE ? AND (age > ? OR email IS NULL) */ LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.like(User::getName, "a") //lambda表達(dá)式內(nèi)的邏輯優(yōu)先運(yùn)算 .and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail)); updateWrapper.set(User::getName, "小黑").set(User::getEmail, "abc@atguigu.com"); int result = userMapper.update(null, updateWrapper); System.out.println("result = " + result); }
四、擴(kuò)展功能
1、鏈?zhǔn)讲樵?xún)與修改
IService接口中提供
- 鏈?zhǔn)讲樵?xún):可以串著寫(xiě)多個(gè)條件
- .one()結(jié)尾:查詢(xún)單條數(shù)據(jù)
- .list()結(jié)尾:查詢(xún)多條數(shù)據(jù)
- .count()結(jié)尾:查詢(xún)記錄數(shù)
@Test public void test1(){ // 單個(gè)查詢(xún),多個(gè)報(bào)錯(cuò) User user = userService.lambdaQuery().eq(User::getName, "Tom").one(); // 集合查詢(xún) List<User> userList = userService.lambdaQuery().like(User::getName, "J").eq(User::getAge,20).list(); // 記錄數(shù)查詢(xún) Integer count = userService.lambdaQuery().like(User::getAge, 20).select(User::getName, User::getAge).count(); }
- 鏈?zhǔn)叫薷模嚎梢源鴮?xiě)多個(gè)條件
- .update()結(jié)尾:修改set的字段(只修改set的字段,fill屬性更新填充屬性不會(huì)修改)
- .update(entity)結(jié)尾:修改entity數(shù)據(jù)的字段
- .remove()結(jié)尾:刪除數(shù)據(jù)
@Test public void test2() { // set修改某些屬性 userService.lambdaUpdate().eq(User::getName, "Tom").set(User::getId, 110).set(User::getAge, 15).update(); // 修改整個(gè)對(duì)象 User user = new User(); user.setId(110L); user.setAge(15); userService.lambdaUpdate().eq(User::getName, "Tom").update(user); // 根據(jù)名稱(chēng)刪除 userService.lambdaUpdate().eq(User::getName, "Jack").remove(); }
2、靜態(tài)工具類(lèi)Db
Service之間也會(huì)相互調(diào)用,為了避免出現(xiàn)循環(huán)依賴(lài)問(wèn)題,MybatisPlus提供一個(gè)靜態(tài)工具類(lèi):Db
@Test void testDbGet() { User user = Db.getById(1L, User.class); System.out.println(user); } ? @Test void testDbList() { // 利用Db實(shí)現(xiàn)復(fù)雜條件查詢(xún) List<User> list = Db.lambdaQuery(User.class) .like(User::getUsername, "o") .ge(User::getBalance, 1000) .list(); } ? @Test void testDbUpdate() { Db.lambdaUpdate(User.class) .set(User::getBalance, 2000) .eq(User::getUsername, "Rose"); }
這樣就可以在任意serviceImpl中都可以使用任意的service,而不需要依賴(lài)注入了
4、自定義sql
4.1、自定義xml分頁(yè)sql
UserMapper中定義接口方法
page 分頁(yè)對(duì)象,xml中可以從里面進(jìn)行取值,傳遞參數(shù) Page 即自動(dòng)分頁(yè),必須放在第一位
/** * 根據(jù)年齡查詢(xún)用戶(hù)列表,分頁(yè)顯示 */ Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
UserMapper.xml中編寫(xiě)SQL
<select id="selectPageVo" resultType="User"> select id,username as name,age,email from t_user where age > #{age} </select>
測(cè)試方法
- xml中沒(méi)有分頁(yè)語(yǔ)句,mybatisplus自動(dòng)分頁(yè)
- 前提必須有分頁(yè)插件,否則沒(méi)有分頁(yè)效果
@Test public void testSelectPageVo(){ //設(shè)置分頁(yè)參數(shù) Page<User> page = new Page<>(1, 5); userMapper.selectPageVo(page, 20); //獲取分頁(yè)數(shù)據(jù) List<User> list = page.getRecords(); list.forEach(System.out::println); System.out.println("當(dāng)前頁(yè):"+page.getCurrent()); System.out.println("每頁(yè)顯示的條數(shù):"+page.getSize()); System.out.println("總記錄數(shù):"+page.getTotal()); System.out.println("總頁(yè)數(shù):"+page.getPages()); System.out.println("是否有上一頁(yè):"+page.hasPrevious()); System.out.println("是否有下一頁(yè):"+page.hasNext()); }
4.2、@Select注解自定義sql
普通sql
@Select("select * from student where age = #{age} and user_name = #{userName}") Student getStudent( @Param("age") Integer age, @Param("userName") String userName);
帶Wrapper條件sql
@Param("ew")
:固定寫(xiě)法,可以參照BaseMapper接口里的方法${ew.customSqlSegment}
:固定寫(xiě)法,在sql后面拼接條件,不需要寫(xiě)where關(guān)鍵字
@Param("page") Page<?> page
:Wrapper條件和分頁(yè)可以共用,但是分頁(yè)需要寫(xiě)在最前面
// 單表查詢(xún) @Select("select * from student ${ew.customSqlSegment}") Student getStudentById( @Param("ew") QueryWrapper<Student> wrapper); // 多表查詢(xún) @Select("SELECT u.* FROM student u INNER JOIN address a ON u.id = a.student_id ${ew.customSqlSegment}") List<Student> queryStudentListByWrapper(@Param("page") Page<Student> page, @Param("ew")QueryWrapper<User> wrapper);
4.3、連表left join和inner join的分頁(yè)查詢(xún)區(qū)別
left join
查詢(xún)通過(guò)Page<?> page參數(shù)自動(dòng)分頁(yè)時(shí)候,查詢(xún)總條數(shù)COUNT(*)時(shí)候會(huì)將left join去掉,也就是只查主表的數(shù)據(jù)- 此時(shí)如果有副表的字段作為查詢(xún)條件會(huì)報(bào)錯(cuò):Unknown column ‘xxx’ in ‘where clause’(主表找不到xxx字段)
// 情況1:如果查詢(xún)條件中有address的字段,比如模糊查詢(xún)地址名稱(chēng) // 情況2:如果查詢(xún)條件的字段,兩個(gè)表都有,這里會(huì)有問(wèn)題 // 自動(dòng)分頁(yè)的查詢(xún)count(*)時(shí)候,就會(huì)拋出以上異常 @Select("SELECT u.* FROM student u LEFT JOIN address a ON u.id = a.student_id ${ew.customSqlSegment}") List<Student> queryStudentListByWrapper(@Param("page") Page<Student> page, @Param("ew")QueryWrapper<User> wrapper);
解決方式:嵌套一層select * from (連表復(fù)雜sql)as result ${ew.customSqlSegment}
@Select("SELECT * FROM (SELECT u.* FROM student u LEFT JOIN address a ON u.id = a.student_id) as result ${ew.customSqlSegment}") List<Student> queryStudentListByWrapper(@Param("page") Page<Student> page, @Param("ew")QueryWrapper<User> wrapper);
inner join
查詢(xún)通過(guò)Page<?> page參數(shù)自動(dòng)分頁(yè)時(shí)候,查詢(xún)總條數(shù)COUNT(*)時(shí)候不會(huì)將inner join去掉,所以一般不需要嵌套
4.4、連表查詢(xún)條件Wrapper和響應(yīng)IPage的泛型
- 查詢(xún)條件Wrapper和響應(yīng)IPage的泛型
非表實(shí)體
也行,只有符合數(shù)據(jù)庫(kù)字段的駝峰命名
即可(需要?jiǎng)?chuàng)建實(shí)體對(duì)象) - 如果查詢(xún)條件同時(shí)需要多個(gè)表字段,也可以
@Param("ew") Wrapper<?> queryWrapper
(不需要?jiǎng)?chuàng)建實(shí)體對(duì)象)
5、IPage的泛型轉(zhuǎn)換(entity轉(zhuǎn)換為vo)
// 分頁(yè)查詢(xún) Page<UserEntity> userPage = super.lambdaQuery().page(new Page<>(request.getCurrent(), request.getSize())); // entity轉(zhuǎn)換為vo IPage<UserVO> infoResPage = userPage.convert(item -> Convert.convert(UserVO.class, item));
6、SimpleQuery工具類(lèi)
Simplequery可以對(duì)selectList查詢(xún)后的結(jié)果用stream流
進(jìn)行了一些封裝,使其可以返回一些指定結(jié)果,簡(jiǎn)潔api的調(diào)用
示例
獲取Student對(duì)象集合的名字集合
// 方式一 List<String> nameList = SimpleQuery.list( new LambdaQueryWrapper<Student>().like(Student::getUserName, "張"), Student::getUserName ); // 方式二 List<Student> list = studentService.list(); List<String> nameList2 = SimpleQuery.list2List(list, Student::getUserName);
先將Student對(duì)象的名稱(chēng)前后添加**再獲取名字集合
List<String> nameList = SimpleQuery.list( new LambdaQueryWrapper<Student>().like(Student::getUserName, "李"), Student::getUserName, new Consumer<Student>() { @Override public void accept(Student student) { student.setUserName("**"+student.getUserName()+"**"); } } );
返回key=名字,value=年齡的map
Map<String, Integer> map = SimpleQuery.map( new LambdaQueryWrapper<>(), Student::getUserName, Student::getAge );
group分組,key位名字,value相同名字的Student對(duì)象集合
Map<String, List<Student>> group = SimpleQuery.group( new LambdaQueryWrapper<Student>(), Student::getUserName );
7、ActiveRecord
- ActiveRecord(活動(dòng)記錄,簡(jiǎn)稱(chēng) AR),是一種領(lǐng)域模型模式,特點(diǎn)是一個(gè)模型類(lèi)對(duì)應(yīng)關(guān)系型數(shù)據(jù)庫(kù)中的一個(gè)表,而模型類(lèi)的一個(gè)實(shí)例對(duì)應(yīng)表中的一行記錄
- ActiveRecord 一直廣受解釋型動(dòng)態(tài)語(yǔ)言( PHP 、 Ruby 等)的喜愛(ài),通過(guò)圍繞一個(gè)數(shù)據(jù)對(duì)象進(jìn)行CRUD操作
- 僅僅需要讓實(shí)體類(lèi)繼承Model類(lèi)且實(shí)現(xiàn)主鍵指定方法
@Data @TableName(value = "student") public class Student extends Model<Student> { /** * 主鍵id */ private Long id; /** * 姓名 */ private String userName; /** * 年齡 */ private Integer age; } @Test void activeRecordAddQ{ Student student = new Student(); student.setUserName("李四"); student.setAge(12); student.insert(); }
Model抽象類(lèi)方法
注意: 必須存在對(duì)應(yīng)的原始mapper并繼承baseMapper并且可以使用
五、插件
1、Mybatis插件
MybatisPlusInterceptor分頁(yè)插件
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //注意使用哪種數(shù)據(jù)庫(kù) interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
2、MyBatisX插件
2.1、生成逆向工程
找到我們需要生成的表點(diǎn)擊右鍵
填寫(xiě)完信息以后下一步
繼續(xù)填寫(xiě)信息
結(jié)果展示
2.2、快速生成CRUD
MyBaitsX可以根據(jù)我們?cè)贛apper接口中輸入的方法名【alt+Enter
】快速幫我們生成對(duì)應(yīng)的sql語(yǔ)句
到此這篇關(guān)于MyBatis-Plus實(shí)用篇超完整教程的文章就介紹到這了,更多相關(guān)MyBatis-Plus實(shí)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用JavaBean根據(jù)指定條件設(shè)置屬性值默認(rèn)值方式
這篇文章主要介紹了使用JavaBean根據(jù)指定條件設(shè)置屬性值默認(rèn)值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03SSH框架網(wǎng)上商城項(xiàng)目第8戰(zhàn)之查詢(xún)和刪除商品類(lèi)別功能實(shí)現(xiàn)
SSH框架網(wǎng)上商城項(xiàng)目第8戰(zhàn)之查詢(xún)和刪除商品類(lèi)別功能實(shí)現(xiàn),為項(xiàng)目增加功能,添加、更新、刪除和查詢(xún)操作,感興趣的小伙伴們可以參考一下2016-05-05Java19新特性中結(jié)構(gòu)化并發(fā)的使用
Java19在并發(fā)編程領(lǐng)域引入了一個(gè)全新的概念:結(jié)構(gòu)化并發(fā),這一特性旨在簡(jiǎn)化并發(fā)任務(wù)的管理,提升多線程程序的可維護(hù)性和安全性,使其生命周期和控制流更加有序和明確,感興趣的可以了解一下2024-09-09Java多線程環(huán)境下SimpleDateFormat類(lèi)安全轉(zhuǎn)換
這篇文章主要介紹了Java多線程環(huán)境下SimpleDateFormat類(lèi)安全轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02springboot整合Mybatis、JPA、Redis的示例代碼
這篇文章主要介紹了springboot整合Mybatis、JPA、Redis的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java ArrayList與Vector和LinkedList的使用及源碼分析
ArrayList、Vector、LinkedList類(lèi)均在java.util包中,均為可伸縮數(shù)組,即可以動(dòng)態(tài)改變長(zhǎng)度的數(shù)組。ArrayList 和 Vector都是基于存儲(chǔ)元素的Object[] array來(lái)實(shí)現(xiàn)的,它們會(huì)在內(nèi)存中開(kāi)辟一塊連續(xù)的內(nèi)存來(lái)存儲(chǔ)2022-11-11Spring Boot實(shí)戰(zhàn)之?dāng)?shù)據(jù)庫(kù)操作的示例代碼
本篇文章主要介紹了Spring Boot實(shí)戰(zhàn)之?dāng)?shù)據(jù)庫(kù)操作的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01dubbo如何設(shè)置連接zookeeper權(quán)限
這篇文章主要介紹了dubbo如何設(shè)置連接zookeeper權(quán)限問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05