SpringBoot整合MongoDB JPA使用

一、整合MongoDB

SpringDataMongoDB是 SpringData家族成员之一,MongoDB的持久层框架,底层封装了 mongodb-driver。mongodb-driver 是 MongoDB官方推出的 Java连接 MongoDB的驱动包,相当于JDBC驱动。

SpringBoot整合 MongoDB,引入 SpringDataMongoDB依赖。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

1、配置MongoDB信息

在 application.yml文件中,添加 MongoDB连接信息。

spring:
  data:
    mongodb:
      # 超级管理员是必须指定?后面的固定写法
      uri: mongodb://用户名:密码@127.0.0.1:27017/ws_sb_local?authSource=admin&authMechanism=SCRAM-SHA-1
      # 普通用户不能指定?后面的固定写法
      # uri: mongodb://luna1:密码@127.0.0.1:27017/ws_sb_local

然后启动项目就可以使用 MongoTemplate或者 MongoDB JPA框架(SpringDataMongoDB)。

2、JPA查询规范

使用 JPA查询时,我们要遵循一定的规范。

示例如下:

在这里插入图片描述

3、JPA注解

JPA常用注解如下:

@Id
用于字段级别,标记这个字段是一个主键,默认生成的名称是“_id”。

@Document
用于类,以表示这个类需要映射到数据库,您也可以指定映射到数据库的集合名称。

@Field
用于字段,并描述字段的名称,因为它将在MongoDB BSON文档中表示,允许名称与该类的字段名不同。

@CreatedDate
用途: 该注解用于标记一个字段,表示这个字段将自动保存文档首次创建时的日期和时间。
行为: 当一个新的文档被插入到MongoDB集合中时,标记了@CreatedDate的字段会被自动填充为当前的日期和时间。
数据类型: 通常该字段应为java.util.Date、java.time.LocalDateTime或java.time.Instant等日期时间类型。

@LastModifiedDate
用途: 这个注解用于标记一个字段,以记录文档最后一次被修改的日期和时间。
行为: 每当文档被更新时,这个字段会自动更新为当前的日期和时间。
数据类型: 类似于@CreatedDate,它也支持java.util.Date、java.time.LocalDateTime或java.time.Instant等类型。

@Indexed
用于字段,表示该字段需要如何创建索引。

@CompoundIndex
用于类,以声明复合索引。

@GeoSpatialIndexed
用于字段,进行地理位置索引 。

@DBRef
用于字段,以表示它将使用com.mongodb.DBRef进行存储。

@TextIndexed
用于字段,标记该字段要包含在文本索引中。

@Language
用于字段,以设置文本索引的语言覆盖属性。

@Transient
默认情况下,所有私有字段都映射到文档,此注解将会去除此字段的映射

@PersistenceConstructor
标记一个给定的构造函数,即使是一个protected修饰的,在从数据库实例化对象时使用。构造函数参数通过名称映射到检索的DBObject中的键值。

