MongoDB基础入门到深入(八)MongoDB整合SpringBoot、Chang Streams

news2024/11/25 1:39:57

文章目录

  • 系列文章索引
  • 十五、MongoDB整合SpringBoot
    • 1、环境准备
    • 2、集合操作
    • 3、文档操作
      • (1)相关注解
      • (2)创建实体
      • (3)添加文档
      • (4)查询文档
      • (5)更新文档
      • (6)删除文档
      • (7)聚合操作
      • (8)小技巧:如何去掉_class属性
    • 4、事务操作
      • (1)编程式事务
      • (2)声明式事务
  • 十六、Chang Streams
    • 1、什么是 Chang Streams
    • 2、Change Stream 的实现原理
    • 3、MongoShell测试
    • 4、Change Stream 故障恢复
    • 5、使用场景
    • 6、Chang Stream整合Spring Boot

系列文章索引

MongoDB基础入门到深入(一)安装、文档操作
MongoDB基础入门到深入(二)聚合高级操作
MongoDB基础入门到深入(三)索引高级操作
MongoDB基础入门到深入(四)复制(副本)集
MongoDB基础入门到深入(五)分片集群
MongoDB基础入门到深入(六)多文档事务
MongoDB基础入门到深入(七)建模、调优
MongoDB基础入门到深入(八)MongoDB整合SpringBoot、Chang Streams

十五、MongoDB整合SpringBoot

1、环境准备

1、引入依赖

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

2、配置yml

spring:
  data:
    mongodb:
      uri: mongodb://root:root@192.168.56.10:27017/test?authSource=admin
      #uri等同于下面的配置
      #database: test
      #host: 192.168.56.10
      #port: 27017
      #username: root
      #password: root
      #authentication-database: admin

      #复制集  https://docs.mongodb.com/manual/reference/connection-string/
      #uri: mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0


连接配置参考文档:https://www.mongodb.com/docs/manual/reference/connection-string/

3、使用时注入mongoTemplate

@Autowired
MongoTemplate mongoTemplate;

4、demo

//连接单点
//MongoClient mongoClient = MongoClients.create("mongodb://192.168.56.10:27017");
//连接副本集
MongoClient mongoClient = MongoClients.create("mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0");
//连接分片集群  节点:mongos
//MongoClient mongoClient = MongoClients.create("mongodb://192.168.56.10:27017,192.168.56.11:27017,192.168.56.12:27017");

//获得数据库对象
MongoDatabase database = mongoClient.getDatabase("test");
//获得集合
MongoCollection<Document> collection = database.getCollection("emp");

System.out.println("emp文档数:"+collection.countDocuments());

2、集合操作

@Test
public void testCollection() {

    boolean exists = mongoTemplate.collectionExists("emp");
    if (exists) {
        //删除集合
        mongoTemplate.dropCollection("emp");
    }
    //创建集合
    mongoTemplate.createCollection("emp");
}

3、文档操作

(1)相关注解

@Document
修饰范围: 用在类上
作用: 用来映射这个类的一个对象为mongo中一条文档数据。
属性:( value 、collection )用来指定操作的集合名称

@Id
修饰范围: 用在成员变量、方法上
作用: 用来将成员变量的值映射为文档的_id的值

@Field
修饰范围: 用在成员变量、方法上
作用: 用来将成员变量及其值映射为文档中一个key:value对。
属性:( name , value )用来指定在文档中 key的名称,默认为成员变量名

@Transient
修饰范围:用在成员变量、方法上
作用:用来指定此成员变量不参与文档的序列化

(2)创建实体

import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


@Document("emp")  //对应emp集合中的一个文档
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Employee {

    @Id   //映射文档中的_id
    private Integer id;
    @Field("username")
    private String name;
    @Field
    private int age;
    @Field
    private Double salary;
    @Field
    private Date birthday;
}

(3)添加文档

