好客租房-09_学习MongoDB并完善通讯系统

news2025/1/16 1:54:40

9. 学习MongoDB 并完善租房的通讯系统后端

本章目的为MongoDB快速入门, 并完善上一节编写的通讯系统后台, 将DAO层从HashMap迁移到MongoDB中.

思考如下问题:

  1. MongoDB属于关系型还是非关系型数据库

  1. 为什么在我们的通讯系统中选择MongoDB作为数据库?

9.1 mongoDB概念简介

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储

解决方案。

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库

的,它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。

MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似

关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

9.2 MongoDB实操相关

接下来我们对mongoDB进行一波命令学习.

9.2.1 通过docker安装MongoDB

#拉取镜像
docker pull mongo:4.0.3
#创建容器
docker create --name mongodb -p 27017:27017 -v /data/mongodb:/data/db mongo:4.0.3
#启动容器
docker start mongodb
#进入容器
docker exec -it mongodb /bin/bash
#使用MongoDB客户端进行操作
mongo
> show dbs #查询所有的数据库
admin 0.000GB
config 0.000GB
local 0.000GB

9.2.2 MongoDB基本命令实操

为了更好的理解,下面与SQL中的概念进行对比:

SQL术语

概念MongoDB术语/概念

解释/说明

database

database

数据库

table

collection

数据库表/集合

row

document

数据记录行/文档

column

field

数据字段/域

index

index

索引

table joins

表连接

MongoDB不支持

primary key

primary key

主键,MongoDB自动将_id字段设置为主键

通过上表, 我们能看得出来, MongoDB的基本概念基本上还是能和关系型数据库的概念对上的.

不过存储结构视图就差别很大了, 左侧为数据库的表格结构, 右侧为MongDB的bson格式:

9.2.2.1mongoDB数据库以及表的操作

# 查看所有的数据库
> show dbs
    admin 0.000GBs
    config 0.000GB
    local 0.000GB
# 通过use关键字切换数据库, 这一点与mysql一致
> use admin
    switched to db admin
    #创建数据库
    #说明:在MongoDB中,数据库是自动创建的,通过use切换到新数据库中,进行插入数据即可自动创建数据库
> use testdb
    switched to db testdb
> show dbs  # 并没有创建数据库
    admin 0.000GB
    config 0.000GB
    local 0.000GB
> db.user.insert({id:1,name:'zhangsan'}) # 插入数据
    WriteResult({ "nInserted" : 1 })
> show dbs
    admin 0.000GB
    config 0.000GB
    local 0.000GB
    testdb 0.000GB #数据库自动创建
    
#查看表
> show tables
    user
> show collections  # tables 与 collections 是一个东西.
    user
>
    # 删除集合(表)
> db.user.drop()
    true # 如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
# 删除数据库
> use testdb # 先切换到要删除的数据中
    switched to db testdb
> db.dropDatabase()	 #删除数据库
    { "dropped" : "testdb", "ok" : 1 }
> show dbs
    admin 0.000GB
    config 0.000GB
    local 0.000GB

9.2.2.2 新增数据

在MongoDB中,存储的文档结构是一种类似于json的结构,称之为bson(全称为:Binary JSON)。

	#插入数据
	#语法:db.COLLECTION_NAME.insert(document)
> db.user.insert({id:1,username:'zhangsan',age:20})
	WriteResult({ "nInserted" : 1 })
> db.user.save({id:2,username:'lisi',age:25})
	WriteResult({ "nInserted" : 1 })
> db.user.find() #查询数据
	{ "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "id" : 1, "username" : "zhangsan",
	"age" : 20 }
	{ "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
	"age" : 25 }

9.2.2.3 更新数据

update() 方法用于更新已存在的文档。实例如下:

> db.user.find()
	{ "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "id" : 1, "username" : "zhangsan",
	"age" : 20 }
	{ "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
	"age" : 25 }