@Value
这个注解是 Spring框架的一部分。在映射框架内,它可以应用于构造函数参数。这允许您使用Spring表达式语言语句来转换在数据库中检索的键值,然后再用它来构造一个域对象。为了引用给定文档的属性,必须使用以下表达式:@Value(“#root.myProperty”),root要指向给定文档的根。

@Version
用于字段锁定,保存操作时检查修改。初始值是0,每次更新时自动触发。

二、MongoDB JPA使用

这里直接上代码。

1、DO类

/**
 * 设备Gps信息
 */
@Data
@Document("device_gps")
public class DeviceGpsDO implements Serializable {
    private static final long serialVersionUID = -9179758476973350170L;

    /**
     * 主键
     */
    @Id
    private String id;

    /**
     * 创建时间
     */
    @CreatedDate
    @Field("create_time")
    private LocalDateTime createTime;

    /**
     * 修改时间
     */
    @LastModifiedDate
    @Field("update_time")
    private LocalDateTime updateTime;

    /**
     * 设备名称
     */
    @Field("device_name")
    @Indexed
    private String deviceName;

    /**
     * 设备编号
     */
    @Field("device_no")
    private String deviceNo;

    /**
     * 设备识别VIN码
     */
    @Field("vin_no")
    private String vinNo;

    /**
     * 预警状态
     */
    @Field("warn_status")
    private Integer warnStatus;

    /**
     * 速度(千米/小时)
     */
    @Field("speed")
    private Float speed;

    /**
     * 海拔高度
     */
    @Field("altitude")
    private Double altitude;

    /**
     * 卫星定位时间
     */
    @Field("satellite_time")
    private Date satelliteTime;

    /**
     * 地理位置,用于LBS搜索
     */
    @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
    private GeoJsonPoint location;

    /**
     * 经度
     */
    @Field("longitude")
    private Double longitude;

    /**
     * 纬度
     */
    @Field("latitude")
    private Double latitude;

}

2、Dao类

/**
 * 设备Gps信息
 * 继承 MongoRepository,指定实体和主键的类型作为泛型
 */
public interface DeviceGpsDao extends MongoRepository<DeviceGpsDO, String> {

    /**
     * JPA语法查询根据速度大于等于和预警状态查询
     *
     * @param speed      速度
     * @param warnStatus 预警状态
     * @return
     */
    List<DeviceGpsDO> findBySpeedGreaterThanEqualAndWarnStatusEquals(Float speed, Integer warnStatus);

    /**
     * @Query注解查询根据速度大于等于和预警状态查询
     *
     * @param speed      速度
     * @param warnStatus 预警状态
     * @return
     */
    @Query("{ speed: {$gte : ?0}, warn_status: ?1  }")
    List<DeviceGpsDO> getBySpeedAndWarnStatus(Float speed, Integer warnStatus);

}

3、Service类

(1)接口

public interface DeviceGpsService {

    /**
     * 分页查询
     *
     * @param pageQueryRequest
     * @return
     */
    BasePageResult pageQuery(DeviceGpsPageQueryRequest pageQueryRequest);

    /**
     * 新增
     *
     * @param deviceGpsDO
     */
    BaseOperateResult insert(DeviceGpsDO deviceGpsDO);

    /**
     * 新增或者修改
     *
     * @param deviceGpsDO
     */
    BaseOperateResult insertOrUpdate(DeviceGpsDO deviceGpsDO);

    /**
     * 根据 id删除
     *
     * @param id
     */
    BaseOperateResult deleteById(String id);

    /**
     * 根据 id查询
     *
     * @param id
     * @return
     */
    DeviceGpsDO findById(String id);

    /**
     * 查询所有
     *
     * @return
     */
    List<DeviceGpsDO> findAll();

    /**
     * JPA语法查询根据速度大于等于和预警状态查询
     *
     * @param speed      速度
     * @param warnStatus 预警状态
     * @return
     */
    List<DeviceGpsDO> findBySpeedGreaterThanEqualAndWarnStatusEquals(Float speed, Integer warnStatus);

    /**
     * @Query注解查询根据速度大于等于和预警状态查询
     *
     * @param speed      速度
     * @param warnStatus 预警状态
     * @return
     */
    List<DeviceGpsDO> getBySpeedAndWarnStatus(Float speed, Integer warnStatus);


}

(2)实现类

@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceGpsServiceImpl implements DeviceGpsService {

    private final DeviceGpsDao deviceGpsDao;

    @Override
    public BasePageResult pageQuery(DeviceGpsPageQueryRequest pageQueryRequest) {
        BasePageResult pageResult = new BasePageResult();

        String deviceName = pageQueryRequest.getDeviceName();
        Integer warnStatus = pageQueryRequest.getWarnStatus();
        // 创建查询条件的实例
        DeviceGpsDO queryDO = new DeviceGpsDO();
        if (StringUtils.isNoneBlank(deviceName)) {
            queryDO.setDeviceName(deviceName);
        }
        if (warnStatus != null) {
            queryDO.setWarnStatus(warnStatus);
        }

        // 设置ExampleMatcher来定义匹配规则
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withIgnoreCase() // 忽略大小写
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING); // 包含

        // 使用条件对象和匹配器创建Example实例
        Example<DeviceGpsDO> example = Example.of(queryDO, matcher);

        // 构造分页对象, 从 0 开始
        int currentPage = pageQueryRequest.getCurrentPage().intValue();
        int pageSize = pageQueryRequest.getPageSize().intValue();
        PageRequest pageRequest = PageRequest.of(currentPage - 1, pageSize);
        // 分页查询
        /**
         * Example 这种方式更适合于属性匹配(如字符串的模糊匹配)。对于比较运算(如大于、小于等),使用Spring Data MongoDB提供的Criteria和Query对象来构建更复杂的查询条件。
         */
        Page<DeviceGpsDO> doPage = deviceGpsDao.findAll(example, pageRequest);

        log.info("doPage.getNumber() = {}", doPage.getNumber());
        log.info("doPage.getNumberOfElements() = {}", doPage.getNumberOfElements());
        log.info("doPage.getSize() = {}", doPage.getSize());
        log.info("doPage.getTotalElements() = {}", doPage.getTotalElements());
        log.info("doPage.getTotalPages() = {}", doPage.getTotalPages());

        pageResult.setPaginator(new Paginator(doPage.getNumber() + 1, doPage.getSize(), doPage.getTotalElements(), doPage.getTotalPages()));
        pageResult.setPageList(doPage.getContent());
        pageResult.setSuccess(true);
        return pageResult;
    }

    @Override
    public BaseOperateResult insert(DeviceGpsDO deviceGpsDO) {
        BaseOperateResult operateResult = new BaseOperateResult();
        // 新增
        Double longitude = deviceGpsDO.getLongitude();
        Double latitude = deviceGpsDO.getLatitude();
        if (longitude != null || latitude != null) {
            // 经纬度 GeoJsonPoint
            deviceGpsDO.setLocation(new GeoJsonPoint(longitude, latitude));
        }
        deviceGpsDao.insert(deviceGpsDO);

        operateResult.setSuccess(Boolean.TRUE);
        return operateResult;
    }

    @Override
    public BaseOperateResult insertOrUpdate(DeviceGpsDO deviceGpsDO) {
        BaseOperateResult operateResult = new BaseOperateResult();
        // 新增或者修改
        Double longitude = deviceGpsDO.getLongitude();
        Double latitude = deviceGpsDO.getLatitude();
        if (longitude != null || latitude != null) {
            // 经纬度 GeoJsonPoint
            deviceGpsDO.setLocation(new GeoJsonPoint(longitude, latitude));
        }
        // MongoDB会清除字段为 null的字段。
        deviceGpsDao.save(deviceGpsDO);

        operateResult.setSuccess(Boolean.TRUE);
        return operateResult;
    }

    @Override
    public BaseOperateResult deleteById(String id) {
        BaseOperateResult operateResult = new BaseOperateResult();
        deviceGpsDao.deleteById(id);

        operateResult.setSuccess(Boolean.TRUE);
        return operateResult;
    }

    @Override
    public DeviceGpsDO findById(String id) {
        return deviceGpsDao.findById(id).get();
    }

    @Override
    public List<DeviceGpsDO> findAll() {
        return deviceGpsDao.findAll();
    }

    @Override
    public List<DeviceGpsDO> findBySpeedGreaterThanEqualAndWarnStatusEquals(Float speed, Integer warnStatus) {
        List<DeviceGpsDO> list = deviceGpsDao.findBySpeedGreaterThanEqualAndWarnStatusEquals(speed, warnStatus);
        return list;
    }

    @Override
    public List<DeviceGpsDO> getBySpeedAndWarnStatus(Float speed, Integer warnStatus) {
        List<DeviceGpsDO> list = deviceGpsDao.getBySpeedAndWarnStatus(speed, warnStatus);
        return list;
    }

}