insert方法返回值是新增的Document对象,里面包含了新增后_id的值。如果集合不存在会自动创建集合。通过Spring Data MongoDB还会给集合中多加一个_class的属性,存储新增时Document对应Java中类的全限定路径。这么做为了查询时能把Document转换为Java类型。

@Test
public void testInsert() {

    Employee employee = new Employee(1, "小明", 30, 10000.00, new Date());

    //添加文档
    // sava:  _id存在时更新数据
    //mongoTemplate.save(employee);
    // insert: _id存在抛出异常   支持批量操作
    mongoTemplate.insert(employee);

    List<Employee> list = Arrays.asList(
            new Employee(2, "张三", 21, 5000.00, new Date()),
            new Employee(3, "李四", 26, 8000.00, new Date()),
            new Employee(4, "王五", 22, 8000.00, new Date()),
            new Employee(5, "张龙", 28, 6000.00, new Date()),
            new Employee(6, "赵虎", 24, 7000.00, new Date()),
            new Employee(7, "赵六", 28, 12000.00, new Date()));
    //插入多条数据
    mongoTemplate.insert(list, Employee.class);
}

插入重复数据时: insert报 DuplicateKeyException提示主键重复; save对已存在的数据进行更新。
批处理操作时: insert可以一次性插入所有数据,效率较高;save需遍历所有数据,一次插入或更新,效率较低。

(4)查询文档

Criteria是标准查询的接口,可以引用静态的Criteria.where的把多个条件组合在一起,就可以轻松地将多个方法标准和查询连接起来,方便我们操作查询语句。
在这里插入图片描述

@Test
public void testFind() {

    System.out.println("==========查询所有文档===========");
    //查询所有文档
    List<Employee> list = mongoTemplate.findAll(Employee.class);
    list.forEach(System.out::println);

    System.out.println("==========根据_id查询===========");
    //根据_id查询
    Employee e = mongoTemplate.findById(1, Employee.class);
    System.out.println(e);

    System.out.println("==========findOne返回第一个文档===========");
    //如果查询结果是多个,返回其中第一个文档对象
    Employee one = mongoTemplate.findOne(new Query(), Employee.class);
    System.out.println(one);

    System.out.println("==========条件查询===========");
    //new Query() 表示没有条件
    //查询薪资大于等于8000的员工
    //Query query = new Query(Criteria.where("salary").gte(8000));
    //查询薪资大于4000小于10000的员工
    //Query query = new Query(Criteria.where("salary").gt(4000).lt(10000));
    //正则查询(模糊查询)  java中正则不需要有//
    //Query query = new Query(Criteria.where("name").regex("张"));

    //and  or  多条件查询
    Criteria criteria = new Criteria();
    //and  查询年龄大于25&薪资大于8000的员工
    //criteria.andOperator(Criteria.where("age").gt(25),Criteria.where("salary").gt(8000));
    //or 查询姓名是张三或者薪资大于8000的员工
    criteria.orOperator(Criteria.where("name").is("张三"), Criteria.where("salary").gt(5000));
    Query query = new Query(criteria);

    //sort排序
    //query.with(Sort.by(Sort.Order.desc("salary")));


    //skip limit 分页  skip用于指定跳过记录数,limit则用于限定返回结果数量。
    query.with(Sort.by(Sort.Order.desc("salary")))
            .skip(0)  //指定跳过记录数
            .limit(4);  //每页显示记录数


    //查询结果
    List<Employee> employees = mongoTemplate.find(
            query, Employee.class);
    employees.forEach(System.out::println);
}
@Test
public void testFindByJson() {

    //使用json字符串方式查询
    //等值查询
    //String json = "{name:'张三'}";
    //多条件查询
    String json = "{$or:[{age:{$gt:25}},{salary:{$gte:8000}}]}";
    Query query = new BasicQuery(json);

    //查询结果
    List<Employee> employees = mongoTemplate.find(
            query, Employee.class);
    employees.forEach(System.out::println);
}