> db.user.update({id:1},{$set:{age:22}}) #更新数据
	WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
	{ "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "id" : 1, "username" : "zhangsan",
	"age" : 22 }
	{ "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
	"age" : 25 }
	#注意:如果这样写,会删除掉其他的字段
> db.user.update({id:1},{age:25})
	WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
	{ "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "age" : 25 }
	{ "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
	"age" : 25 }
	#更新不存在的字段,会新增字段
> db.user.update({id:2},{$set:{sex:1}}) #更新数据
> db.user.find()
	{ "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "age" : 25 }
	{ "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
	"age" : 25, "sex" : 1 }
	#更新不存在的数据,默认不会新增数据
> db.user.update({id:3},{$set:{sex:1}})
	WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.user.find()
	{ "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "age" : 25 }
	{ "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
	"age" : 25, "sex" : 1 }
	#如果设置第一个参数为true,就是新增数据
> db.user.update({id:3},{$set:{sex:1}},true)
    WriteResult({
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("5c08cb281418d073246bc642")
    })
> db.user.find()
    { "_id" : ObjectId("5c08c0024b318926e0c1f6dc"), "age" : 25 }
    { "_id" : ObjectId("5c08c0134b318926e0c1f6dd"), "id" : 2, "username" : "lisi",
    "age" : 25, "sex" : 1 }
    { "_id" : ObjectId("5c08cb281418d073246bc642"), "id" : 3, "sex" : 1 }

9.2.2.4 删除数据

> db.user.remove({age:25})
    WriteResult({ "nRemoved" : 2 }) #删除了2条数据
    #插入4条测试数据
> db.user.insert({id:1,username:'zhangsan',age:20})
> db.user.insert({id:2,username:'lisi',age:21})
> db.user.insert({id:3,username:'wangwu',age:22})
> db.user.insert({id:4,username:'zhaoliu',age:22})


> db.user.remove({age:22},true)
	WriteResult({ "nRemoved" : 1 }) #删除了1条数据
    #删除所有数据
> db.user.remove({})
    #说明:为了简化操作,官方推荐使用deleteOne()与deleteMany()进行删除数据操作。
    db.user.deleteOne({id:1})
    db.user.deleteMany({}) #删除所有数据

9.2.2.5 查询数据

#插入测试数据
db.user.insert({id:1,username:'zhangsan',age:20})
db.user.insert({id:2,username:'lisi',age:21})
db.user.insert({id:3,username:'wangwu',age:22})
db.user.insert({id:4,username:'zhaoliu',age:22})

# 常见的几种查询方式.
db.user.find() #查询全部数据
db.user.find({},{id:1,username:1}) #只查询id与username字段
db.user.find().count() #查询数据条数
db.user.find({id:1}) #查询id为1的数据
db.user.find({age:{$lte:21}}) #查询小于等于21的数据
db.user.find({age:{$lte:21}, id:{$gte:2}}) #and查询,age小于等于21并且id大于等于2
db.user.find({$or:[{id:1},{id:2}]}) #查询id=1 or id=2
#分页查询:Skip()跳过几条,limit()查询条数
db.user.find().limit(2).skip(1) #跳过1条数据,查询2条数据
db.user.find().sort({id:-1}) #按照age倒序排序,-1为倒序,1为正序

通过上面的操作的练习, 我们可以基本熟悉他的套路, 本质上就是调用一个表对应的某个方法. 扩展一下, 你可以试试下面这个命令

db.user.find

输出结果如下:

不难看出, 通过这个命令调出的就是对应的C++代码.

9.2.3 索引

索引通常能够极大的提高查询的效率, 这个我相信大多数人在关系型数据库中都有很深的印象, mongoDB底层基本概念延续了关系型数据库那一套, 索引也被其延续下来使用了.

#查看当前user表的索引
> db.user.getIndexes()
    [
        {
            "v" : 2,
            "key" : {
                "_id" : 1
            },
            "name" : "_id_",
            "ns" : "testdb.user"
        }
    ]
#说明:1表示升序创建索引,-1表示降序创建索引。
#创建索引
> db.user.createIndex({'age':1})
    {
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
    }

#删除索引
> db.user.dropIndex("age_1")
#或者,删除除了_id之外的索引
> db.user.dropIndexes()
#创建联合索引
> db.user.createIndex({'age':1, 'id':-1})
#查看索引大小,单位:字节
db.user.totalIndexSize()

9.2.4 UI客户端工具

Robo 3T是MongoDB的客户端工具,我们可以使用它来操作MongoDB, 有需要的可以自行去官网下载.

9.3 Java 通过api操作MongoDB

新建一个itcast-mongodb项目

9.3.1 搭建项目

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>itcast-mongodb</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-sync</artifactId>
            <version>3.9.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

完成构建后的项目结构如图所示:

9.3.2 使用demo测试连接MongoDB

package cn.itcast.mongodb;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;

import java.util.function.Consumer;
/**
 * 和连接mysql的demo如出一辙
 */ 
public class MongoDBDemo {
    public static void main(String[] args) {
        // 建立连接
        MongoClient mongoClient = MongoClients.create("mongodb://127.0.0.1:27017");
        // 连接数据库
        MongoDatabase mongoDatabase = mongoClient.getDatabase("testdb");
        // 选择表
        MongoCollection<Document> userCollection = mongoDatabase.getCollection("user");
        // 查询十条数据
        userCollection.find().limit(10).forEach((Consumer<? super Document>) document -> {
            System.out.println(document.toJson());
        });
        // 关闭连接
        mongoClient.close();
    }
}

9.3.2 使用Junit测试MongoDB的常见操作

新建一个类, 测试增删改查

package cn.itcast.mongodb;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Sorts;
import com.mongodb.client.result.DeleteResult;
import org.bson.Document;
import org.junit.Before;
import org.junit.Test;

import java.util.function.Consumer;

import static com.mongodb.client.model.Filters.*;

public class TestCRUD {
    // mongoDB的表对象, 我们对数据的操作都是通过调用表对象的方法来实现. 这一点与我们输入mongoDB的命令如出一辙
    private MongoCollection<Document> mongoCollection;

    @Before
    public void init() {
        // 建立连接
        MongoClient mongoClient = MongoClients.create("mongodb://127.0.0.1:27017");
        // 连接数据库
        MongoDatabase mongoDatabase = mongoClient.getDatabase("testdb");
        // 选择表, 并缓存表对象
        mongoCollection = mongoDatabase.getCollection("user");
    }

    @Test
    public void testQuery() {
        // 调用表对象的查询方法与排序
        this.mongoCollection.find( and(
                lte("age", 50),
                gt("id", 100)
                )
        ).sort(Sorts.descending("id"))
                .projection(
                        // 这一步相当于我们的 select id, age (不包含_id) from ...
                        Projections.fields(
                                Projections.include("id", "age"),
                                // 不查询 '_id' ,即不查询mongoDB生成的随机 _id
                                Projections.excludeId()
                        )
                )
                .forEach(new Consumer<Document>() {
                    @Override
                    public void accept(Document document) {
                        // 对于遍历出来的结果, 打印即可
                        System.out.println(document.toJson());
                    }
                });
    }

    @Test
    public void testInsert() {
        Document document = new Document("id", 10001)
                .append("name", "张三")
                .append("age", 30);
        this.mongoCollection.insertOne(document);
        System.out.println("插入数据成功");

        this.mongoCollection.find(eq("id", 10001)
        ).forEach(new Consumer<Document>() {
            @Override
            public void accept(Document document) {
                System.out.println(document.toJson());
            }
        });
    }

    /**
     *
     */
    @Test
    public void testDelete(){
        DeleteResult deleteResult = this.mongoCollection.deleteOne(eq("id",
                10001));
        System.out.println(deleteResult);
    }

}

9.3.3 Java的面向对象接入MongoDB

我们知道MySQL可以很好的对接面向对象, MongoDB用的是类json格式, 那么也应该效果不差.

首先我们构建两个对象Person和Address

package cn.itcast.mongodb.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private ObjectId id;
    private String name;
    private int age;
    private Address address;
}
package cn.itcast.mongodb.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
    private String street;
    private String city;
    private String zip;
}

我们写一下单元测试

package cn.itcast.mongodb;

import cn.itcast.mongodb.pojo.Address;
import cn.itcast.mongodb.pojo.Person;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

/**
 * mongoDB上了面向对象, 就好像我们在使用mybatis-plus的效果一致
 */
public class TestPerson {
    private MongoCollection<Person> personCollection;

    /**
     * 连接, 得到mongoDB的表对象
     */
    @Before
    public void init() {
        CodecRegistry pojoCodecRegistry = CodecRegistries
                .fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
                        CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build())
                );
        // 建立连接
        MongoClient mongoClient = MongoClients.create("mongodb://127.0.0.1:27017");
        // 连接数据库
        MongoDatabase mongoDatabase = mongoClient.getDatabase("testdb").withCodecRegistry(pojoCodecRegistry);
        // 选择表
        personCollection = mongoDatabase.getCollection("person", Person.class);
    }

    @Test
    public void testInsert() {
        Person person = new Person(ObjectId.get(),
                "张三", 20, new Address("人民路", "上海市", "666666"));
        this.personCollection.insertOne(person);
        System.out.println("插入数据成功");
    }

    @Test
    public void testInserts() {
        List<Person> personList = Arrays.asList(new Person(ObjectId.get(), "张三",
                        20, new Address("人民路", "上海市", "666666")),
                new Person(ObjectId.get(), "李四", 21, new Address("北京西路", "上海市", "666666")),
                new Person(ObjectId.get(), "王五", 22, new Address("南京东路", "上海市", "666666")),
                new Person(ObjectId.get(), "赵六", 23, new Address("陕西南路", "上海市", "666666")),
                new Person(ObjectId.get(), "孙七", 24, new Address("南京西路", "上海市", "666666")));
        this.personCollection.insertMany(personList);
        System.out.println("插入数据成功");
    }

    @Test
    public void testQuery() {
        this.personCollection.find(
                Filters.eq("name", "张三"))
                .forEach((Consumer<? super Person>) person -> {
                    System.out.println(person);
                });
    }

    @Test
    public void testUpdate() {
        UpdateResult updateResult = this.personCollection.updateMany(
                // SET age = 22 WHERE name = '张三'
                Filters.eq("name", "张三"), Updates.set("age", 22)
        );
        System.out.println(updateResult);
    }

    @Test
    public void testDelete() {
        DeleteResult deleteResult = this.personCollection.deleteOne(
                Filters.eq("name", "张三")
        );
        System.out.println(deleteResult);
    }
}

