mybatisplus?復(fù)合主鍵(多主鍵)?CRUD示例詳解
mybatisplus 復(fù)合主鍵CRUD
需求描述
最近接到個(gè)挺有意思的需求,做用戶觀看學(xué)習(xí)視頻時(shí)長的一個(gè)數(shù)據(jù)埋點(diǎn)
儲存用戶觀看視頻時(shí)長、記錄的接口的調(diào)用肯定會特別頻繁,因?yàn)槊块g隔指定時(shí)間每個(gè)用戶都會調(diào)用,如果在這個(gè)接口里直接操作數(shù)據(jù)庫將會給我們的數(shù)據(jù)庫帶來一定的壓力,在我的代碼中是不允許的,而我是這樣完成這個(gè)需求的:
首先將用戶觀看視頻的時(shí)長、記錄存儲到阿里云的日志庫里,隨后以定時(shí)器從阿里云的日志庫中拉取用戶觀看視頻的數(shù)據(jù)同步到我們的數(shù)據(jù)庫中。
而就是最后這一步,同步數(shù)據(jù)到數(shù)據(jù)庫中,這里的數(shù)據(jù)量肯定是龐大的,所以我做了分表。
但是盡管做了分表數(shù)據(jù)量也不少,如果通過自增的主鍵id去編輯數(shù)據(jù)那么我在更新數(shù)據(jù)之前都要先從數(shù)據(jù)庫中查詢一次,然后在更新
在數(shù)據(jù)量大的情況下依然會給我們數(shù)據(jù)庫造成不少壓力,且這個(gè)定時(shí)器的執(zhí)行時(shí)長將會拉大,這是我不能接受的
所以直接使用復(fù)合主鍵,以視頻id+用戶id去批量更新數(shù)據(jù),這樣就會快很多,然而mybatisplus卻僅支持單一主鍵操作,這就讓我剛屢清楚的思路陷入了僵局
不過還是讓我找到了支持復(fù)合主鍵的框架
mybatisplus-plus
是不是看起來挺離譜的?啥玩意就plus-plus?別急,讓我們來看看代碼先
注意mybatisplus與mybatisplus-plus的版本兼容性
首先引入jar包
<dependency>
<groupId>com.github.jeffreyning</groupId>
<artifactId>mybatisplus-plus</artifactId>
<version>1.5.1-RELEASE</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.0</version>
</dependency>
PO對象
package com.youxue.model.lesson;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
import com.youxue.sharding.annotation.TableIndex;
import com.youxue.sharding.annotation.TableIndices;
import com.youxue.sharding.model.BaseShardingPo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 用戶觀看視頻時(shí)長
* <p/>
*
* @author dx
* @since 2021/6/22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("UserWatchVideoLog")
@ApiModel(value="UserWatchVideoLogPo對象", description="用戶觀看視頻時(shí)長表")
@TableIndices({
@TableIndex(name = "IX_USERID" ,ddl = "CREATE NONCLUSTERED INDEX [IX_USERID] ON UserWatchVideoLog ( [UserId] DESC)" ),
@TableIndex(name = "IX_LESSONITEMID_USERID" ,ddl = "CREATE NONCLUSTERED INDEX [IX_LESSONITEMID_USERID] ON UserWatchVideoLog (LessonItemId ASC,UserId ASC)" )
})
public class UserWatchVideoLogPo implements Serializable, BaseShardingPo {
@MppMultiId // 復(fù)合主鍵
@TableField("userId")
@ApiModelProperty(value = "用戶id")
private Integer userId;
@TableField("lessonItemId")
@ApiModelProperty(value = "子課程id")
private Integer lessonItemId;
@ApiModelProperty(value = "觀看時(shí)長 單位秒(s)")
@TableField("seconds")
private Integer seconds;
@ApiModelProperty(value = "科目id")
@TableField("subjectId")
private Integer subjectId;
@ApiModelProperty(value = "視頻觀看時(shí)長 單位秒(s)")
@TableField("VideoProgress")
private Integer videoProgress;
@ApiModelProperty(value = "視頻來源 默認(rèn) 0 ")
@TableField("[Resource]")
private Integer resource;
@ApiModelProperty(value = "類型 默認(rèn) 0 ")
@TableField("[Type]")
private Integer type;
@ApiModelProperty(value = "創(chuàng)建時(shí)間")
@TableField("CreateTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;
@ApiModelProperty(value = "修改時(shí)間")
@TableField("UpdateTime")
private LocalDateTime updateTime;
}@MppMultiId 注解即聲明為復(fù)合主鍵,并以@TableField 主鍵 聲明表字段
Service接口
package com.youxue.service.lesson;
import com.github.jeffreyning.mybatisplus.service.IMppService;
import com.youxue.model.lesson.UserWatchVideoLogPo;
/**
* <p>
* 用戶觀看視頻記錄 服務(wù)類
* </p>
*
* @author dx
* @since 2021-06-22
*/
public interface IUserWatchVideoLogService extends IMppService<UserWatchVideoLogPo> {
}
Impl類
package com.youxue.service.lesson.impl;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.youxue.dao.lesson.UserWatchVideoLogMapper;
import com.youxue.model.lesson.UserWatchVideoLogPo;
import com.youxue.service.lesson.IUserWatchVideoLogService;
import org.springframework.stereotype.Service;
/**
* <p>
* 用戶觀看視頻記錄 服務(wù)類實(shí)現(xiàn)類
* </p>
*
* @author dx
* @since 2021/6/22
*/
@Service
public class UserWatchVideoLogServiceImpl extends MppServiceImpl<UserWatchVideoLogMapper, UserWatchVideoLogPo> implements IUserWatchVideoLogService {
}
Mapper接口
package com.youxue.dao.lesson;
import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
import com.youxue.model.lesson.UserWatchVideoLogPo;
/**
* <p>
* 用戶觀看視頻記錄 Mapper 接口
* </p>
*
* @author dx
* @since 2021-06-22
*/
public interface UserWatchVideoLogMapper extends MppBaseMapper<UserWatchVideoLogPo> {
}service 繼承 IMppService ,mapper 繼承 MppBaseMapper,impl 繼承 MppServiceImpl 實(shí)現(xiàn) service
并在啟動類上添加 @EnableMPP 注解
隨后直接在測試用例中運(yùn)行(測試用例中未使用分表):
@Autowired
private IUserWatchVideoLogService userWatchVideoLogService;
@Test
public void testUserWatchVideo() {
UserWatchVideoLogPo userWatchVideoLogPo = new UserWatchVideoLogPo()
.setUserId(6202238)
.setLessonItemId(56303)
.setSeconds(8888)
.setResource(11);
boolean create = userWatchVideoLogService.save(userWatchVideoLogPo);
System.out.println(create);
System.out.println("create result :" + create);
System.out.println("================ create end ==================");
// 斷點(diǎn)01
UserWatchVideoLogPo watchVideoLogPo = userWatchVideoLogService.selectByMultiId(userWatchVideoLogPo);
System.out.println(watchVideoLogPo.toString());
System.out.println("================ retrieve end ==================");
userWatchVideoLogPo.setSeconds(99999);
userWatchVideoLogPo.setResource(22);
// 斷點(diǎn)03
boolean upd = userWatchVideoLogService.updateByMultiId(userWatchVideoLogPo);
System.out.println("upd result :" + upd);
System.out.println("================ update end ==================");
// 斷點(diǎn)03
boolean remove = userWatchVideoLogService.deleteByMultiId(userWatchVideoLogPo);
System.out.println("remove result :" + remove);
System.out.println("================ remove end ==================");
}我在save 方法后每個(gè)方法出都打了斷點(diǎn),下面我們來看看運(yùn)行結(jié)果

可以看到,添加方法打印的SQL與mybatisplus并沒有什么區(qū)別,隨后看一下數(shù)據(jù)庫中的數(shù)據(jù)

是正常的,我們來看一下查詢操作

可以看到,這里的where條件后跟的是兩個(gè)查詢條件,是不是很棒。再看看編輯操作


可以到編輯操作的SQL也是已兩個(gè)條件操作的,數(shù)據(jù)也更新過來了,最后刪除操作:

至此支持復(fù)合組件的CRUD就完成了
而 mybatisplus-plus 作為 mybatisplus 的升級版 新穎的功能肯定不止于此
根據(jù)多個(gè)字段聯(lián)合主鍵增刪改查
原生mybatisplus只支持一個(gè)主鍵,
mpp支持多個(gè)字段聯(lián)合主鍵(復(fù)合主鍵)增刪改查,
mapper需要繼承MppBaseMapper
實(shí)體類中聯(lián)合主鍵的字段需要用@MppMultiId注解修飾
如果需要在service使用多主鍵相關(guān)操作包括saveOrUpdateByMultiId和批量操作updateBatchByMultiId和saveOrUpdateBatchByMultiId,可以直接繼承IMppService接口
優(yōu)化分頁插件實(shí)現(xiàn)在不分頁時(shí)進(jìn)行排序操作
原生mybatisplus分頁與排序是綁定的,mpp優(yōu)化了分頁插件,使用MppPaginationInterceptor插件
在不分頁的情況下支持排序操作
page參數(shù)size設(shè)置為-1可實(shí)現(xiàn)不分頁取全量數(shù)據(jù),同時(shí)設(shè)置OrderItem可以實(shí)現(xiàn)排序自動填充優(yōu)化功能 & 自動掃描Entity類構(gòu)建ResultMap功能
原生mybatisplus只能做%s+1和now兩種填充,mybatisplus-plus在插入或更新時(shí)對指定字段進(jìn)行自定義復(fù)雜sql填充。
需要在實(shí)體類字段上用原生注解@TableField設(shè)置fill=FieldFill.INSERT fill=FieldFill.UPDATE或fill=FieldFill.INSERT_UPDATE否則不會觸發(fā)自定義填充
mybatisplus-plus使用@InsertFill注解觸發(fā)插入時(shí),執(zhí)行注解中自定義的sql填充實(shí)體類字段
mybatisplus-plus使用@UpdateFill注解觸發(fā)更新時(shí),執(zhí)行注解中自定義的sql填充實(shí)體類字段
還可以自動填充主鍵字段,解決原生mybatisplus不支持多個(gè)主鍵的問題
使用ColNameUtil.pn靜態(tài)方法,獲取實(shí)體類中讀取方法對應(yīng)的列名稱
mybatisplus-plus更多詳細(xì)用法【碼云倉庫地址】
到此這篇關(guān)于mybatisplus 復(fù)合主鍵(多主鍵) CRUD的文章就介紹到這了,更多相關(guān)mybatisplus 復(fù)合主鍵內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot項(xiàng)目快速構(gòu)建的全步驟
這篇文章主要給大家介紹了關(guān)于spring boot項(xiàng)目快速構(gòu)建的全步驟,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Java下SpringBoot創(chuàng)建定時(shí)任務(wù)詳解
這篇文章主要介紹了Java下SpringBoot創(chuàng)建定時(shí)任務(wù)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
mybatis-plus QueryWrapper and or 連用并且實(shí)現(xiàn)分
這篇文章主要介紹了mybatis-plus QueryWrapper and or 連用并且實(shí)現(xiàn)分頁,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Spring Security和Shiro的相同點(diǎn)與不同點(diǎn)整理
在本篇文章里小編給大家整理的是關(guān)于Spring Security和Shiro的相同不同點(diǎn)整理,需要的朋友們可以參考下。2020-02-02
Java中LambdaQueryWrapper設(shè)置自定義排序代碼示例
這篇文章主要給大家介紹了關(guān)于Java中LambdaQueryWrapper設(shè)置自定義排序的相關(guān)資料,lambdaquerywrapper是MyBatis-Plus框架中的一個(gè)查詢條件構(gòu)造器,它可以用于構(gòu)建自定義的查詢條件,需要的朋友可以參考下2023-12-12
spring cloud學(xué)習(xí)入門之config配置教程
這篇文章主要給大家介紹了關(guān)于spring cloud學(xué)習(xí)入門之config配置的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring cloud具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09