(5)更新文档

在Mongodb中无论是使用客户端API还是使用Spring Data,更新返回结果一定是受行数影响。如果更新后的结果和更新前的结果是相同,返回0。
updateFirst() 只更新满足条件的第一条记录
updateMulti() 更新所有满足条件的记录
upsert() 没有符合条件的记录则插入数据

@Test
public void testUpdate() {

    //query设置查询条件
    Query query = new Query(Criteria.where("salary").gte(15000));

    System.out.println("==========更新前===========");
    List<Employee> employees = mongoTemplate.find(query, Employee.class);
    employees.forEach(System.out::println);

    Update update = new Update();
    //设置更新属性
    update.set("salary", 13000);

    //updateFirst() 只更新满足条件的第一条记录
    //UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Employee.class);
    //updateMulti() 更新所有满足条件的记录
    //UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Employee.class);

    //upsert() 没有符合条件的记录则插入数据
    //update.setOnInsert("id",11);  //指定_id
    UpdateResult updateResult = mongoTemplate.upsert(query, update, Employee.class);

    //返回修改的记录数
    System.out.println(updateResult.getModifiedCount());
    //返回匹配的记录数
    System.out.println(updateResult.getMatchedCount());


    System.out.println("==========更新后===========");
    employees = mongoTemplate.find(query, Employee.class);
    employees.forEach(System.out::println);
}

(6)删除文档

@Test
public void testDelete() {

    //删除所有文档
    //mongoTemplate.remove(new Query(),Employee.class);

    //条件删除
    Query query = new Query(Criteria.where("salary").gte(10000));
    mongoTemplate.remove(query, Employee.class);

}

(7)聚合操作

MongoTemplate提供了aggregate方法来实现对数据的聚合操作。

在这里插入图片描述
基于聚合管道mongodb提供的可操作的内容:
在这里插入图片描述
基于聚合操作Aggregation.group,mongodb提供可选的表达式:
在这里插入图片描述

// 以聚合管道示例2为例
// 返回人口超过1000万的州
db.zips.aggregate( [
   { $group: { _id: "$state", totalPop: { $sum: "$pop" } } },
   { $match: { totalPop: { $gt: 10*1000*1000 } } }
] )

@Test
public void test(){
    //$group
    GroupOperation groupOperation = Aggregation
            .group("state").sum("pop").as("totalPop");

    //$match
    MatchOperation matchOperation = Aggregation.match(
            Criteria.where("totalPop").gte(10*1000*1000));

    // 按顺序组合每一个聚合步骤
    TypedAggregation<Zips> typedAggregation = Aggregation.newAggregation(
            Zips.class, groupOperation, matchOperation);

    //执行聚合操作,如果不使用 Map,也可以使用自定义的实体类来接收数据
    AggregationResults<Map> aggregationResults = mongoTemplate
            .aggregate(typedAggregation, Map.class);
    // 取出最终结果
    List<Map> mappedResults = aggregationResults.getMappedResults();
    for(Map map:mappedResults){
        System.out.println(map);
    }
}



// 返回各州平均城市人口
db.zips.aggregate( [
   { $group: { _id: { state: "$state", city: "$city" }, cityPop: { $sum: "$pop" } } },
   { $group: { _id: "$_id.state", avgCityPop: { $avg: "$cityPop" } } },
   { $sort:{avgCityPop:-1}}
] )

