JB3-7-MongoDB

Java道经第3卷 - 第7阶 - MongoDB

心法:本章使用 Maven 父子结构项目进行练习

练习项目结构如下

|_ v3-7-ssm-mongodb
	|_ 13701 test

武技:搭建练习项目结构

  1. 创建父项目 v3-7-ssm-mongodb,删除 src 目录。
  2. 在父项目中管理依赖:
<dependencyManagement>
    <dependencies>
        <!--spring-boot-starter-parent-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <!--声明该项目是一个POM类型的父项目,不参与打包,只用于传递依赖-->
            <type>pom</type>
            <!--导入该项目的 `<dependencyManagement>` 到本项目-->
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  1. 在父项目中添加依赖:
<dependencies>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>
    <!--hutool-all-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool-all.version}</version>
    </dependency>
</dependencies>
  1. 创建子项目 test,暂不用添加任何依赖。

S01. Mongo基础入门

E01. Mongo概念入门

心法:MongoDB 是一个由 C++ 语言编写的,基于分布式文件存储的数据库,面向文档(类似 JSON 对象)存储,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 类型:是非关系数据库当中功能最丰富,最像关系数据库的的产品:

  • MongoDB 支持支持索引,支持复杂查询。
  • MongoDB 不支持连表操作,但支持文档数据嵌套。

Mongo 对应 MySQL

MongoDB 角色MySQL 角色
database 数据库database 数据库
collection 集合table 表格
document 文档row 表格行
field 字段column 表格列
index 索引index 索引
primary key 主键primary key 主键

1. Mongo单机容器搭建

武技:在 Docker 中搭建 MongoDB 单机容器

  1. 创建相关目录:
# 创建Mongo相关目录
mkdir -p /opt/mongo/data
chmod -R 777 /opt/mongo/data
  1. 创建运行容器:
# 拉取镜像(二选一)
docker pull mongo:6.0.3;
docker pull registry.cn-hangzhou.aliyuncs.com/joezhou/mongo:6.0.3;

# 创建并启动Mongo容器
# args: `--auth`: 需要账号密码访问Mongo数据库
docker run -itd --name mongo --network my-net -p 27017:27017 \
	-v /opt/mongo/data:/data \
	registry.cn-hangzhou.aliyuncs.com/joezhou/mongo:6.0.3 --auth

# 查看Mongo容器
docker ps --format "{{.ID}}\t{{.Names}}\t{{.Ports}}"
docker logs mongo -f

# 开放Mongo容器端口
firewall-cmd --add-port=27017/tcp --permanent
firewall-cmd --reload
  1. 进入 Mongo 容器:进入内置的 admin 数据库中:
# 进入 Mongo 容器
docker exec -it mongo mongosh admin
  1. 创建 Mongo 用户:
# 创建Mongo用户: admin/admin
db.createUser({ 
	user:'admin', 
	pwd:'admin', 
	roles:[{ role: 'root', db: 'admin' }]
});

# 登录Mongo容器
db.auth('admin', 'admin');

# 查看所有非空数据库: 默认包含 `admin/config/local` 三个数据库
show dbs

2. 使用IDEA连接Mongo服务器

武技:使用 IDEA 作为 Mongo 客户端

  1. 点击 Database - 加号 - Data Source - MongoDB 进入 Mongo 配置页面。
  2. 点击 Driver:MongoDB - Go to Driver - 加号 - Custom Jars,然后选择 mongo-jdbc-standalone-1.20.jar(自行准备):
  3. General 选项卡中填写 Mongo 连接的基本信息,注意认证方式要选择 SCRAM-SHA-1 哈希算法。

在这里插入图片描述

E02. Mongo常用角色

1. Mongo数据库

心法:一个 Mongo 服务中可以建立多个数据库,默认数据库为 db,存储在 data 目录中。

Mongo 内置数据库分为 admin, local 和 config 三个

admin 数据库:从权限的角度来看,这是 root 数据库:

  • 要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。
  • 一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。

local 数据库:这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合。

config 数据库:当 Mongo 用于分片设置时,config 数据库在内部使用,用于保存分片的相关信息。

武技:测试 Mongo 数据库常用操作

// 切换当前数据库为 mongo: 数据库不存在时自动创建
use mongo

