mongodb介绍
MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是可以应用于各种规模的企业、各个行业以及各类应用程序的开源数据库。基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
MongoDB是一个文档数据库。这里对比RDBMS来介绍Mongodb。
传统的关系数据库一般由数据库(database)、表(table)、记录(record)三个层次概念组成,MongoDB是由数据库(database)、集合(collection)、文档对象(document)三个层次组成。MongoDB的collection对应关系型数据库里的表,但是集合中没有列、行和关系概念
1、基本概念
1.1、数据库:
在Mongodb中,db表示数据库,对应RDBMS的数据库。
'show dbs'
可以查看所有数据库
'use 数据库名称'
表示使用指定数据库
1.2、集合:
集合Collections对应 RDBMS的表
'show collections'
可以查看当前db下所有集合
1.3、文档:
文档对应RDBMS的表中的数据
db.'集合名称'.find();
表示查询当前集合下所有文档数据。其中文档是以BSON进行存储,BSON是一种二进制序列化格式,用于在MongoDB中存储文档和进行远程过程调用。参考BSON介绍
2、特性
2.1、高性能:
- 对嵌入式数据模型的支持减少了数据库系统上的I / O操作
- 索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键
2.2、丰富的查询语言:
- MongoDB支持丰富的查询语言以支持读写操作(CRUD)
- 数据聚合
- 文本搜索和地理空间查询
2.3、高可用:
- MongoDB的复制工具(称为副本集)提供 自动故障转移 和 数据冗余。副本集是一组维护相同数据集合的 mongod实例,提供了冗余和提高了数据可用性。
2.4、水平拓展:
- MongoDB提供水平可伸缩性作为其核心 功能的一部分:数据分片切割将数据分布在一个集群的机器上。
- 从3.4开始,MongoDB支持基于分片键创建数据区域。在平衡群集中,MongoDB仅将区域覆盖的读写定向到区域内的那些分片。
2.5、支持多种存储引擎:
- WiredTiger存储引擎(包括对静态加密的支持 )
- 内存存储引擎
- PS: MongoDB提供可插拔的存储引擎API,允许第三方为MongoDB开发存储引擎
3、mongodb基本命令使用
使用mongodb自带的mongodb Shell来学习mongodb提供的基本CRUD操作
3.1、进入mongodb安装路劲的bin目录
mongo --host 127.0.0.1 --port 27017 // 使用命令连接mongodb并使用mongodb Shell
show dbs //查询所有数据库
use mytest //使用指定的数据库
db.myCollection01.insertOne( {x : 1} ); //创建文档{x : 1} 如果没有数据库和集合,那么会默认创建集合和数据库,并插入文档到集合中
MongoDB中的所有写操作都是单个文档级别的原子操作。 有关MongoDB和原子性的更多信息,请参见原子性
3.2、新增操作
db.inventory.insertOne(
{ item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
) //插入文档到集合inventory中,如果集合不存在,那么会默认创建集合
db.inventory.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]); //插入多条文档
3.3、查询操作
db.inventory.find( {} ) // SELECT * FROM inventory
db.inventory.find( { status: "D" } ) // SELECT * FROM inventory WHERE status = "D"
db.inventory.find( { status: { $in: [ "A", "D" ] } } ) // SELECT * FROM inventory WHERE status in ("A", "D")
db.inventory.find( { status: "A", qty: { $lt: 30 } } ) // SELECT * FROM inventory WHERE status = "A" AND qty < 30
db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } ) // SELECT * FROM inventory WHERE status = "A" OR qty < 30
db.inventory.find( {
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} ) // SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")
3.4、删除操作
db.inventory.deleteMany({}) // 从inventory收集中删除所有文档
db.inventory.deleteMany({ status : "A" }) // 从状态字段等于“ A”的inventory集合中删除所有文档
db.inventory.deleteOne( { status: "D" } ) // 删除状态为“ D”的第一个文档
即使从集合中删除所有文档,删除操作也不会删除索引。
3.5、修改操作
先使用插入多条数据插入示例数据,在示例数据的基础上进行更新操作
db.inventory.insertMany( [
{ item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status: "A" },
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status: "A" },
{ item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" }, status: "P" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
{ item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" }
] ); //插入多条示例文档
db.inventory.updateOne(
{ item: "paper" },
{
$set: { "size.uom": "cm", status: "P" },
$currentDate: { lastModified: true }
}
)
// 使用$set 运算符将size.uom字段的值更新为“ cm”,将状态字段的值更新为“ P”,使用$currentDate运算符将lastModified字段的值更新为当前日期。 如果lastModified字段不存在,则$currentDate将创建该字段
要替换_id字段以外的文档的全部内容,请将一个全新的文档作为第二个参数传递给db.collection.replaceOne()。
在替换文档中,由于_id字段是不可变的,因此可以省略_id字段。如果确实包含_id字段,则它必须与当前值具有相同的值
db.inventory.replaceOne(
{ item: "paper" },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)//替换了inventory集合中的第一个文件,其中项为"paper":
文档设置后,您将无法更新_id字段的值,也无法将现有文档替换为具有不同_id字段值的替换文档
除以下情况外,MongoDB会在执行写操作后保留文档字段的顺序:
- _id字段始终是文档中的第一个字段。
- 包含字段名称renaming 的更新可能导致文档中字段的重新排序
如果updateOne(), updateMany(), or replaceOne() 包含upsert:true,并且没有文档与指定的过滤器匹配,则该操作将创建一个新文档并将其插入。 如果存在匹配的文档,则该操作将修改或替换一个或多个匹配的文档。
4、java操作mongodb
Spring Data的MongoRepository和MongoTemplate。
4.1、MongoRepository依次实现了PagingAndSortingRepository、CrudRepository、Spring Data的Repository
- Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类
- CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
- PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
- MongoRepository: 继承 PagingAndSortingRepository,实现一组 mongodb规范相关的方法 //自定义的 XxxxRepository 需要继承 MongoRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力(CURD的操作功能)
MongoRepository的缺点是不够灵活,MongoTemplate正好可以弥补不足
4.2、MongoTemplate采用了模板方法设计模式,封装了代码调用获取链接执行语句等操作。
- MongoTemplate实现了interface MongoOperations。
- MongoDB documents和domain classes之间的映射关系是通过实现了MongoConverter这个interface的类来实现的。
- MongoTemplate提供了非常多的操作MongoDB的方法。 它是线程安全的,可以在多线程的情况下使用。
- MongoTemplate实现了MongoOperations接口, 此接口定义了众多的操作方法如"find", "findAndModify", "findOne", "insert", "remove", "save", "update" and "updateMulti"等。
- MongoTemplate转换domain object为DBObject,缺省转换类为MongoMappingConverter,并提供了Query, Criteria, and Update等流式API
MongoTemplate的类结构如下:其中有两个重要的操作类
-
Criteria类:封装所有的语句,以方法的形式查询。
-
Query类:将语句进行封装或者添加排序之类的操作。
从上面类结构可以知道使用 Query 构造方法,传入一个Criteria类,可以构建一个简单的过滤器去进行CRUD。
下面介绍使用MongoTemplate进行简单的crud
//yml配置
spring:
data:
mongodb:
uri: mongodb://localhost:27017/mytest
//测试代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongodbUtil {
@Autowired
private MongoTemplate mongoTemplate;
//
// @Autowired
// private PersonRepository personRepository;
/**
* insert document
*
* {
* "name" : "cgg",
* "info": {
* "age" : 18
* }
* }
*/
@Test
public void testInsert() {
Map<String, Object> data = Maps.newHashMap();
Map<String, Object> info = Maps.newHashMap();
data.put("name", "cgg");
info.put("age" , 18);
data.put("info", info);
mongoTemplate.insert(data, "inventory");
System.out.println(mongoTemplate.findAll(Map.class, "inventory"));
}
/**
* select * from inventory where status in ('A')
*/
@Test
public void testQuery() {
Query query = new Query();
query.addCriteria(Criteria.where("status").in("A"));
mongoTemplate.executeQuery(query, "inventory", System.out::println);
}
/**
* update inventory set qty = 55 where status in ('A')
*/
@Test
public void testUpdate() {
Query query = new Query();
query.addCriteria(Criteria.where("status").in("A"));
Update update = new Update();
update.set("qty", 55);
mongoTemplate.updateMulti(query, update, "inventory");
System.out.println(mongoTemplate.findAll(Map.class, "inventory"));
}
/**
* delete from inventory where name = 'cgg'
*/
@Test
public void testDelete() {
Query query = new Query();
query.addCriteria(Criteria.where("name").in("cgg"));
mongoTemplate.remove(query, "inventory");
testQuery();
}
}
5、mongodb客户端
mongodb客户端有很多,这里推荐大家使用Robo,支持查看各种语言的查询语句、结果集转换,筛选查询,查看查询计划等等
Robo下载地址Robo studio
6、mongodb对接多数据源
Springboot项目整合mongodb多数据源配置
#yml配置
spring:
data:
mongodb:
one:
uri: mongodb://localhost:27017/mytest
two:
uri: mongodb://localhost:27018/mytest
/**
* mongodb 多数据源属性配置类
*
* @author cgg
* @since 2021/07/21
* @version 1.0
*/
@Configuration
public class MongoConfiguration {
@Bean(name = "oneMongoProperties")
@Primary
@ConfigurationProperties(prefix = "spring.data.mongodb.one")
public MongoProperties masterMongoProperties() {
return new MongoProperties();
}
@Bean(name = "twoMongoProperties")
@ConfigurationProperties(prefix = "spring.data.mongodb.two")
public MongoProperties twoMongoProperties() {
return new MongoProperties();
}
}
/**
* mongodb://localhost:27017/mytest 数据源配置类 完成MongoTemplate的注入
*
* @author cgg
* @since 2021/07/21
* @version 1.0
*/
@Configuration
@EnableMongoRepositories(mongoTemplateRef = "oneMongo")
public class OneMongoTemplateConf {
private final MongoProperties mongoProperties;
public OneMongoTemplateConf(@Qualifier("oneMongoProperties") MongoProperties mongoProperties) {
this.mongoProperties = mongoProperties;
}
@Bean(name = "oneMongo")
@Primary
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDatabaseFactory(mongoProperties));
}
@Bean(name = "oneMongoFactory")
@Primary
public MongoDatabaseFactory mongoDatabaseFactory(MongoProperties mongoProperties) {
return new SimpleMongoClientDatabaseFactory(mongoProperties.getUri());
}
}
/**
*
* mongodb://localhost:27018/mytest 数据源配置类 完成MongoTemplate的注入
*
* @author cgg
* @since 2021/07/21
* @version 1.0
*/
@Configuration
@EnableMongoRepositories(mongoTemplateRef = "twoMongo")
public class TwoMongoTemplateConf {
private final MongoProperties mongoProperties;
public TwoMongoTemplateConf(@Qualifier("twoMongoProperties") MongoProperties mongoProperties) {
this.mongoProperties = mongoProperties;
}
@Bean(name = "twoMongo")
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDatabaseFactory(mongoProperties));
}
@Bean(name = "twoMongoFactory")
public MongoDatabaseFactory mongoDatabaseFactory(MongoProperties mongoProperties) {
return new SimpleMongoClientDatabaseFactory(mongoProperties.getUri());
}
}
/**
* mongodb 多数据源测试
*
* @author cgg
* @since 2021/07/21
* @version 1.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoTest {
@Autowired
@Qualifier("oneMongo")
private MongoTemplate oneMongoTemplate;
@Autowired
@Qualifier("twoMongo")
private MongoTemplate twoMongoTemplate;
/**
* 向 localhost:27017/mytest插入数据
*/
@Test
public void testOneMongoInsert() {
Map<String, Object> data = Maps.newHashMap();
Map<String, Object> info = Maps.newHashMap();
data.put("name", "cgg");
data.put("attr", "one");
info.put("age" , 18);
data.put("info", info);
oneMongoTemplate.insert(data, "inventory_27017");
System.out.println(oneMongoTemplate.findAll(Map.class, "inventory_27017"));
}
/**
* 向 localhost:27018/mytest插入数据
*/
@Test
public void testTwoMongoInsert() {
Map<String, Object> data = Maps.newHashMap();
Map<String, Object> info = Maps.newHashMap();
data.put("name", "cgg");
data.put("attr", "two");
info.put("age" , 18);
data.put("info", info);
twoMongoTemplate.insert(data, "inventory_27018");
System.out.println(twoMongoTemplate.findAll(Map.class, "inventory_27018"));
}
}