@Test
public void test2(){
    //$group
    GroupOperation groupOperation = Aggregation
            .group("state","city").sum("pop").as("cityPop");
    //$group
    GroupOperation groupOperation2 = Aggregation
            .group("_id.state").avg("cityPop").as("avgCityPop");
    //$sort
    SortOperation sortOperation = Aggregation
            .sort(Sort.Direction.DESC,"avgCityPop");

    // 按顺序组合每一个聚合步骤
    TypedAggregation<Zips> typedAggregation = Aggregation.newAggregation(
            Zips.class, groupOperation, groupOperation2,sortOperation);

    //执行聚合操作,如果不使用 Map,也可以使用自定义的实体类来接收数据
    AggregationResults<Map> aggregationResults = mongoTemplate
            .aggregate(typedAggregation, Map.class);
    // 取出最终结果
    List<Map> mappedResults = aggregationResults.getMappedResults();
    for(Map map:mappedResults){
        System.out.println(map);
    }
}
// 按州返回最大和最小的城市
db.zips.aggregate( [
   { $group:
      {
        _id: { state: "$state", city: "$city" },
        pop: { $sum: "$pop" }
      }
   },
   { $sort: { pop: 1 } },
   { $group:
      {
        _id : "$_id.state",
        biggestCity:  { $last: "$_id.city" },
        biggestPop:   { $last: "$pop" },
        smallestCity: { $first: "$_id.city" },
        smallestPop:  { $first: "$pop" }
      }
   },
  { $project:
    { _id: 0,
      state: "$_id",
      biggestCity:  { name: "$biggestCity",  pop: "$biggestPop" },
      smallestCity: { name: "$smallestCity", pop: "$smallestPop" }
    }
  },
   { $sort: { state: 1 } }
] )



@Test
public void test3(){
    //$group
    GroupOperation groupOperation = Aggregation
            .group("state","city").sum("pop").as("pop");

    //$sort
    SortOperation sortOperation = Aggregation
            .sort(Sort.Direction.ASC,"pop");

    //$group
    GroupOperation groupOperation2 = Aggregation
            .group("_id.state")
            .last("_id.city").as("biggestCity")
            .last("pop").as("biggestPop")
            .first("_id.city").as("smallestCity")
            .first("pop").as("smallestPop");

    //$project
    ProjectionOperation projectionOperation = Aggregation
            .project("state","biggestCity","smallestCity")
            .and("_id").as("state")
            .andExpression(
                    "{ name: \"$biggestCity\",  pop: \"$biggestPop\" }")
            .as("biggestCity")
            .andExpression(
                    "{ name: \"$smallestCity\", pop: \"$smallestPop\" }"
            ).as("smallestCity")
            .andExclude("_id");

    //$sort
    SortOperation sortOperation2 = Aggregation
            .sort(Sort.Direction.ASC,"state");


    // 按顺序组合每一个聚合步骤
    TypedAggregation<Zips> typedAggregation = Aggregation.newAggregation(
            Zips.class, groupOperation, sortOperation, groupOperation2,
            projectionOperation,sortOperation2);

    //执行聚合操作,如果不使用 Map,也可以使用自定义的实体类来接收数据
    AggregationResults<Map> aggregationResults = mongoTemplate
            .aggregate(typedAggregation, Map.class);
    // 取出最终结果
    List<Map> mappedResults = aggregationResults.getMappedResults();
    for(Map map:mappedResults){
        System.out.println(map);
    }

}

(8)小技巧:如何去掉_class属性

@Configuration
public class MongoConfig {

    /**
     * 定制TypeMapper去掉_class属性
     * @param mongoDatabaseFactory
     * @param context
     * @param conversions
     * @return
     */
    @Bean
    MappingMongoConverter mappingMongoConverter(
            MongoDatabaseFactory mongoDatabaseFactory,
            MongoMappingContext context, MongoCustomConversions conversions){

        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
        MappingMongoConverter mappingMongoConverter =
                new MappingMongoConverter(dbRefResolver,context);
        mappingMongoConverter.setCustomConversions(conversions);

        //构造DefaultMongoTypeMapper,将typeKey设置为空值
        mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));

        return mappingMongoConverter;
    }

}

4、事务操作

官方文档:https://www.mongodb.com/docs/upcoming/core/transactions/

(1)编程式事务

/**
 * 事务操作API
 * https://docs.mongodb.com/upcoming/core/transactions/
 */
