mybatisplus?复合主键(多主键)?CRUD示例详解

 更新时间:2022年03月07日 16:47:10   作者:叶儿飞飞  
这篇文章主要介绍了mybatisplus?复合主键(多主键)?CRUD实例详解,本文通过实例代码图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

mybatisplus 复合主键CRUD

需求描述

最近接到个挺有意思的需求,做用户观看学习视频时长的一个数据埋点

储存用户观看视频时长、记录的接口的调用肯定会特别频繁,因为每间隔指定时间每个用户都会调用,如果在这个接口里直接操作数据库将会给我们的数据库带来一定的压力,在我的代码中是不允许的,而我是这样完成这个需求的:

首先将用户观看视频的时长、记录存储到阿里云的日志库里,随后以定时器从阿里云的日志库中拉取用户观看视频的数据同步到我们的数据库中。

而就是最后这一步,同步数据到数据库中,这里的数据量肯定是庞大的,所以我做了分表。

但是尽管做了分表数据量也不少,如果通过自增的主键id去编辑数据那么我在更新数据之前都要先从数据库中查询一次,然后在更新

在数据量大的情况下依然会给我们数据库造成不少压力,且这个定时器的执行时长将会拉大,这是我不能接受的

所以直接使用复合主键,以视频id+用户id去批量更新数据,这样就会快很多,然而mybatisplus却仅支持单一主键操作,这就让我刚屡清楚的思路陷入了僵局

不过还是让我找到了支持复合主键的框架

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>
 *     用户观看视频时长
 * <p/>
 *
 * @author dx
 * @since 2021/6/22
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("UserWatchVideoLog")
@ApiModel(value="UserWatchVideoLogPo对象", description="用户观看视频时长表")
@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 // 复合主键
    @TableField("userId")
    @ApiModelProperty(value = "用户id")
    private Integer userId;
    @TableField("lessonItemId")
    @ApiModelProperty(value = "子课程id")
    private Integer lessonItemId;
    @ApiModelProperty(value = "观看时长 单位秒(s)")
    @TableField("seconds")
    private Integer seconds;
    @ApiModelProperty(value = "科目id")
    @TableField("subjectId")
    private Integer subjectId;
    @ApiModelProperty(value = "视频观看时长  单位秒(s)")
    @TableField("VideoProgress")
    private Integer videoProgress;
    @ApiModelProperty(value = "视频来源 默认 0 ")
    @TableField("[Resource]")
    private Integer resource;
    @ApiModelProperty(value = "类型  默认 0 ")
    @TableField("[Type]")
    private Integer type;
    @ApiModelProperty(value = "创建时间")
    @TableField("CreateTime")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改时间")
    @TableField("UpdateTime")
    private LocalDateTime updateTime;
}

@MppMultiId 注解即声明为复合主键,并以@TableField 主键 声明表字段

Service接口

package com.youxue.service.lesson;

import com.github.jeffreyning.mybatisplus.service.IMppService;
import com.youxue.model.lesson.UserWatchVideoLogPo;