9.3.4 SpringBoot接入MongoDB

因为我们在搭建项目的时候, 就已经引入了SpringBoot与MongoDB整合所需的jar, 此处不再赘述.

直接新建并编写application.properties配置文件

# Spring boot application
spring.application.name = itcast-mongodb
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/testdb

编写PersonDao

package cn.itcast.mongodb.dao;

import cn.itcast.mongodb.pojo.Person;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 提供一些类似Mybatis-plus的常见接口
 */
@Component
public class PersonDao {

    /**
     * 这个对象是通过读取我们的properties文件生成, 就是一个简化的表对象
     */
    @Autowired
    private MongoTemplate mongoTemplate;

    public void savePerson(Person person) {
        // TODO 提问: 为什么 mongoTemplate 这里没有指定表, 却能准确的插入到person表中呢?
        // 你可以点进去看一下源码: 他使用了你传入的类的名称小写首字母, 作为目标表名.
        // 你不好奇如果改了类名, 那表名不会自动更改吗?
        // 事实上我们在Spring整合时, 可以指定对象的表名
        this.mongoTemplate.save(person);
    }

    public List<Person> queryPersonListByName(String name) {
        Query query = Query.query(Criteria.where("name").is(name));
        return this.mongoTemplate.find(query, Person.class);
    }