@Test
public void updateEmployeeInfo() {
    //连接复制集
    MongoClient client = MongoClients.create("mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0");

    MongoCollection<Document> emp = client.getDatabase("test").getCollection("emp");
    MongoCollection<Document> events = client.getDatabase("test").getCollection("events");
    //事务操作配置
    TransactionOptions txnOptions = TransactionOptions.builder()
            .readPreference(ReadPreference.primary())
            .readConcern(ReadConcern.MAJORITY)
            .writeConcern(WriteConcern.MAJORITY)
            .build();
    try (ClientSession clientSession = client.startSession()) {
        //开启事务
        clientSession.startTransaction(txnOptions);

        try {

            emp.updateOne(clientSession,
                    Filters.eq("username", "张三"),
                    Updates.set("status", "inactive"));

            int i=1/0;

            events.insertOne(clientSession,
                    new Document("username", "张三").append("status", new Document("new", "inactive").append("old", "Active")));

            //提交事务
            clientSession.commitTransaction();

        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            clientSession.abortTransaction();
        }
    }
}

(2)声明式事务

1、配置事务管理器

@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory factory){
    //事务操作配置
TransactionOptions txnOptions = TransactionOptions.builder()
        .readPreference(ReadPreference.primary())
        .readConcern(ReadConcern.MAJORITY)
        .writeConcern(WriteConcern.MAJORITY)
        .build();
    return new MongoTransactionManager(factory);
}

2、编程测试service

@Service
public class EmployeeService {

    @Autowired
    MongoTemplate mongoTemplate;

    @Transactional
    public void addEmployee(){
        Employee employee = new Employee(100,"张三", 21,
                15000.00, new Date());
        Employee employee2 = new Employee(101,"赵六", 28,
                10000.00, new Date());

        mongoTemplate.save(employee);
        //int i=1/0;
        mongoTemplate.save(employee2);

    }
}

十六、Chang Streams

1、什么是 Chang Streams

Change Stream指数据的变化事件流,MongoDB从3.6版本开始提供订阅数据变更的功能。
Change Stream 是 MongoDB 用于实现变更追踪的解决方案,类似于关系数据库的触发器,但原理不完全相同:
在这里插入图片描述

2、Change Stream 的实现原理

Change Stream 是基于 oplog 实现的,提供推送实时增量的推送功能。它在 oplog 上开启一个 tailable cursor 来追踪所有复制集上的变更操作,最终调用应用中定义的回调函数。

被追踪的变更事件主要包括:
insert/update/delete:插入、更新、删除;
drop:集合被删除;
rename:集合被重命名;
dropDatabase:数据库被删除;
invalidate:drop/rename/dropDatabase 将导致 invalidate 被触发, 并关闭 change stream;
在这里插入图片描述

如果只对某些类型的变更事件感兴趣,可以使用使用聚合管道的过滤步骤过滤事件:

var cs = db.user.watch([{
    $match:{operationType:{$in:["insert","delete"]}}
}])

Change Stream会采用 "readConcern:majority"这样的一致性级别,保证写入的变更不会被回滚。因此:
未开启 majority readConcern 的集群无法使用 Change Stream;
当集群无法满足 {w: “majority”} 时,不会触发 Change Stream(例如 PSA 架构 中的 S 因故障宕机)。

3、MongoShell测试

# 窗口1
db.user.watch([],{maxAwaitTimeMS:1000000}).pretty()

# 窗口2
db.user.insert({name:"xxxx"})

在这里插入图片描述
变更事件字段说明
在这里插入图片描述

4、Change Stream 故障恢复

假设在一系列写入操作的过程中,订阅 Change Stream 的应用在接收到“写3”之后 于 t0 时刻崩溃,重启后后续的变更怎么办?
在这里插入图片描述
想要从上次中断的地方继续获取变更流,只需要保留上次变更通知中的 _id 即可。 Change Stream 回调所返回的的数据带有 _id,这个 _id 可以用于断点恢复。例如:

var cs = db.collection.watch([], {resumeAfter: <_id>}) 

即可从上一条通知中断处继续获取后续的变更通知。

5、使用场景

跨集群的变更复制——在源集群中订阅 Change Stream,一旦得到任何变更立即写 入目标集群。
微服务联动——当一个微服务变更数据库时,其他微服务得到通知并做出相应的变更。
其他任何需要系统联动的场景。

案例 1.监控
用户需要及时获取变更信息(例如账户相关的表),ChangeStreams 可以提供监控功能,一旦相关的表信息发生变更,就会将变更的消息实时推送出去。

案例 2.分析平台
例如需要基于增量去分析用户的一些行为,可以基于 ChangeStreams 把数据拉出来,推到下游的计算平台, 比如 类似 Flink、Spark 等计算平台等等。

案例 3.数据同步
基于 ChangeStreams,用户可以搭建额外的 MongoDB 集群,这个集群是从原端的 MongoDB 拉取过来的, 那么这个集群可以做一个热备份,假如源端集群发生 网络不通等等之类的变故,备集群就可以接管服务。 还可以做一个冷备份,如用户基于 ChangeStreams 把数据同步到文件,万一源端数据库发生不可服务, 就可以从文件里恢复出完整的 MongoDB 数据库, 继续提供服务。(当然,此处还需要借助定期全量备份来一同完成恢复) 另外数据同步它不仅仅局限于同一地域,可以跨地域,从北京到上海甚至从中国到美国等等。

案例 4.消息推送
假如用户想实时了解公交车的信息,那么公交车的位置每次变动,都实时推送变更的信息给想了解的用 户,用户能够实时收到公交车变更的数据,非常便捷实用。

注意事项
Change Stream 依赖于 oplog,因此中断时间不可超过 oplog 回收的最大时间窗;
在执行 update 操作时,如果只更新了部分数据,那么 Change Stream 通知的也是增量部分;
删除数据时通知的仅是删除数据的 _id。

6、Chang Stream整合Spring Boot

1、引入依赖

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

2、配置yml

spring:
  data:
    mongodb:
      uri: mongodb://root:root@192.168.56.10:27017/test?authSource=admin
      #uri等同于下面的配置
      #database: test
      #host: 192.168.56.10
      #port: 27017
      #username: root
      #password: root
      #authentication-database: admin

      #复制集  https://docs.mongodb.com/manual/reference/connection-string/
      #uri: mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0

3、配置 mongo监听器的容器MessageListenerContainer,spring启动时会自动启动监听的任务用于接收changestream

@Configuration
public class MongodbConfig {

    @Bean
    MessageListenerContainer messageListenerContainer(MongoTemplate template, DocumentMessageListener documentMessageListener) {

        Executor executor = Executors.newFixedThreadPool(5);

        MessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer(template, executor) {
            @Override
            public boolean isAutoStartup() {
                return true;
            }
        };

        ChangeStreamRequest<Document> request = ChangeStreamRequest.builder(documentMessageListener)
                .collection("user")  //需要监听的集合名
                //过滤需要监听的操作类型,可以根据需求指定过滤条件
                .filter(Aggregation.newAggregation(Aggregation.match(
                        Criteria.where("operationType").in("insert", "update", "delete"))))
                //不设置时,文档更新时,只会发送变更字段的信息,设置UPDATE_LOOKUP会返回文档的全部信息
                .fullDocumentLookup(FullDocument.UPDATE_LOOKUP)
                .build();
        messageListenerContainer.register(request, Document.class);

        return messageListenerContainer;
    }
}

4、配置mongo监听器,用于接收数据库的变更信息

@Component
public class DocumentMessageListener<S, T> implements MessageListener<S, T> {

   @Override
   public void onMessage(Message<S, T> message) {

      System.out.println(String.format("Received Message in collection %s.\n\trawsource: %s\n\tconverted: %s",
            message.getProperties().getCollectionName(), message.getRaw(), message.getBody()));


   }

}