然后,单元测试调用ok。

4、MongoDB的UTC时差问题

SpringDataMongoDB框架会自动处理 MongoDB的UTC时差问题。我们正常在服务端代码中保存和查询操作即可。但是,对于 Controller接口返回是不一样的,与使用 LocalDateTime和 Date类型有关。

MongoDB数据库记录:
在这里插入图片描述

Controller接口返回DO数据:

在这里插入图片描述

结论:

  • 如果使用 Date类型,接口返回存在 8小时时差,请使用 @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")注解并且必须指定时区解决。
  • 如果使用 LocalDateTime,接口返回不存在时差问题,序列化时也可以使用 @JsonFormat注解。
  • 推荐使用 LocalDateTime

在这里插入图片描述

5、“_class” 字段问题

SpringBoot整合mongodb时,发现添加文档会自动添加一个_class的字段。

在这里插入图片描述

有或者没有_class列,对于我们使用 MongoDB进行任何操作及序列化实体类都没有任何影响。

所以,我们可以使用下面方法,禁用 “_class” 字段。

新建 MongoDB配置类。

//@EnableMongoAuditing   // 开启审计功能
@Configuration
public class MongoDBConfig implements InitializingBean {

    @Resource
    private MappingMongoConverter mappingMongoConverter;

    @Override
    public void afterPropertiesSet() {
        // 禁用 "_class" 字段
        mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
    }

}