/**
 * <p>
 * 用户观看视频记录 服务类
 * </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>
 * 用户观看视频记录 服务类实现类
 * </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 实现 service

并在启动类上添加 @EnableMPP 注解

随后直接在测试用例中运行(测试用例中未使用分表):

@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 ==================");
		// 断点01
        UserWatchVideoLogPo watchVideoLogPo = userWatchVideoLogService.selectByMultiId(userWatchVideoLogPo);
        System.out.println(watchVideoLogPo.toString());
        System.out.println("================ retrieve end ==================");
        userWatchVideoLogPo.setSeconds(99999);
        userWatchVideoLogPo.setResource(22);
		
        // 断点03        
        boolean upd = userWatchVideoLogService.updateByMultiId(userWatchVideoLogPo);
        System.out.println("upd result :" + upd);
        System.out.println("================ update end ==================");
        // 断点03
        boolean remove = userWatchVideoLogService.deleteByMultiId(userWatchVideoLogPo);
        System.out.println("remove result :" + remove);
        System.out.println("================ remove end ==================");
}

我在save 方法后每个方法出都打了断点,下面我们来看看运行结果

img

可以看到,添加方法打印的SQL与mybatisplus并没有什么区别,随后看一下数据库中的数据

img

是正常的,我们来看一下查询操作

img

可以看到,这里的where条件后跟的是两个查询条件,是不是很棒。再看看编辑操作

img

img

可以到编辑操作的SQL也是已两个条件操作的,数据也更新过来了,最后删除操作:

img

至此支持复合组件的CRUD就完成了

而 mybatisplus-plus 作为 mybatisplus 的升级版 新颖的功能肯定不止于此

根据多个字段联合主键增删改查 

原生mybatisplus只支持一个主键,

mpp支持多个字段联合主键(复合主键)增删改查,

mapper需要继承MppBaseMapper
实体类中联合主键的字段需要用@MppMultiId注解修饰
如果需要在service使用多主键相关操作包括saveOrUpdateByMultiId和批量操作

updateBatchByMultiId和saveOrUpdateBatchByMultiId,可以直接继承IMppService接口

优化分页插件实现在不分页时进行排序操作

原生mybatisplus分页与排序是绑定的,mpp优化了分页插件,使用MppPaginationInterceptor插件
在不分页的情况下支持排序操作
page参数size设置为-1可实现不分页取全量数据,同时设置OrderItem可以实现排序

自动填充优化功能 & 自动扫描Entity类构建ResultMap功能

原生mybatisplus只能做%s+1和now两种填充,mybatisplus-plus在插入或更新时对指定字段进行自定义复杂sql填充。
需要在实体类字段上用原生注解@TableField设置fill=FieldFill.INSERT fill=FieldFill.UPDATE或fill=FieldFill.INSERT_UPDATE否则不会触发自定义填充
mybatisplus-plus使用@InsertFill注解触发插入时,执行注解中自定义的sql填充实体类字段
mybatisplus-plus使用@UpdateFill注解触发更新时,执行注解中自定义的sql填充实体类字段
还可以自动填充主键字段,解决原生mybatisplus不支持多个主键的问题
使用ColNameUtil.pn静态方法,获取实体类中读取方法对应的列名称

mybatisplus-plus更多详细用法【码云仓库地址】

到此这篇关于mybatisplus 复合主键(多主键) CRUD的文章就介绍到这了,更多相关mybatisplus 复合主键内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • Java编程通过list接口实现数据的增删改查代码示例

    Java编程通过list接口实现数据的增删改查代码示例

    这篇文章是介绍Java编程基础方面的内容,涉及list接口的操作,通过list接口实现对数据的增删改查的相关代码,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • idea推送项目到gitee中的创建方法

    idea推送项目到gitee中的创建方法

    这篇文章主要介绍了idea推送项目到gitee中的创建方法,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • 详解SpringBoot的Run方法

    详解SpringBoot的Run方法

    本文给大家介绍了SpringBoot的Run方法,文中通过实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-10-10
  • Java数据结构专题解析之栈和队列的实现

    Java数据结构专题解析之栈和队列的实现

    从数据结构的定义看,栈和队列也是一种线性表。其不同之处在于栈和队列的相关运算具有特殊性,只是线性表相关运算的一个子集。更准确的说,一般线性表的插入、删除运算不受限制,而栈和队列上的插入删除运算均受某种特殊限制。因此,栈和队列也称作操作受限的线性表
    2021-10-10
  • 详解Java的Exception异常机制

    详解Java的Exception异常机制

    Java的Exception异常机制,为什么会突然聊到异常?其实不是突然,而是我已经准备了很久,但苦于没有好的例子来讲解,从表象到底层实现,今天就带大家详细了解Exception异常,需要的朋友可以参考下
    2021-05-05
  • Java使用Zxing二维码生成的简单示例

    Java使用Zxing二维码生成的简单示例

    ZXing是一个开源的,用Java实现的多种格式的1D/2D条码图像处理库,下面这篇文章主要给大家介绍了关于Java使用Zxing二维码生成的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Java parseInt解释加方法示例

    Java parseInt解释加方法示例

    使用此方法得到的原始数据类型的一个特定的字符串。 parseXxx()是一个静态方法,可以有一个参数或两个
    2013-11-11
  • Spring?this调用当前类方法无法拦截的示例代码

    Spring?this调用当前类方法无法拦截的示例代码

    这篇文章主要介绍了Spring?this调用当前类方法无法拦截,通过debug 查看这个proxyService1 和this的区别,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • 基于HttpServletRequest 相关常用方法的应用

    基于HttpServletRequest 相关常用方法的应用

    本篇文章小编为大家介绍,基于HttpServletRequest 相关常用方法的应用,需要的朋友参考下
    2013-04-04
  • 浅谈java常用的几种线程池比较

    浅谈java常用的几种线程池比较

    下面小编就为大家带来一篇浅谈java常用的几种线程池比较。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01

最新评论

?


http://www.vxiaotou.com