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

目录
  • mybatisplus复合主键CRUD
    • 需求描述
  • mybatisplus-plus

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 方法后每个方法出都打了断点,下面我们来看看运行结果

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

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

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

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

至此支持复合组件的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 复合主键内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MybatisPlus中插入数据后获取该对象主键值的实现

    实体对象 主键IdType要设置为AUTO 表示数据库ID自增 @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class Employee implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) priv

  • Springboot+MybatisPlus+Oracle实现主键自增的示例代码

    上周周一,本来刚过完周末,高高兴兴,老大突然安排了个活,要在一天内把项目的MySQL数据库换成Oracle数据库,我们都知道这是不可能完成的任务,但是,秉承着"没有困难的工作,只有不努力的打工人"的精神,我们马上投入了工作,第一步当然是先配置数据库.oracle建表,这个解决调试了一上午,然后下午卡到oracle主键了,所有人网上找方法,一直到第二天凌晨3点半都还没解决,网上方法很多,试了好多都不管用,终于第二天才找到了满足的方法. 废话不多说,下面贴出. application.ym

  • MybatisPlus,无XML分分钟实现CRUD操作

    不讲太多理论知识,官网都有,直接上手. 1.测试表 DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) UNSIGNED NOT NULL, `name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户名', `password` varchar(18) CHARACTER SET utf8 COLLA

  • Mybatisplus主键生成策略算法解析

    mybatisplus支持多种主键生成策略,默认采用认 ID_WORKER 即雪花算法 雪花算法 snowflflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID.其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0.可以保证几乎全球唯一! mybatisplus默认主键生成策略有可能会和我们的数据库

  • Flutter的键值存储数据库使用示例详解

    目录 Flutter 键值存储数据库 unqlite unqlite_flutter 快速上手 简单键值对存储 JSON 为什么你应该使用unqlite_flutter? Flutter 键值存储数据库 键值存储是开发中十分常见的需求,在Flutter开发中,一般使用 shared_preferences 插件来实现.shared_preferences 本质上就是将键值对保存到一个XML文件中进行持久化. 而shared_preferences 实际上存在一定缺陷,譬如其性能较差,不适合处理大

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

    目录 mybatisplus复合主键CRUD 需求描述 mybatisplus-plus mybatisplus 复合主键CRUD 需求描述 最近接到个挺有意思的需求,做用户观看学习视频时长的一个数据埋点 储存用户观看视频时长.记录的接口的调用肯定会特别频繁,因为每间隔指定时间每个用户都会调用,如果在这个接口里直接操作数据库将会给我们的数据库带来一定的压力,在我的代码中是不允许的,而我是这样完成这个需求的: 首先将用户观看视频的时长.记录存储到阿里云的日志库里,随后以定时器从阿里云的日志库中拉取

  • PHP中替换键名的简易方法示例详解

    YII框架中封装好了的数据库操作函数,默认输出的时候,将数据库字段名作为数组的键名进行输出,但是有些时候带有键名的数据不能够满足未知情况下的操作,譬如:数据库数据导出为EXCEL等比较非正常的操作. 所以这边需要对数据库结果集进行解析,下面就是针对这种特殊情况的一个简单方法: 复制代码 代码如下: /** * @todo 针对YII 查询输出带有数据库表字段名键名进行优化EXCEL表格输出 * @todo 替换键名为0.1.2... * @param array $data * @return

  • springboot+mybatis-plus实现内置的CRUD使用详解

    springboot+mybatis-plus实现内置的CRUD使用详情,具体修改删除操作内容后文也有详细说明 mybatis-plus的特性 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作 强大的 CRUD操作:内置通用 Mapper.通用Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 支持 Lambda形式调用:通过 Lambda 表达式,方

  • Python教程通过公共键对不同字典进行排序示例详解

    利用 operator 模块中的 itemgetter 函数对这类结构进行排序. rows = [ {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'Big', 'lname': 'Jones', 'uid': 100

  • MyBatis-Plus动态返回实体类示例详解

    目录 1. 自定义SqlSession 2. 自定义SqlSessionFactory 3. 自定义SqlSessionTemplate 4. 自定义基础Mapper 5. 使用 1. 自定义SqlSession @Slf4j public class GenericSqlSession extends DefaultSqlSession { private static final ThreadLocal<Class<?>> CTX = new ThreadLocal<&g

  • MongoDB分片键的选择和案例实例详解

    前言 分片键选择不好,应用程序就无法利用分片集群所提供的诸多优势.在这种情况下,插入和查询的性能都会显著下降.下决定时一定要严肃,一旦选择了分片键,就必须坚持选择,分片键是不可以修改的.要让分片键提供好的体验,部分源自了解怎样才算一个好的分片键. 本文将详细介绍关于MongoDB分片键的选择和案例,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. MongoDB版本:3.6 一.分片键类别 1.升序片键 升序片键例如:日期时间字段.自增字段. 2.随机分发片键 随机分发片键例如:

  • Redis中过期键如何删除示例详解

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • MySQL中主键为0与主键自排约束的关系详解(细节)

    前言 本文主要介绍了关于MySQL主键为0与主键自排约束的关系,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 开始不设置主键表的设计如下: 如果id的位置有好几个0的话:设置主键并且自动排序时,0会从1开始递增: Insert 进去 id = 0的数据,数据会从实际的行数开始增加,和从0变化不一样: 现在主键是没有0的,如果把某个id改成0的话,0不会变!直接会进行排序: 再insert一个id=0的看看,居然还是跟刚才一样直接跟行数相关! 再重置一下自动排序,看看这个0会不

  • Redis中键值过期操作示例详解

    1.过期设置 Redis 中设置过期时间主要通过以下四种方式: expire key seconds:设置 key 在 n 秒后过期: pexpire key milliseconds:设置 key 在 n 毫秒后过期: expireat key timestamp:设置 key 在某个时间戳(精确到秒)之后过期: pexpireat key millisecondsTimestamp:设置 key 在某个时间戳(精确到毫秒)之后过期: 下面分别来看以上这些命令的具体实现. 1)expire:N

随机推荐