6、开启审计功能

使用 @EnableMongoAuditing注解开启审计功能。

我们在启动类或者 上面配置类上添加它之后,@CreatedDate和 @LastModifiedDate注解就会生效了。

注意:@CreatedDate:id为空时生效。

三、@Query注解使用

在 Dao类中,我们可以通过使用 @Query 注解手写查询语句。

1、简单参数传参

我们需要使用 ?数字占位符 表达式来取出参数中指定位置的值,占位符从 0开始。

    @Query("{ speed: {$gte : ?0}, warn_status: ?1  }")
    List<DeviceGpsDO> getBySpeedAndWarnStatus(Float speed, Integer warnStatus);

2、对象参数传参

我们需要使用 SpEL 表达式,格式:?#{[]}

括号中使用 [下标] 来取出指定位置的参数,比如:?#{[0]} 则取出第一个参数。之后直接取出参数中的指定属性即可,比如如 ?#{[1].age} 就是取出第二个参数的age属性。

    @Query("{ speed: {$gte : ?#{[0].speed}}, warn_status: ?#{[0].warnStatus} }")
    List<DeviceGpsDO> getBySpeedAndWarnStatus2(DeviceGpsDO deviceGpsDO);

3、投影查询

如果我们只想获取指定的字段,可以使用 @Query注解的 fields 来进行投影查询。

    @Query(value= "{ speed: {$gte : ?#{[0].speed}}, warn_status: ?#{[0].warnStatus} }",
            fields = "{ speed:1, warn_status:1 }")
    List<DeviceGpsDO> getBySpeedAndWarnStatus3(DeviceGpsDO deviceGpsDO);

测试:

    @Override
    public List<DeviceGpsDO> getBySpeedAndWarnStatus(Float speed, Integer warnStatus) {
        List<DeviceGpsDO> list = deviceGpsDao.getBySpeedAndWarnStatus(speed, warnStatus);

        DeviceGpsDO deviceGpsDO = new DeviceGpsDO();
        deviceGpsDO.setSpeed(0.0F);
        deviceGpsDO.setWarnStatus(0);
        List<DeviceGpsDO> list2 = deviceGpsDao.getBySpeedAndWarnStatus2(deviceGpsDO);
        List<DeviceGpsDO> list3 = deviceGpsDao.getBySpeedAndWarnStatus3(deviceGpsDO);
        log.info("list = {} ", list);
        log.info("list2 = {} ", list2);
        log.info("list3 = {} ", list3);
        return list;
    }

在这里插入图片描述