// 查看所有数据库,空数据库不展示
show dbs

// 查看当前数据库
db

// 删除当前数据库
db.dropDatabase();

2. Mongo用户

心法:一个 Mongo 数据库中可以包含多个 Mongo 用户。

Mongo 内置角色

  • read:支持读取所有非系统集合和 system.js 集合上的数据。
  • readWrite:包含 read 角色的所有特权,支持修改所有非系统集合和 system.js 集合上的数据 。
  • dbAdmin:支持管理数据库对象,但不支持读写数据库。
  • dbOwner:具有该数据库的全部权限。
  • userAdmin:支持为当前用户创建,修改用户和角色,支持将该数据库的任意权限赋予任意的用户。
  • readAnyDatabase:具有 read 每一个数据库的权限,但不包括应用到集群中的数据库。
  • readWriteAnyDatabase:具有 readWrite 每一个数据库权限,但是不包括应用到集群中的数据库。
  • userAdminAnyDatabase:具有 userAdmin 每一个数据库权限,但是不包括应用到集群中的数据库。
  • dbAdminAnyDatabase:具有 dbAdmin 每一个数据库权限,但是不包括应用到集群中的数据库。
  • root:包含以上全部角色特权,不具有备份恢复和直接操作 system.* 集合的权限,但 root 角色用户可以赋予自己这些权限。

武技:测试 Mongo 用户常用操作

// 查看当前数据库中的全部用户
show users

// 创建用户: joezhou/joezhou,拥有针对 mongo 仓库的 dbAdmin 角色
// 重复创建报错
db.createUser({
    user: 'joezhou',
    pwd: '123456',
    roles: [{role: 'dbAdmin', db: 'mongo'}]
});

// 修改用户密码
db.changeUserPassword('joezhou', 'joezhou');

// 修改用户角色:拥有针对 mongo 仓库的 dbOwner 角色
db.updateUser('joezhou', {
    roles: [{role: 'root', db: 'mongo'}]
});

// 删除用户
db.dropUser('joezhou');

3. Mongo集合

心法:Mongo 集合就是 Mongo 文档组,类似于 RDBMS 中的 table 表格,当第一个文档插入时,集合就会被创建。

Mongo 内置集合

  • system.users:存储 Mongo 用户相关信息的集合。
  • system.version:存储 Mongo 版本相关信息的集合。

武技:测试 Mongo 集合常用操作

// 查看当前数据库中的全部集合
show collections;

// 创建集合
db.createCollection("test_collection");

// 创建容量固定的集合: 当达到最大值时,它会自动覆盖最早的文档,适用于日志等数据
db.createCollection("fix_collection", {
    capped: true, // 固定容量
    size: 10000, // 最大不超过10000字节
    max: 1000 // 最多不超过1000个文档
});

// 删除指定的集合
db.fix_collection.drop();
db.test_collection.drop();

4. Mongo文档

心法:Mongo 文档采用类似于 JSON 结构的 BSON(Binary JSON)结构存储数据,底层同样为键值对结构。

Mongo 文档特点

  • Mongo文档中的键值对是有序的。
  • Mongo文档中区分类型和大小写。
  • Mongo文档中不能有重复的键。

武技:测试 Mongo 文档的常用操作

// 向当前数据库的user集合中插入一条数据: 集合自动创建 
// 允许插入相同文档,但每条文档的 `_id` 值不同
db.user.insertOne({
    id: 1,
    name: "joezhou",
    age: 16
});

// 向当前数据库的user集合中插入多条数据
// 集合不存时自动创建
db.user.insertMany([
    {id: 2, name: "lucky", age: 16},
    {id: 3, name: "zhaosi", age: 40}
]);

// 修改满足条件的第一个文档
db.user.updateOne(
    {id: 1}, // 修改条件
    {$set: {name: 'tom', age: 18}} // 修改的内容
);

// 修改满足条件的第一个文档
// `{upsert: true}`: 若满足条件的文档不存在则执行添加操作
// `{upsert: false}`: 若满足条件的文档不存在则不执行任何操作,默认值
db.user.updateOne(
    {id: 4}, // 修改条件
    {$set: {name: 'jack', age: 30}}, // 修改的内容
    {upsert: true} // 若满足条件的文档不存在则执行添加操作
);