5、测试
mongo shell插入一条文档
在这里插入图片描述
6、控制台输出
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

paligemma、Grounding-DINO-1.5简单无需标注无需训练直接可以使用的VLM图像到文本模型

1、paligemma 参考:https://github.com/google-research/big_vision/blob/main/big_vision/configs/proj/paligemma/README.md 模型架构: 文本与图像特征一起送入大模型 在线体验网址: https://huggingface.co/spaces/big-vision/paligemma 通过文字prompt既可与图片对话…

工行音视频服务平台建设与应用经验

近些年来&#xff0c;伴随着技术能力的积累突破&#xff0c;音视频服务开始蓬勃生长走进千家万户&#xff0c;使用远程视频通话、观看各类视频直播逐渐成为人们的日常&#xff0c;而金融服务作为社会生活的重要组成部分&#xff0c;自然需要积极拥抱应用新技术。 如今&#xff…

目标检测——无人机垃圾数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

【C++入门】—— C++入门 (下)_内联函数

前言&#xff1a;在了解完前面的C基础内容后&#xff0c;马上我们就要真正不如C的学习了&#xff0c;但在之前让我们最后了解最后一点点C入门知识&#xff01;来迟的520特别篇&#xff01; 本篇主要内容&#xff1a; 内联函数 auto关键字 范围for 指针空值nullptr C入门 1. 内联…

Science| 单体耦合纤维实现无芯片纺织电子(纤维器件/智能织物/柔性可穿戴电子)

东华大学Hongzhi Wang,Chengyi Hou和Qinghong Zhang团队在《Science》上发布了一篇题为“Single body-coupled fiber enables chipless textile electronics”的论文。论文内容如下: 一、 摘要 智能纺织品为将技术融入日常生活中提供了理想的平台。然而,目前的纺织电子系统…

飞凌嵌入式亮相上海CPSE,展现智能充储技术新力量

5月22日~24日&#xff0c;第三届上海国际充电桩及换电站展览会(CPSE)在上海汽车会展中心举行&#xff0c;飞凌嵌入式以“聚焦充电桩主控智造赋能车桩智联”为主题参展&#xff0c;与来自全国的客户朋友及行业伙伴一同交流分享&#xff0c;展位号Z15。 作为国内较早从事嵌入式技…

HTTPS:安全网络通信的基石

在数字化时代&#xff0c;网络通信的安全变得至关重要。HTTPS&#xff08;超文本传输安全协议&#xff09;是一种用于保护网络通信的协议&#xff0c;它通过加密技术确保数据传输的安全性和完整性。下面我们就来了解一下HTTPS。 一、HTTPS是什么&#xff1f; HTTPS是HTTP&…

罗德与施瓦茨FPS7频谱分析仪怎么判断真实信号?

频谱分析仪是电子测量领域的重要仪器&#xff0c;可以帮助工程师、研究人员分析信号的频域特性&#xff0c;为设备调试、故障诊断等提供有价值的数据支持。作为业界领先的频谱分析仪制造商&#xff0c;罗德与施瓦茨的FPS7型号在精度、灵敏度和分辨率等指标上都有出色表现&#…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-39-highlight() 方法之追踪定位

1.简介 在之前的文章中宏哥讲解和分享了&#xff0c;为了看清自动化测试的步骤&#xff0c;通过JavaScript添加高亮颜色&#xff0c;就可以清楚的看到执行步骤了。在学习和实践Playwright的过程中&#xff0c;偶然发现了使用Playwright中的highlight()方法也突出显示Web元素。…

macOS Monterey 12.7.5 (21H1222) 正式版发布,ISO、IPSW、PKG 下载