总结:

  • 对于保存、修改、删除等操作,JPA语法使用非常友好,推荐使用
  • 对于动态条件查询,JPA语法与@Query注解处理不够友好,不推荐使用。
  • 对于动态条件查询,推荐使用 Criteria对象构建动态参数,然后使用 mongoTemplate查询。
  • 项目中,通常会 JPA语法与 mongoTemplate两者结合使用。

– 求知若饥,虚心若愚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/759216.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】数据库——备份与恢复,日志管理1

一、数据备份的重要性 1.备份的主要目的是灾难恢复 在生产环境中&#xff0c;数据的安全性至关重要 任何数据的丢失都可能产生严重的后果造成数据丢失的原因&#xff1a; 程序错误人为,操作错误运算错误磁盘故障灾难&#xff08;如火灾、地震&#xff09;和盗窃 2.数据库备份…

pcap包常见拆分方法

文章目录 Wireshark 拆分流量包SplitCap使用简介魔数报错示例结果 在进行流量分析时&#xff0c;经常需要分析pcap流量包。但是体积过大的流量包不容易直接分析&#xff0c;经常需要按照一定的规则把它拆分成小的数据包。 这里统一选择cic数据集里的Thursday-WorkingHours.pcap…

【Oracle篇】逻辑备份工具expdp(exp)/impdp(imp)和物理备份工具rman的区别和各自的使用场景总汇(第八篇,总共八篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

基于局域网下的服务器连接、文件传输以及内网穿透教程 | 服务器连接ssh | 服务器文件传输scp | 内网穿透frp | 研究生入学必备 | 深度学习必备

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;本篇博客分享的是基于局域网下的服务器连接&#x1f517;、文件传输以及内网穿透教程&#xff0c;内容非常完备✨&#xff0c;涵盖了在服务器上做深度学…

目标检测常用涨点方法:注意力机制小结(空间注意力、通道注意力、CBAM等)

1.通道注意力 通道注意力&#xff08;Channel Attention&#xff09;是在通道维度上对输入数据进行学习&#xff0c;再对不同的通道分配相应的权重表示重要性&#xff0c;从而达到“分配注意力”的效果。SENet&#xff08;Squeeze and Excitation networks) 是一个典型的使用通…

J020_二分查找算法

一、查找过程 使用二分查找算法有一个必要的前提&#xff1a;数组已经是一个排好序的数组。 以下面数组为例&#xff0c;讲述二分查找过程&#xff1a; 二、代码实现 package com.itheima.sort;public class BinarySearch {public static void main(String[] args) {int[] a…

STM32第十三课:DMA多通道采集光照烟雾

文章目录 需求一、DMA&#xff08;直接存储器存取&#xff09;二、实现流程1.时钟使能2.设置外设寄存器地址3.设置存储器地址4.设置要传输的数据量5.设置通道优先级6.设置传输方向7.使通道和ADC转换 三、数据处理四、需求实现总结 需求 通过DMA实现光照强度和烟雾浓度的多通道…

VQVAE:Neural Discrete Representation Learning

论文名称&#xff1a;Neural Discrete Representation Learning 开源地址 发表时间&#xff1a;NIPS2017 作者及组织&#xff1a;Aaron van den Oord,Oriol Vinyals和Koray Kavukcuoglu, 来自DeepMind。 1、VAE 简单回顾下VAE的损失函数&#xff0c;ELBO的下界为&#xff1a; …

单晶层状氧化物制作方法技术资料 纳离子技术

网盘 https://pan.baidu.com/s/1hjHsXvTXG74-0fDo5TtXWQ?pwd10jk 单晶型高熵普鲁士蓝正极材料及其制备方法与应用.pdf 厘米级铬氧化物单晶及其制备方法和存储器件.pdf 多孔氧化物单晶材料及其制备方法和应用.pdf 大单晶层状氧化物正极材料及其制备方法和应用.pdf 富钠P2相层状…

3DMAX折纸插件FoldPoly使用方法详解

3DMAX折纸插件FoldPoly使用教程 3DMAX折纸插件FoldPoly&#xff0c;用于挤出可编辑多边形的边&#xff08;边界&#xff09;并可旋转&#xff08;折叠&#xff09;新生成的面&#xff0c;创建类似手工折纸以及纸箱包装盒的建模效果。 【版本要求】 3dMax2014 - 2025&#xff…

线性代数|机器学习-P20鞍点和极值

文章目录 1 . 瑞利商的思考1.1 瑞利商的定义1.2 投影向量 2. 拉格朗日乘子法3. 鞍点 1 . 瑞利商的思考 1.1 瑞利商的定义 假设A是n阶实对称矩阵&#xff0c;x是n维非零列向量&#xff0c;那么瑞利商表示如下&#xff1a; R ( A , x ) x T A x x T x \begin{equation} R(A,x…

嵌入式学习——硬件(ARM内核汇编指令)——day52

ARM汇编指令 学习arm汇编的主要目的是为了编写arm启动代码&#xff0c;启动代码启动以后&#xff0c;引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有&#xff1a; 初始化异常向量表&#xff1b;初…

前端学习 Vue 插槽如何实现组件内容分发?

目录 一、Vue.js框架介绍二、什么是Vue 插槽三、Vue 插槽的应用场景四、Vue 插槽如何实现组件内容分发 一、Vue.js框架介绍 Vue.js是一个用于构建用户界面的渐进式JavaScript框架。它设计得非常灵活&#xff0c;可以轻松地被集成到现有的项目中&#xff0c;也可以作为一个完整…

Redis-实战篇-缓存击穿问题及解决方案

文章目录 1、缓存击穿2、常见的解决方案有两种&#xff1a;2.1、互斥锁2.2、逻辑过期2.3、两种方案对比 3、利用互斥锁解决缓存击穿问题3.1、ShopServiceImpl.java3.2、使用 jmeter.bat 测试高并发 4、利用逻辑过期解决缓存击穿问题 1、缓存击穿 缓存击穿问题 也叫 热点key问题…

半个月从几十升粉到500(发红包喽)

目录 1. 背景2. 涨粉秘籍2.1 持续创作高质量内容2.1.1 保持频率2.1.2 技术文章为主2.1.3 图文并茂 2.2 积极参与社区活动2.2.1 社区分享2.2.2 发文活动 2.3 互动与建立信任2.3.1 与读者互动2.3.2 红包互动2.3.3 动态分享 2.4 标题与内容的优化2.4.1 标题吸引2.4.2 内容实用 2.5…

【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【从零开始学架构 架构基础】五 架构设计的复杂度来源:低成本、安全、规模

架构设计的复杂度来源其实就是架构设计要解决的问题&#xff0c;主要有如下几个&#xff1a;高性能、高可用、可扩展、低成本、安全、规模。复杂度的关键&#xff0c;就是新旧技术之间不是完全的替代关系&#xff0c;有交叉&#xff0c;有各自的特点&#xff0c;所以才需要具体…

解决idea中git无法管理项目中所有需要管理的文件

点击文件->设置 选择版本控制—>目录映射 点击加号 设置整个项目被Git管理

springboot助农电商系统-计算机毕业设计源码08655

摘要 近年来&#xff0c;电子商务的快速发展引起了行业和学术界的高度关注。基于移动端的助农电商系统旨在为用户提供一个简单、高效、便捷的农产品购物体验&#xff0c;它不仅要求用户清晰地查看所需信息&#xff0c;而且还要求界面设计精美&#xff0c;使得功能与页面完美融…

【GPU虚拟化到池化技术深度分析 2024】

文末有福利&#xff01; 随着大模型的兴起&#xff0c;对GPU算力的需求越来越多&#xff0c;而当前现实情况使企业往往受限于有限的GPU卡资源&#xff0c;即便进行了虚拟化&#xff0c;往往也难以充分使用GPU卡资源或持续使用资源。为解决GPU算力资源不均衡等问题&#xff0c;…