    public List<Person> queryPersonListByName(Integer page, Integer rows) {
        Query query = new Query().limit(rows).skip((page - 1) * rows);
        return this.mongoTemplate.find(query, Person.class);
    }

    public UpdateResult update(Person person) {
        Query query = Query.query(Criteria.where("id").is(person.getId()));
        Update update = Update.update("age", person.getAge());
        return this.mongoTemplate.updateFirst(query, update, Person.class);
    }

    public DeleteResult deleteById(String id) {
        Query query = Query.query(Criteria.where("id").is(id));
        return this.mongoTemplate.remove(query, Person.class);
    }
}

测试相关代码

package cn.itcast.mongodb;

import cn.itcast.mongodb.dao.PersonDao;
import cn.itcast.mongodb.pojo.Address;
import cn.itcast.mongodb.pojo.Person;
import org.bson.types.ObjectId;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestPersonDao {
    @Autowired
    private PersonDao personDao;

    @Test
    public void testSave() {
        Person person = new Person(ObjectId.get(), "张三", 20,
                new Address("人民路", "上海市", "666666"));
        this.personDao.savePerson(person);
    }

    @Test
    public void testQuery() {
        List<Person> personList = this.personDao.queryPersonListByName("张三");
        for (Person person : personList) {
            System.out.println(person);
        }
    }

    @Test
    public void testQuery2() {
        List<Person> personList = this.personDao.queryPersonListByName(2, 2);
        for (Person person : personList) {
            System.out.println(person);
        }
    }

    @Test
    public void testUpdate() {
        Person person = new Person();
        person.setId(new ObjectId("5c0956ce235e192520086736"));
        person.setAge(30);
        this.personDao.update(person);
    }

    @Test
    public void testDelete() {
        this.personDao.deleteById("5c09ca05235e192d8887a389");
    }
}