macOS Monterey 12.7.5 (21H1222) 正式版发布&#xff0c;ISO、IPSW、PKG 下载 5 月 13 日凌晨&#xff0c;macOS Sonoma 14.5 发布&#xff0c;同时带来了 macOS Ventru 13.6.7 和 macOS Monterey 12.7.5 安全更新。 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Appli…

【qt】QListWidget 组件

QListWidget 组件 一.QListWidget的用途二.界面设计三.QListWidget的添加1.界面添加2.代码添加 四.列表项的设置1.文本2.图标3.复选框4.列表大小 五.字体和图标的设置1.字体&#xff1a;2.图标&#xff1a; 六.设置显示模式1.图标2.列表 七.其他功能实现1.删除2.全选3.反选4.ad…

服务高峰期gc,导致服务不可用

随着应用程序的复杂性和负载的不断增加&#xff0c;对JVM进行调优&#xff0c;也是保障系统稳定性的一个重要方向。 需要注意&#xff0c;调优并非首选方案&#xff0c;一般来说解决性能问题还是要从应用程序本身入手&#xff08;业务日志&#xff0c;慢请求等&#xff09;&am…

今日刷三题(day13):变态跳台阶+包含不超过两种字符的最长字串+字符串的排列

题目一&#xff1a;变态跳台阶 题目描述&#xff1a; 一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。 输入输出描述&#xff1a; 输入&#xff1a;3 输出&#xff1a;…

SAPUI5基础知识2 - 手动创建一个SAPUI5的项目

1. 前言 在本篇文章中&#xff0c;我们将手动一步一步建立出第一个SAPUI5的 ‘Hello World!’ 项目。 2. 步骤详解 2.1 在BAS中建立Dev Space 进入SAP Business Application Studio的Dev Space Manger&#xff0c;选择创建Dev Space。 勾选HTML5 Application Template插件…

C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

由于删除、修改、重装QT库引起的软件问题@FreeBSD

由于由于删除、修改、重装QT库以及snappy库等&#xff0c;导致很多软件出现了异常&#xff0c;即无法启动&#xff0c;逐个解决问题。 qutebrowser浏览器 报错&#xff1a; qutebrowser报错 No backend library found qutebrowser needs QtWebKit or QtWebEngine, but neith…

Ollama本地运行 Mistral-7B-Instruct-v0.3

Ollama本地运行 Mistral-7B-Instruct-v0.3 0. 引言1. 运行 mistral:7b-instruct-v0.3-q8_02. 简单问个问题 0. 引言 Mixtral 5月23日发布了 Mistral-7B-Instruct-v0.3&#xff0c;支持 function calling&#xff0c;今天简单运行一下。 1. 运行 mistral:7b-instruct-v0.3-q8_…

人类听觉处理和语言中枢

人类听觉概述 人类听觉是指通过耳朵接收声音并将其转化为神经信号&#xff0c;从而使我们能够感知和理解声音信息的能力。听觉是人类五种感觉之一&#xff0c;对我们的日常生活和交流至关重要。 听觉是人类交流和沟通的重要工具。通过听觉&#xff0c;我们能够听到他人的语言…

蓝牙(2):BR/EDR的连接过程;查询(发现)=》寻呼(连接)=》安全建立=》认证=》pair成功;类比WiFi连接过程。

4.2.1 BR/EDR 流程&#xff1a; 查询&#xff08;发现&#xff09;》寻呼&#xff08;连接&#xff09;》安全建立》认证》pair成功 4.2.1.1 查询&#xff08;发现&#xff09;流程Inquiry (discovering) 类比WiFi的probe request/response 蓝牙设备使用查询流程来发现附近的…

Python下载安装图文教程,Pycharm下载安装图文教程

Python及Pycharm安装图文教程&#xff0c;供大家参考&#xff0c;具体内容如下 为了学习Python我今天对它进行了安装&#xff0c;并将Python及Pycharm安装方法进行了分享&#xff0c;希望可以帮助到大家 注&#xff1a;建议大家在安装过程中不要将软件安装到系统盘中。 1、P…