Spring Boot MongoDB入门

摘要: 原创出处 欢迎转载,保留摘要,谢谢!


1. 概述

官网介绍

Mongodb 是一个文档数据库,这意味着它将数据存储在类似 json 的文档中。 我们认为这是考虑数据最自然的方式,比传统的行 / 列模型更具表达能力和强大

1.1. 特点

1.1.1. 丰富的JSON文档

  • MongoDB都是以JSON格式存储数据

  • 支持数组和嵌套对象

  • 允许以灵活和动态的模式

1.1.2. 强大的查询语言

  • 丰富的表达式查询语言,允许按任何字段进行筛选和排序,无论该字段在文档中的嵌套程度如何。

  • 支持聚合和其他现代用例,如基于地理的搜索、图表搜索和文本搜索。

  • 查询本身就是 JSON,因此很容易组合,不需要连接字符串来动态生成 SQL 查询。

1.1.3. 支持所有关系型数据库的功能, 并且更强大

  • 所有ACID事务

  • 支持联接的方式查询

  • 以两种关系类型:引用和嵌套

贴一下人类语言的解释:-)

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

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

Mongo 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

1.2. MongoDB和mysql参照

MySQL MongoDB
库 Database 库 Database
表 Table 集合 Collection
行 Row 文档 Document
列 Column 字段 Field
joins 嵌入文档或者链接

在早期,在项目中 MongoDB 的 ORM 框架使用 Morphia 较多。随着 Spring Data MongoDB 的日趋完善,更为主流。

在 Spring Data MongoDB 中,有两种方式进行 MongoDB 操作:

  • Spring Data Repository 方式

  • MongoTemplate

1.3. 安装

单机版安装方式基本傻瓜式 MongoDB4.2.5版官网下载链接

1.4. 快速入门

目录结构

.
├── pom.xml
├── spring-boot-mongo
│   ├── README.md
│   ├── pom.xml
│   ├── spring-boot-mongo.iml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── shawn
│   │   │   │           ├── Application.java
│   │   │   │           ├── config
│   │   │   │           │   └── MongoDBConfig.java
│   │   │   │           ├── dataobject
│   │   │   │           │   └── UserDO.java
│   │   │   │           └── repository
│   │   │   │               └── UserRepository.java
│   │   │   └── resources
│   │   │       └── application.yml
│   │   └── test
│   │       └── java
│   │           └── UserRepositoryTest.java

示例代码

1.4.1. 引入依赖,由于我是一个子module,所以代码于此处有些出入

<?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">
    <parent>
        <artifactId>spring-boot-study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-boot-mongo</artifactId>

    <dependencies>
        <!-- 自动化配置 Spring Data Mongodb -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- 方便等会写单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
</project>

1.4.2. 启动类

package com.shawn;

import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author gaoxiang
 * @description TODO
 * @date 2020/4/2 15:38
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {

    }
}

1.4.3. 配置类

package com.shawn.config;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.Bean;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

/**
 * @author gaoxiang
 * @description MongoDB 配置类
 * @date 2020/4/2 16:11
 */
public class MongoDBConfig {
    // 目的,就是为了移除 _class field 。参考博客 https://blog.csdn.net/bigtree_3721/article/details/82787411
    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory,
                                                       MongoMappingContext context,
                                                       BeanFactory beanFactory) {
        // 创建 DbRefResolver 对象
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        // 创建 MappingMongoConverter 对象
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        // 设置 conversions 属性
        try {
            mappingConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class));
        } catch (NoSuchBeanDefinitionException ignore) {
        }
        // 设置 typeMapper 属性,从而移除 _class field 。
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        return mappingConverter;
    }
}
  • 通过在自定义 MappingMongoConverter Bean 对象,避免实体保存到 MongoDB 中时,会多一个 _class 字段,存储实体的全类名。

1.4.4. application.yml配置文件

spring:
  data:
    # MongoDB 配置项,对应 MongoProperties 类
    mongodb:
      host: 127.0.0.1
      port: 27017
      database: test
      # 上述属性,也可以只配置 uri

logging:
  level:
    org:
      springframework:
        data:
          mongodb:
            core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下,不建议开启。

1.4.5. UserDO类, 我因为引入了lombok,所以此处get/set方法就不用写了

package com.shawn.dataobject;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;

/**
 * @author gaoxiang
 * @description UserDO.java
 * @date 2020/4/2 16:24
 */
@Document(collection = "User")
@Getter
@Setter
@ToString
public class UserDO {
    /**
     * 用户信息
     */
    @Getter
    @Setter
    @ToString
    public static class Profile {
        /**
         * 昵称
         */
        private String nickname;
        /**
         * 性别
         */
        private Integer gender;
    }
    @Id
    private Integer id;
    /**
     * 账号
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 用户信息
     */
    private Profile profile;
}
  • 在 UserDO 类中,我们内嵌了一个 profile 属性,它是 Profile 类。这里仅仅作为示例,实际场景下,还是建议把 User 和 Profile 拆分开。

  • 推荐阅读 《你应该知道的 MongoDB 最佳实践》 文章。对于初用 MongoDB 的开发者,往往错误的使用内嵌属性,需要去理解一下。

1.4.6 UserRepository.java

package com.shawn.repository;

import com.shawn.dataobject.UserDO;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * @author gaoxiang
 * @description MongoRepository.java
 * @date 2020/4/2 16:37
 */
public interface UserRepository extends MongoRepository<UserDO, Integer> {

}

  • 第一个泛型设置对应的实体是 UserDO ,第二个泛型设置对应的主键类型是 Integer 。

  • 因为实现了MongoRepository 接口, Spring Data MongoDB会自动生成对应的CRUD的代码

  • mongoRepository类图如下:

  • 其实和Spring Data JPA的使用方式基本一致.

1.4.7 简单测试

// UserRepositoryTest.java

import com.shawn.Application;
import com.shawn.dataobject.UserDO;
import com.shawn.repository.UserRepository;
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 org.springframework.util.Assert;

import java.util.Arrays;
import java.util.Date;
import java.util.Optional;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test // 插入一条记录
    public void testInsert() {
        // 创建 UserDO 对象
        UserDO user = new UserDO();
        user.setId(1); // 这里先临时写死一个 ID 编号,后面演示自增 ID 的时候,在修改这块
        user.setUsername("shawn");
        user.setPassword("1234");
        user.setCreateTime(new Date());
        // 创建 Profile 对象
        UserDO.Profile profile = new UserDO.Profile();
        profile.setNickname("shawn.nickname");
        profile.setGender(1);
        user.setProfile(profile);
        // 存储到 DB
        userRepository.insert(user);
    }

    // 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。
    // 所以,这里仅仅是作为一个示例。
    @Test // 更新一条记录
    public void testUpdate() {
        // 查询用户
        Optional<UserDO> userResult = userRepository.findById(1);
        Assert.isTrue(userResult.isPresent(), "用户一定要存在");
        // 更新
        UserDO updateUser = userResult.get();
        updateUser.setUsername("yutou");
        userRepository.save(updateUser);
    }

    @Test // 根据 ID 编号,删除一条记录
    public void testDelete() {
        userRepository.deleteById(1);
    }

    @Test // 根据 ID 编号,查询一条记录
    public void testSelectById() {
        Optional<UserDO> userDO = userRepository.findById(1);
        System.out.println(userDO.isPresent());
    }

    @Test // 根据 ID 编号数组,查询多条记录
    public void testSelectByIds() {
        Iterable<UserDO> users = userRepository.findAllById(Arrays.asList(1, 4));
        users.forEach(System.out::println);
    }

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容