// 修改满足条件的全部文档
db.user.updateMany(
    {id: 1}, // 修改条件
    {$set: {name: 'guangkun', age: 50}} // 修改的内容
);

// 删除当前数据库的user集合中,符合条件的第一条文档
db.user.deleteOne({id: 1});

// 删除当前数据库的user集合中,符合条件的全部文档
db.user.deleteMany({id: 1});

// 查询全部文档
db.user.find();

// 查询全部文档,按id升序查询
db.user.find().sort({id: 1});

// 查询全部文档,按age降序查询
db.user.find().sort({age: -1});

// 跳过前4条文档后,查询2条文档
// 查询第N页,每页M条公式: .skip( (N-1)*M ).limit(M)
db.user.find().skip(4).limit(2);

// 查询ID为3,且姓名为lucky的全部文档
// 第2个参数用来设置结果集中展示那些列,1表示展示,默认展示全部列
db.user.find({id: 3, name: "lucky"}, {name: 1, id: 1});

// 查询ID为3,且姓名为lucky的第一个文档
db.user.findOne({id: 3, name: "lucky"});

// 查询ID为3,或姓名为lucky的全部文档
db.user.find({$or: [{id: 3}, {name: "lucky"}]});

// 查询ID为3,或姓名为lucky的第一个文档
db.user.findOne({$or: [{id: 3}, {name: "lucky"}]});

// 查询姓名符合正则的全部文档
db.user.find({name: /lu/});

// 查询姓名符合正则的第一个文档
db.user.findOne({name: /lu/});

// 查询年龄小于24的全部文档
db.user.find({age: {$lt: 24}});

// 查询年龄小于24的第一个文档
db.user.findOne({age: {$lt: 24}});

S02. Mongo项目整合

心法:org.springframework.data.mongodb.repository.MongoRepository<实体类, 主键类型> 是 Spring Data MongoDB 提供的一个接口数据访问接口,旨在简化在 Spring 应用中对 Mongo 的操作,使得开发者可以像操作传统数据库一样方便地操作 Mongo 中的数据。

武技:创建 v3-7-ssm-mongodb/test 子项目

  1. 添加三方依赖:
<!--三方依赖-->
<dependencies>
    <!---spring-boot-starter-data-mongodb-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
	<!--spring-boot-starter-web-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!--spring-boot-starter-test-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
	</dependency>
</dependencies>
  1. 开发主配文件:
server:
  port: 13701
spring:
  data:
    mongodb:
      host: 192.168.40.77 # mongodb的连接地址
      port: 27017 # mongodb的连接端口号
      database: mongo # mongodb的连接的数据库
      username: joezhou # mongodb账号
      password: joezhou # mongodb密码
  1. 开发启动类:
package com.joezhou;

/** @author 周航宇 */
@SpringBootApplication
public class MongoApp {
    public static void main(String[] args) {
        SpringApplication.run(MongoApp.class, args);
    }
}

E01. 开发Mongo实体类

Mongo 实体类常用注解如下

注解描述
@Document标记实体类以映射 Mongo 文档
@Id标记属性以映射 Mongo 文档的主键
@Indexed标记属性以映射 Mongo 文档的索引字段
package com.joezhou.mongo;

/** @author 周航宇 */
@Data
@Document
public class History implements Serializable {
    @Id
    private Long id;
    @Indexed
    private Long userId;
    private String nickname;
    @Indexed
    private Long productId;
    private String productTitle;
    private String productCover;
    private Double productPrice;
    private LocalDateTime created;
    private LocalDateTime updated;
}

E02. 开发数据接口

  1. 开发数据层接口:
package com.joezhou.dao;

/** @author 周航宇 */
public interface HistoryRepository extends MongoRepository<History, Long> {

    /**
     * 根据用户主键获取该用户的浏览记录的数量
     *
     * @param userId 用户主键
     * @return 浏览记录数量
     */
    int countByUserId(Long userId);

    /**
     * 根据商品名称模糊查询浏览记录
     *
     * @param productTitle 商品名称
     * @return 浏览记录列表
     */
    List<History> findByProductTitleContaining(String productTitle);