到这里我们基本上已经把常见的MongoDB操作都执行一遍了.

9.4 使用MongoDB改进实时通讯系统的存储方式

我们先改造项目依赖, 引入相关整合用的依赖

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

编写application.properties配置文件

# Spring boot application
spring.application.name = itcast-mongodb
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/testdb

9.4.1 改进pojo类

我们在这里只需要改进Message类即可, 改后的完整代码如下:

package cn.itcast.haoke.im.pojo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
// 新增内容: 指定表名为message
@Document(collection = "message")
public class Message {
    // 修改内容: long -> org.bson.types.ObjectId, 并指定这个id 与_id对应.
    @Id
    private ObjectId id;
    /**
     * 消息体暂不支持复杂消息.
     */
    private String msg;
    /**
     * 消息状态,1-未读,2-已读
     * 建立索引
     */
    @Indexed
    private Integer status;

    /**
     * 发送的时间和已读的时间
     */
    @Field("send_date")
    @Indexed
    private Date sendDate;
    @Field("read_date")
    private Date readDate;
    /**
     * 发送方和接收方
     */
    @Indexed
    private User from;
    @Indexed
    private User to;
}

9.4.2 改进Dao接口与相关实现

更改MessageDao接口, 主要就是因为主键类型的变更导致的.

package cn.itcast.haoke.im.dao;

import cn.itcast.haoke.im.pojo.Message;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.types.ObjectId;

import java.util.List;

public interface MessageDAO {

    /**
     * 查询点对点聊天记录
     */
    List<Message> findListByFromAndTo(Long fromId, Long toId, Integer page, Integer
            rows);

    /**
     * 根据id查询数据
     *
     * @param id
     * @return
     */
    Message findMessageById(String id);

    /**
     * 更新消息状态
     *
     * @param id
     * @param status
     * @return
     */
    UpdateResult updateMessageState(ObjectId id, Integer status);

