mybatisplus?復(fù)合主鍵(多主鍵)?CRUD示例詳解
mybatisplus 復(fù)合主鍵CRUD
需求描述
最近接到個(gè)挺有意思的需求,做用戶觀看學(xué)習(xí)視頻時(shí)長(zhǎng)的一個(gè)數(shù)據(jù)埋點(diǎn)
儲(chǔ)存用戶觀看視頻時(shí)長(zhǎng)、記錄的接口的調(diào)用肯定會(huì)特別頻繁,因?yàn)槊块g隔指定時(shí)間每個(gè)用戶都會(huì)調(diào)用,如果在這個(gè)接口里直接操作數(shù)據(jù)庫(kù)將會(huì)給我們的數(shù)據(jù)庫(kù)帶來(lái)一定的壓力,在我的代碼中是不允許的,而我是這樣完成這個(gè)需求的:
首先將用戶觀看視頻的時(shí)長(zhǎng)、記錄存儲(chǔ)到阿里云的日志庫(kù)里,隨后以定時(shí)器從阿里云的日志庫(kù)中拉取用戶觀看視頻的數(shù)據(jù)同步到我們的數(shù)據(jù)庫(kù)中。
而就是最后這一步,同步數(shù)據(jù)到數(shù)據(jù)庫(kù)中,這里的數(shù)據(jù)量肯定是龐大的,所以我做了分表。
但是盡管做了分表數(shù)據(jù)量也不少,如果通過自增的主鍵id去編輯數(shù)據(jù)那么我在更新數(shù)據(jù)之前都要先從數(shù)據(jù)庫(kù)中查詢一次,然后在更新
在數(shù)據(jù)量大的情況下依然會(huì)給我們數(shù)據(jù)庫(kù)造成不少壓力,且這個(gè)定時(shí)器的執(zhí)行時(shí)長(zhǎng)將會(huì)拉大,這是我不能接受的
所以直接使用復(fù)合主鍵,以視頻id+用戶id去批量更新數(shù)據(jù),這樣就會(huì)快很多,然而mybatisplus卻僅支持單一主鍵操作,這就讓我剛屢清楚的思路陷入了僵局
不過還是讓我找到了支持復(fù)合主鍵的框架
mybatisplus-plus
是不是看起來(lái)挺離譜的?啥玩意就plus-plus?別急,讓我們來(lái)看看代碼先
注意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對(duì)象
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í)長(zhǎng) * <p/> * * @author dx * @since 2021/6/22 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("UserWatchVideoLog") @ApiModel(value="UserWatchVideoLogPo對(duì)象", description="用戶觀看視頻時(shí)長(zhǎng)表") @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í)長(zhǎng) 單位秒(s)") @TableField("seconds") private Integer seconds; @ApiModelProperty(value = "科目id") @TableField("subjectId") private Integer subjectId; @ApiModelProperty(value = "視頻觀看時(shí)長(zhǎng) 單位秒(s)") @TableField("VideoProgress") private Integer videoProgress; @ApiModelProperty(value = "視頻來(lái)源 默認(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
并在啟動(dòng)類上添加 @EnableMPP 注解
隨后直接在測(cè)試用例中運(yùn)行(測(cè)試用例中未使用分表):
@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),下面我們來(lái)看看運(yùn)行結(jié)果
可以看到,添加方法打印的SQL與mybatisplus并沒有什么區(qū)別,隨后看一下數(shù)據(jù)庫(kù)中的數(shù)據(jù)
是正常的,我們來(lái)看一下查詢操作
可以看到,這里的where條件后跟的是兩個(gè)查詢條件,是不是很棒。再看看編輯操作
可以到編輯操作的SQL也是已兩個(gè)條件操作的,數(shù)據(jù)也更新過來(lái)了,最后刪除操作:
至此支持復(fù)合組件的CRUD就完成了
而 mybatisplus-plus 作為 mybatisplus 的升級(jí)版 新穎的功能肯定不止于此
根據(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)化分頁(yè)插件實(shí)現(xiàn)在不分頁(yè)時(shí)進(jìn)行排序操作
原生mybatisplus分頁(yè)與排序是綁定的,mpp優(yōu)化了分頁(yè)插件,使用MppPaginationInterceptor插件
在不分頁(yè)的情況下支持排序操作
page參數(shù)size設(shè)置為-1可實(shí)現(xiàn)不分頁(yè)取全量數(shù)據(jù),同時(shí)設(shè)置OrderItem可以實(shí)現(xiàn)排序自動(dòng)填充優(yōu)化功能 & 自動(dòng)掃描Entity類構(gòu)建ResultMap功能
原生mybatisplus只能做%s+1和now兩種填充,mybatisplus-plus在插入或更新時(shí)對(duì)指定字段進(jìn)行自定義復(fù)雜sql填充。
需要在實(shí)體類字段上用原生注解@TableField設(shè)置fill=FieldFill.INSERT fill=FieldFill.UPDATE或fill=FieldFill.INSERT_UPDATE否則不會(huì)觸發(fā)自定義填充
mybatisplus-plus使用@InsertFill注解觸發(fā)插入時(shí),執(zhí)行注解中自定義的sql填充實(shí)體類字段
mybatisplus-plus使用@UpdateFill注解觸發(fā)更新時(shí),執(zhí)行注解中自定義的sql填充實(shí)體類字段
還可以自動(dòng)填充主鍵字段,解決原生mybatisplus不支持多個(gè)主鍵的問題
使用ColNameUtil.pn靜態(tài)方法,獲取實(shí)體類中讀取方法對(duì)應(yīng)的列名稱
mybatisplus-plus更多詳細(xì)用法【碼云倉(cāng)庫(kù)地址】
到此這篇關(guān)于mybatisplus 復(fù)合主鍵(多主鍵) CRUD的文章就介紹到這了,更多相關(guān)mybatisplus 復(fù)合主鍵內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot項(xiàng)目快速構(gòu)建的全步驟
這篇文章主要給大家介紹了關(guān)于spring boot項(xiàng)目快速構(gòu)建的全步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Java下SpringBoot創(chuàng)建定時(shí)任務(wù)詳解
這篇文章主要介紹了Java下SpringBoot創(chuàng)建定時(shí)任務(wù)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07mybatis-plus QueryWrapper and or 連用并且實(shí)現(xiàn)分
這篇文章主要介紹了mybatis-plus QueryWrapper and or 連用并且實(shí)現(xiàn)分頁(yè),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Spring Security和Shiro的相同點(diǎn)與不同點(diǎn)整理
在本篇文章里小編給大家整理的是關(guān)于Spring Security和Shiro的相同不同點(diǎn)整理,需要的朋友們可以參考下。2020-02-02Java中LambdaQueryWrapper設(shè)置自定義排序代碼示例
這篇文章主要給大家介紹了關(guān)于Java中LambdaQueryWrapper設(shè)置自定義排序的相關(guān)資料,lambdaquerywrapper是MyBatis-Plus框架中的一個(gè)查詢條件構(gòu)造器,它可以用于構(gòu)建自定義的查詢條件,需要的朋友可以參考下2023-12-12spring cloud學(xué)習(xí)入門之config配置教程
這篇文章主要給大家介紹了關(guān)于spring cloud學(xué)習(xí)入門之config配置的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring cloud具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09