摘要: 原创出处 欢迎转载,保留摘要,谢谢!
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,所以代码于此处有些出入
spring-boot-study
org.example
1.0-SNAPSHOT
4.0.0
spring-boot-mongo
org.springframework.boot
spring-boot-starter-data-mongodb
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
test
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 。
因为实现了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 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 = userRepository.findById(1);
System.out.println(userDO.isPresent());
}
@Test // 根据 ID 编号数组,查询多条记录
public void testSelectByIds() {
Iterable users = userRepository.findAllById(Arrays.asList(1, 4));
users.forEach(System.out::println);
}
}