    /**
     * 新增消息
     *
     * @param message
     * @return
     */
    Message saveMessage(Message message);

    /**
     * 根据消息id删除数据
     *
     * @param id
     * @return
     */
    DeleteResult deleteMessage(String id);

}

实现类变更如下:

package cn.itcast.haoke.im.dao.impl;

import cn.itcast.haoke.im.dao.MessageDAO;
import cn.itcast.haoke.im.pojo.Message;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;

@Component
public class MessageDAOImpl implements MessageDAO {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 实现:查询点对点消息记录
     * @param fromId
     * @param toId
     * @param page
     * @param rows
     * @return
     */
    @Override
    public List<Message> findListByFromAndTo(Long fromId, Long toId, Integer page, Integer rows) {

        // 用户A发送给用户B的条件
        Criteria criteriaFrom = new Criteria().andOperator(
                Criteria.where("from.id").is(fromId),
                Criteria.where("to.id").is(toId)
        );

        // 用户B发送给用户A的条件
        Criteria criteriaTo = new Criteria().andOperator(
                Criteria.where("from.id").is(toId),
                Criteria.where("to.id").is(fromId)
        );


        Criteria criteria = new Criteria().orOperator(criteriaFrom, criteriaTo);

        PageRequest pageRequest = PageRequest.of(page - 1, rows, Sort.by(Sort.Direction.ASC, "sendDate"));

        // 设置查询条件,分页
        Query query = Query.query(criteria).with(pageRequest);

//        System.out.println(query);

        return this.mongoTemplate.find(query, Message.class);
    }

    @Override
    public Message findMessageById(String id) {
        return this.mongoTemplate.findById(new ObjectId(id), Message.class);
    }

    @Override
    public UpdateResult updateMessageState(ObjectId id, Integer status) {
        Query query = Query.query(Criteria.where("id").is(id));
        Update update = Update.update("status", status);
        if (status.intValue() == 1) {
            update.set("send_date", new Date());
        } else if (status.intValue() == 2) {
            update.set("read_date", new Date());
        }
        return this.mongoTemplate.updateFirst(query, update, Message.class);
    }

    @Override
    public Message saveMessage(Message message) {
        // 写入发送时间
        message.setSendDate(new Date());
        message.setStatus(1);
        message.setId(ObjectId.get());
        return this.mongoTemplate.save(message);
    }

    @Override
    public DeleteResult deleteMessage(String id) {
        Query query = Query.query(Criteria.where("id").is(id));
        return this.mongoTemplate.remove(query, Message.class);
    }
}

调整单元测试, 这里也是因为id的类型变更导致的调整.

package cn.itcast.haoke.im.dao;

import cn.itcast.haoke.im.pojo.Message;
import cn.itcast.haoke.im.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMessageDAO {
    @Autowired
    private MessageDAO messageDAO;

    @Test
    public void testSave(){
        Message message = Message.builder()
                .msg("你好")
                .sendDate(new Date())
                .status(1)
                .from(new User(1001L, "zhangsan"))
                .to(new User(1002L,"lisi"))
                .build();
        this.messageDAO.saveMessage(message);
        message = Message.builder()
                .msg("你也好")
                .sendDate(new Date())
                .status(1)
                .to(new User(1001L, "zhangsan"))
                .from(new User(1002L,"lisi"))
                .build();
        this.messageDAO.saveMessage(message);
        message = Message.builder()
                .msg("我在学习开发IM")
                .sendDate(new Date())
                .status(1)
                .from(new User(1001L, "zhangsan"))
                .to(new User(1002L,"lisi"))
                .build();
        this.messageDAO.saveMessage(message);
        message = Message.builder()
                .msg("那很好啊!")
                .sendDate(new Date())
                .status(1)
                .to(new User(1001L, "zhangsan"))
                .from(new User(1002L,"lisi"))
                .build();
        this.messageDAO.saveMessage(message);
        System.out.println("ok");
    }

    @Test
    public void testQueryList(){
        List<Message> list = this.messageDAO.findListByFromAndTo(1001L, 1002L, 2,
                1);
        for (Message message : list) {
            System.out.println(message);
        }
    }

    @Test
    public void testQueryById(){
        List<Message> list = this.messageDAO.findListByFromAndTo(1001L, 1002L, 2, 1);
        Message message = this.messageDAO.findMessageById(list.get(0).getId().toString());
        System.out.println(message);
    }
}