    /**
     * 根据商品名称分页模糊查询浏览记录
     *
     * @param productTitle 商品名称
     * @param pageable     分页条件
     * @return 浏览记录列表
     */
    Page<History> findByProductTitleContaining(String productTitle, Pageable pageable);

    /**
     * 根据商品名称分页模糊查询浏览记录,按 ID 降序
     *
     * @param productTitle 商品名称
     * @param pageable     分页条件
     * @return 浏览记录列表
     */
    Page<History> findByProductTitleContainingOrderByIdDesc(String productTitle, Pageable pageable);
}
  1. 测试数据层接口:
package dao;

/** @author 周航宇 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MongoApp.class)
public class HistoryRepositoryTest {

    @Autowired
    private HistoryRepository historyRepository;

    @Test
    public void insert() {
        // 单条插入:重复添加视为修改
        History history$01 = new History();
        history$01.setId(1L);
        history$01.setUserId(1L);
        history$01.setNickname("赵四");
        history$01.setProductId(1L);
        history$01.setProductTitle("小米手机");
        history$01.setProductCover("小米手机.jpg");
        history$01.setProductPrice(1000.00);
        history$01.setCreated(LocalDateTime.now());
        history$01.setUpdated(LocalDateTime.now());
        System.out.println(historyRepository.save(history$01));

        // 批量插入(循环插入100条数据)
        LocalDateTime now = LocalDateTime.now();
        List<History> histories = new ArrayList<>();
        for (long i = 1000; i <= 1100; i++) {
            History history = new History();
            history.setId(i);
            history.setUserId(i);
            history.setNickname("用户" + i + "号");
            history.setProductId(i);
            history.setProductTitle("小米手机" + i + "号");
            history.setProductCover("小米手机" + i + "号.jpg");
            history.setProductPrice(i * 10.0);
            history.setCreated(now);
            history.setUpdated(now);
            histories.add(history);
        }
        for (History history : historyRepository.saveAll(histories)) {
            System.out.println(history);
        }
    }

    @Test
    public void select() {

        // 按主键查询一条文档记录是否存在,存在返回true,不存在返回false
        System.out.println(historyRepository.existsById(1000L));

        // 按主键查询一条文档记录
        Optional<History> optional$50 = historyRepository.findById(1000L);
        if (optional$50.isPresent()) {
            System.out.println(optional$50.get());
        } else {
            System.out.println("数据不存在");
        }

        // 按主键列表查询多条文档记录
        Iterable<History> deptDocs = historyRepository.findAllById(List.of(1000L, 1001L, 1002L));
        deptDocs.forEach(System.out::println);

        // 查询全部文档记录
        historyRepository.findAll().forEach(System.out::println);
    }

    @Test
    public void count() {

        // 查询全部文档数量
        System.out.println(historyRepository.count());

        // 查询1号用户的全部文档数量
        System.out.println(historyRepository.countByUserId(1L));
    }

    @Test
    public void search() {

        // 查询商品名包含 "9" 的全部文档
        historyRepository.findByProductTitleContaining("9").forEach(System.out::println);
    }

    @Test
    public void page() {
        int page = 1;
        int size = 2;
        // 查询第1页的两条数据(ES分页的page是从0开始的)
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<History> page01 = historyRepository.findByProductTitleContaining("9", pageable);
        System.out.printf("page=%s, size=%s, pages=%s, total=%s\n",
                page01.getNumber(),
                page01.getSize(),
                page01.getTotalPages(),
                page01.getTotalElements());
        page01.getContent().forEach(System.out::println);
        System.out.println();

        Page<History> page02 = historyRepository.findByProductTitleContainingOrderByIdDesc("9", pageable);
        System.out.printf("page=%s, size=%s, pages=%s, total=%s\n",
                page02.getNumber(),
                page02.getSize(),
                page02.getTotalPages(),
                page02.getTotalElements());
        page02.getContent().forEach(System.out::println);
    }

    @Test
    public void delete() {

        // 按条件删除文档
        History history = new History();
        history.setId(1000L);
        historyRepository.delete(history);

        // 按主键删除一条文档
        historyRepository.deleteById(1001L);
        // 按主键列表批量删除多条文档
        historyRepository.deleteAllById(List.of(1002L, 1003L, 1004L));
    }
}

Java道经第3卷 - 第7阶 - MongoDB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值