这样我们就完成了本次改造目标.

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

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

相关文章

python+django医院固定资产设备管理系统

管理员功能模块 管理员登录&#xff0c;通过填写用户名、密码、角色等信息&#xff0c;输入完成后选择登录即可进入医院设备管理系统&#xff0c; 管理员登录进入医院设备管理系统可以查看首页、个人中心、科室员管理、维修员管理、设备领用管理、设备信息管理、设备入库管理、…

人工智能入门杂记

本篇文章属于所有发表的文章的导读吧&#xff0c;以后会常更新。 目录 1.数据挖掘、机器学习、深度学习、云计算、人工智能 2.深度学习、强化学习、对抗学习、迁移学习 3.基础知识--线性代数 4.基础知识--概率与数理统计 5.常用工具库 6.机器学习 6.1 什么是训练什么是推…

Java数组

文章目录Java 数组一、数组介绍二、数组1. 数组静态初始化1.1 数组定义格式1.2 数组静态初始化2. 数组元素访问3. 数组遍历操作3.1 数组遍历介绍3.2 数组遍历场景3.3 数组遍历案例1&#xff09;数组遍历-求偶数和2&#xff09;数组遍历-求最大值3&#xff09;数组遍历综合案例4…

【C语言航路】第十四站:文件

目录 一、为什么使用文件 二、什么是文件 1.程序文件 2.数据文件 3.文件名 三、文件的打开和关闭 1.文件指针 2.文件的打开和关闭 四、文件的顺序读写 1.对于输入输出的理解 2.fgetc与fputc &#xff08;1&#xff09;fgetc与fputc的介绍 &#xff08;2&#xff0…

2023年springcloud面试题(第一部分)

1. 什么是微服务架构微服务架构就是将单体的应用程序分成多个应用程序&#xff0c;这多个应用程序就成为微服务&#xff0c;每个微服务运行在自己的进程中&#xff0c;并使用轻量级的机制通信。这些服务围绕业务能力来划分&#xff0c;并通过自动化部署机制来独立部署。这些服务…

MP-4可燃气体传感器介绍

MP-4可燃气体传感器简介MP-4可燃气体传感器采用多层厚膜制造工艺&#xff0c;在微型Al2O3陶瓷基片的两面分别制作加热器和金属氧化物半导体气敏层&#xff0c;封装在金属壳体内。当环境空气中有被检测气体存在时传感器电导率发生变化。该气体的浓度越高&#xff0c;传感器的电导…

JavaWeb | JDBC相关API详解 2 (内附以集合形式输出表)

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JDBC Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&#x…

C语言编程题

1、求斐波那契数列1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8……前20项之和 #include<stdio.h> int main() {int i,j,k,t2;ij1;printf("%d %d\n",i,j);for(k0;k<9;k){iij;jij;ttij;printf("%d %d\n",i,j);}printf(&q…

java七大查找 十大排序 贪心

七大查找 1.1二分查找(前提是 数据有序)说明&#xff1a;元素必须是有序的&#xff0c;从小到大&#xff0c;或者从大到小都是可以的。public static int binarySearc(int[] arr,int number){int min0;int maxarr.length-1;while(true){if(min>max){return -1;}int mid(maxm…

c++二插搜索树

1二插搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: ​ 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 ​ 若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值 …

mongodb shell

连接指定数据库 .\mongosh.exe localhost:27017/test不连接数据库 .\mongosh.exe --nodb然后连接数据库 conn new Mongo("localhost:27017") /// mongodb://localhost:27017/?directConnectiontrue&serverSelectionTimeoutMS2000 db conn.getDB("test&q…

Git学习笔记(黑马)

目录 一、获取本地仓库 二、为常用指令配置别名 三、基础操作指令 四、分支 五、Git远程仓库&#xff08; 码云Gitee&#xff09; &#xff08;一&#xff09;配置SSH公钥 &#xff08;二&#xff09;Gitee设置账户公钥 六、操作远程仓库 &#xff08;一&#xff09;添…

【数据结构】详谈复杂度

目录 1.前言 2.什么是复杂度 3.如何计算时间复杂度 1.引例 2.二分查找 3.常见的复杂度 4.如何计算空间复杂度 5.关于递归 6.总结 1.前言 我们在做一些算法题时&#xff0c;经常会发现题目会对时间复杂度或者空间复杂度有所要求&#xff0c;如果你不知道什么是复杂度时&am…

SQL--DDL

目录 一、数据库的相关概念 二、MySQL数据库 1. 关系型数据库&#xff08;RDBMS&#xff09; 2. 数据数据库 3. MySQL客户端连接的两种方式 方式一&#xff1a;使用MySQL提供的客户端命令行工具 方式二&#xff1a;使用系统自带的命令行工具执行指令 三、SQL SQL的…

【C++】深浅拷贝

最近一些老铁一直问我深浅拷贝的问题&#xff0c;今天我们就来介绍一下深浅拷贝在说深浅拷贝构造之前&#xff0c;我们先介绍一下拷贝构造函数的应用场景&#xff1a;使用另一个同类型的对象来初始化新创建的对象。浅拷贝我们在学类和对象时了解到了类的6大默认函数&#xff0c…

给定一个数组arr,代表每个人的能力值。再给定一个非负数k,如果两个人能力差值正好为k,那么可以凑在一起比赛 一局比赛只有两个人,返回最多可以同时有多少场比赛

目录题目描述题目解析代码实现对数器题目描述 给定一个数组arr&#xff0c;代表每个人的能力值。再给定一个非负数k&#xff0c;如果两个人能力差值正好为k&#xff0c;那么可以凑在一起比赛一局比赛只有两个人&#xff0c;返回最多可以同时有多少场比赛 比如&#xff1a; [3&a…

MyBatis的入门

1、Mybatis的简介和特性 2、环境配置及其注意事项 2.1、注意事项 本文示例&#xff0c;开发环境 IDE&#xff1a;idea 2019.2 构建工具&#xff1a;maven 3.8.6 MySQL版本&#xff1a;MySQL 8 MyBatis版本&#xff1a;MyBatis 3.5.7 MySQL不同版本的注意事项&#xff1a;…

Allegro如何自动做差分对内等长操作指导

Allegro如何自动做差分对内等长操作指导 在做PCB设计的时候,需要给差分做对内等长,如果差分对比较多,Allegro支持自动做差分对内等长,如下图 具体操作如下 选择Route选择Auto-interactive Phase Tu

【UE4】将pmx导入到ue4中(obj-zip-mixamo绑骨)|模之屋模型导入UE4(较详细)

前言&#xff1a;我用fbx导入mixamo会报错&#xff0c;所以想用obj格式试试。 fbx导入↓ 效果预览&#xff1a; 目录 1.下载模型 2. 为blender安装插件 3.打开blender ​编辑 要删掉默认生成的方块&#xff01;&#xff01;&#xff01; 4.帮老婆找衣服环节&#xff01;&…

CSS定位属性详解

一、简介 1.文档流 在介绍postion之前&#xff0c;有必要先了解下文档流。 简单说就是元素按照其在 HTML 中的位置顺序决定排布的过程。HTML的布局机制就是用文档流模型的&#xff0c;即块元素&#xff08;block&#xff09;独占一行&#xff0c;内联元素&#xff08;inline…