文章目录

MongoDB
1.关于MongoDB的理解
1.1 什么是MongoDB?
MongoDB是一个以JOSN为数据模型的文档的非关系型数据库,应用于数据库,专注处理海量数据!
特点:
1.建模灵活 2.json数据模型 3.横向扩展简单 4.大数据量存储 5.高并发
1.2 MongoDB优势
- 简单直观
以自然的方式来建模, 以直观的方式来与数据库交互,采用bson结构存储数据 - 结构灵活 弹性模式从容响应需求的变更
- 快速开发 做更多的事,写更少的代码
- 原生的高可用与易扩展
单机模式:开始与测试
复制集模式:数据量不大应用, 需要事务支持的应用
分片集群模式:大数据量应用
1.3 应用场景
从阿里云 MongoDB 云数据库上的用户看,MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例。
1.游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
2.物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
3.社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
4.物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
5.视频直播,使用 MongoDB 存储用户信息、礼物信息等
……
6.日志处理
国内外使用mongodb的公司
facebook
视觉中国、
哲思社区
东方航空公司的机票库存运价系统
台湾最大半导体厂商的产线数据平台
香港顶级银行的金融数据中台
京东
淘宝
…
1.4 数据类型
2.客户端指令
官方操作文档:https://docs.mongodb.com/manual/introduction/
2.1 插入文档
----------------------------插入数据------------------------------------
db.集合名.insert( 文档 ) : 往集合中插入一个文档或者多个
db.wolf.insert({id:1,name:"大飞",age:18} )
db.wolf.insert({id:2,name:"小飞",age:17} )
//当我们使用insert语句进行插入的时候,如果没有创建集合,则自动帮我们创建
//查看文档的所有数据
db.col.find()
//将数据定义为一个变量,然后进行插入的操作
document = ({
title:"数据变量",
name:"有点意思哈",
hh:"还不够你呢"
})
db.col.insert(document)
//插入单条数据
db.col.insertOne({
title:"菜鸟教学0",
name:"你真棒1",
hh:"不用双引号1"
})
//插入多条数据,用[]
db.col.insertMany([{
title:"菜鸟教学2",
name:"你真棒2",
hh:"不用双引号1"
},
{
title:"菜鸟教学3",
name:"你真棒1",
hh:"不用双引号1"
}
])
//使用数组,插入多条数据
//1.创建一个数组
var arr = [];
//2.将数据放在数组中
for(var i=1; i<= 2000; i++){
arr.push({num:i});
}
//3.一次性insert到集合之中
db.numbers.insert(arr)
2.2 更新文档
db.collection.update(
<query>, ---查询条件
<update>, ----更新的操作
{
upsert: <boolean>, -----可选:true:不存在更新记录,则进行插入
multi: <boolean>, ------false:只找到第一条记录,true:找到全部的记录
writeConcern: <document>----可选:设置抛出异常级别
}
)
//更新文档-------------------------------------------
//插入数据
db.col2.insert({
title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
//修改数据
db.col2.update({title:"MongoDB 教程"},{$set:{title:"大话西游"}})
//把一个带有name=dafei的文档,修改其age值为30
db.wolf.updateOne({name:"大飞"},{$set:{age:3}})
//修改所有name=dafei的文档,修改其name=大飞,age=20
db.wolf.updateMany({name:"大飞"}, {$set:{name:"dafei",age:20}})
//修改所有的文档,修改其name=xxx,age=10
db.wolf.updateMany({}, {$set:{name:"xxx",age:20}})
//以格式化的方式查看所有文档的信息
db.col2.find().pretty()
//set方法,通过传入的文档来替代已有的文档
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
db.col2.save({
"_id" : ObjectId("60d3e82aa91c00002d000905"),
"title" : "MongoDB",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "Runoob",
"url" : "http://www.runoob.com",
"tags" : [
"mongodb",
"NoSQL"
],
"likes" : 110
})
2.3 删除文档
//删除文档--------------------------------
db.collection.remove(
<query>,
{
justOne: <boolean>, 可选:true:只删除一个 false:删除满足条件的
writeConcern: <document> 可选:抛出异常的级别
}
)
db.col2.remove({title:"大话西游"})
//删除集合下面的所有文档
db.col2.deleteMany({})
//删除满足条件的全部文档
db.col2.deleteMany({title:"大话西游"})
//删除一个满足条件文档
db.col2.deleteOne({title:"大话下雨"})
2.4 查询文档
2.4.1 数据准备
---------------------------查询操作练习---------------------------------------
db.users.insert({"id":NumberLong(1),"name":"dafei","age":NumberInt(18)})
db.users.insert({"id":NumberLong(2),"name":"xiaofei","age":NumberInt(20)})
db.users.insert({"id":NumberLong(3),"name":"zhang quan dan","age":NumberInt(33)})
db.users.insert({"id":NumberLong(4),"name":"zhang kun","age":NumberInt(26)})
db.users.insert({"id":NumberLong(6),"name":"cai xv kun","age":NumberInt(29)})
db.users.insert({"id":NumberLong(7),"name":"jia nai liang","age":NumberInt(25)})
db.users.insert({"id":NumberLong(8),"name":"fu rong wang","age":NumberInt(28)})
db.users.insert({"id":NumberLong(9),"name":"wang da","age":NumberInt(31)})
db.users.insert({"id":NumberLong(10),"name":"da wang","age":NumberInt(32)})
db.users.insert({"id":NumberLong(11),"name":"will","age":NumberInt(26)})
//数据准备
db.col.insert({
title: 'PHP 教程',
description: 'PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['php'],
likes: 200
})
db.col.insert({title: 'Java 教程',
description: 'Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['java'],
likes: 150
})
db.col.insert({title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['mongodb'],
likes: 100
})
//普通查询文档
//查询文档----------------------------------------------
//以格式化的方式查看所有文档的信息
db.col.find().pretty()
//and条件
db.col.find({title:"菜鸟教学",name:"你真棒"}).pretty()
//or条件
db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
db.col.find(
{$or:[{title:"菜鸟教学",name:"你真棒"}]}
)
2.4.2 排序
1.排序
db.集合名.find(query, projection). 1:升序 -1:降序
查询所有用户按年龄排序
db.users.find({}).sort({age:1})
db.users.find({}).sort({age:-1})
db.users.find({}).sort({age:1,id:1})
//排序-------------------
//-1 :表示降序 1:升序
db.col.find({},{likes:1,_id:0}).sort({"likes":-1})
//注意:skip,sort,limit一起执行的时候,先sort,skip,limit
2.4.3 分页
2.分页 db.集合.find({}).skip(n).limit(m) n:表示跳过的索引 m:每页的条数
db.users.find({}).skip(3).limit(3)
db.users.find().skip((currentPage-1) * pageSize).limit(pageSize)
//limi与skip方法(数据的分页)-----------------------
//limit ----限制满足条件的记录
//第二个{},表示显示的列名,_id:0 表示不显示,
db.col.find({},{name:1,_id:0}).limit(2)
db.col.find({}).limit(2)
//skip 跳过指定数量的记录
db.col.find({}).skip(1).limit(2)
2.4.4 比较运算符
3.比较运算符 语法 ->db.集合名. find( { 字段: {比较操作符: 值, ...} } )
查询age > 30的用户
db.users.find(
{age:{$gt:30}}
)
查询名字为 dafei 或xiaofei用户
db.users.find(
{name:{$in:["dafei","xiaofei"]}}
)
判断判断指定列是否存在
db.users.find(
{name:{$exists:true}}
)
2.4.5 逻辑运算符
语法 -> find( {逻辑操作符: [条件1, 条件2, ...]} )
&& 与 - $and
|| 或 - $or
! 非 - $not
//$type操作符--------------------------------------------
//用于获取对应的字段类型,每个数据类型对应一个数字
//获取title为string类型的全部数据
db.col.find({title:{$type:2}})
db.col.find({title:{$type:"string"}})
// ">" $gt
db.col.find({likes:{$gt:100}})
// ">=" $gte
db.col.find({likes:{$gte:100}})
// "<" $lt
db.col.find({likes:{$lt:150}})
// "<=" $lte
db.col.find({likes:{$lte:150}})
//使用 “>” "<" 联合查询
db.col.find({likes:{$gt:100,$lt:200}})
查年龄在28 到 30间的用户信息
//sql: select * from users where age >= 28 and age <=30
db.users.find(
{age:{$gte:28, $lte:30}}
)
db.users.find(
{
$and:[{age:{$gte:28}},{age:{$lte:30}}]
}
)
//and or联合使用
db.col.find({likes:{$gt:50},$or:[{title:"菜鸟教学",name:"你真棒"}]})
查看年龄小于28或者年龄大于30用户信息
//sql: select * from users where age <28 or age >30
db.users.find(
{$or: [{age: {$lt: 28}}, {age: {$gt:30}}]}
)
查看年龄等于28或者等于30用户信息
//sql: select * from users where age =28 or age =30
db.users.find(
{$or: [{age:28}, {age: 30}]}
)
2.4.6 模糊查询
db.集合.find( { 列: {$regex: /关键字/} } )
//sql: select * from user where name like '%关键字%'
db.集合.find({列: {$regex: /关键字/}}) / / 表示正则对象(js)
db.集合.find({列: {$regex: "关键字"}})
{name:/xxx/} --->%xxx%
{name:/^xxx/} --->xxx%
{name:/xxx$/} --->%xxx
{name:/xxx/i} 忽略大小写
查询name带有fei的用户信息
//sql: select * from users where name like '%fei%'
db.users.find(
{name: {$regex:/fei/ }}
)
查name中包含fei字样,并且年龄在28 到 30间的用户信息,
//sql: select * from users where name like '%fei%' and age >= 28 and age <=30
db.users.find(
{$and: [{name: {$regex:/fei/}}, {age: {$gte:28, $lte:30}}]}
)
2.5 关于数据库
//创建并且进入到某个数据库-----------------------------
use runoob
db
//查看所有数据库
show dbs
//插入数据到某个集合中
db.runoob.insert({"name":"菜鸟教程"})
//删除当前所在的数据库
db.dropDatabase()
2.6 关于集合的操作
//创建集合---------------------------------------
db.createCollection("a")
//查看集合
show collections
//在mongodb中不需要创建集合,当你插入数据的时候,自动创建集合
db.mycol2.insert({"name":"菜鸟教学"})
//删除集合-------------------------------------------
//查看集合
show collections
//删除集合
db.mycol2.drop()
2.7 关于索引
//索引--------------------------------------------------
// 创建索引,单个字段,多个字段(符合索引)
//db.collection.createIndex(keys, options)
db.col.createIndex({"title":1})
/*
Parameter Type Description
background (Boolean): 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false。
unique (Boolean): 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name (string): 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups (Boolean): 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
sparse (Boolean): 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds (integer): 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
v (index version): 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weights (document): 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language (string): 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_override (string): 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.
*/
//1、查看集合索引
db.col.getIndexes()
db.users.getIndexes()
//2、查看集合索引大小
db.col.totalIndexSize()
//3、删除集合所有索引
db.col.dropIndexes()
//4、删除集合指定索引
db.col.dropIndex("title_1")
db.col.createIndex({"createDate": 1},{expireAfterSeconds: 10})
2.8 聚合函数
//聚合函数-------------------------------------------
//group根据by_user字段进行分组,显示的字段num_tutorial,sum:1:求和
//数据准备
db.mycol.insert([{
_id: ObjectId("7df78ad8902c"),
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
_id: ObjectId("7df78ad8902d"),
title: 'NoSQL Overview',
description: 'No sql database is very fast',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 10
},
{
_id: ObjectId("7df78ad8902e"),
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
url: 'http://www.neo4j.com',
tags: ['neo4j', 'database', 'NoSQL'],
likes: 750
}])
// select by_user, count(*) from mycol group by by_user
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
/*
$sum 计算总和。
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
$avg 计算平均值
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min 获取集合中所有文档对应值得最小值。
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max 获取集合中所有文档对应值得最大值。
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
$push 在结果文档中插入值到一个数组中。
db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本。
db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根据资源文档的排序获取第一个文档数据。
db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last 根据资源文档的排序获取最后一个文档数据
db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])
*/
2.9 管道
什么是管道?
就是将上次的执行的结果,作为下一个执行语句的参数,相当于sql语句里面嵌套了子查询
//管道-----------------------
//将一个命令的操作结果,作为参数给下一个
db.mycol.aggregate(
{ $project : {
_id : 0 ,
title : 1 ,
author : 1
}});
//$match实例
db.mycol.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
//3.$skip实例
db.article.aggregate(
{ $skip : 5 });
3.集成SpringBoot&Jpa
3.1 导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--spring boot data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
3.2 配置
#application.properties
#配置数据库连接
#格式: mongodb://账号:密码@ip:端口/数据库?认证数据库
#spring.data.mongodb.uri=mongodb://root:admin@localhost/mongodemo?authSource=admin
spring.data.mongodb.uri=mongodb://localhost/mongodemo
# 配置MongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=debug
3.3 domain
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
@Document("users") //设置文档所在的集合
public class User {
//文档的id使用ObjectId类型来封装,并且贴上@Id注解,
// 自动映射为_id 自动封装ObjectId
@Id
private String id;
private String name;
private Integer age;
private List<String> hobby = new ArrayList<>();
}
3.4 了解Spring-data -> jpa规范
https://spring.io/projects/spring-data
打开spring的官方文档,我们可以发现,他定义了一套专门适配Spring的持久层规范,然后出了实现了操作各个数据库的持久层的规范框架
因此我们只要遵循jpa规范即可,就可使用对应的Spring-data-jpa规范操作数据库
Spring-Data规范
3.5 CRUD
持久层
public interface UserMongoRespority extends MongoRepository<User,String> {
List<User> findByName(String name);
}
业务层
public interface IUserService {
void save(User user);
void delete(String id);
void update(User user);
User get(String id);
List<User> list();
}
@Service
public class UserServiceImpl implements IUserService{
@Autowired
private UserMongoRespority userRepository;
@Override
public void save(User user) {
userRepository.save(user);
}
@Override
public void delete(String id) {
userRepository.deleteById(id);
}
@Override
public void update(User user) {
userRepository.save(user);
}
@Override
public User get(String id) {
return userRepository.findById(id).get();
}
@Override
public List<User> list() {
return userRepository.findAll();
}
}
测试类
@SpringBootTest
public class UserTest {
@Autowired
private IUserService userService;
@Autowired
private UserMongoRespority userMongoRespority;
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void testSave(){
User user = new User();
user.setName("dafei");
user.setAge(18);
userService.save(user);
}
@Test
public void testUpdate(){
User user = new User();
user.setId("5de507fca0852c6c7ebc1eac");
user.setId("21");
user.setName("dafei2222");
user.setAge(18);
userService.update(user);
}
@Test
public void testDelete(){
userService.delete("5de507fca0852c6c7ebc1eac");
}
@Test
public void testGet(){
System.out.println(userService.get("5de507fca0852c6c7ebc1eac"));
}
@Test
public void testList(){
System.out.println(userService.list());
}
@Test
public void testName(){
System.out.println(userMongoRespority.findByName("dafei"));
}
}
3.6 提出问题
/* JPA规范:
* 根据spring的规范进行命名,才能实现代理对象
* 规范: 前缀 + 操作符 + 属性名 例如 find by name 方法使用驼峰命名法
*
* 问题1:
* 没有进行respority扫描(动态代理类),就能执行crud操作?
* spring-data-mongodb启动时,会会扫描所有继承MongoRespority接口的所有子接口
* 然后通过动态代理方式创建代理对象,然后交给Spring管理
* 问题2:
* 定义了一个接口方法,为什么没有实现,也会能起作用?
* 因为spring-data-mongodb遵循了jpa的规范,所以只要对应jpa的规范定义接口方法即可被自动实现
*
*
*
* jpa针对条件少的查询很方便,当条件多或者高级查询到额时候,非常不方便
* 当有条件多的时候或者高级查询的时候,我们一般使用MongoTemplete对象
*/
3.7 条件查询(非常麻烦)
// 查询所有name为dafei的文档
@Test
public void testQuery2() throws Exception {
// 构建限制条件 {"name": "dafei"}
Criteria criteria = Criteria.where("name").is("dafei");
// 创建查询对象
Query query = new Query();
// 添加限制条件
query.addCriteria(criteria);
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}
4.使用MongoTemplate
参考文章:SpringBoot 集成 Spring Data Mongodb 操作 MongoDB 详解 | 超级小豆丁 (mydlq.club)
4.1 配置
#配置mongodb数据库连接
#spring.data.mongodb.uri=mongodb://admin:123456@172.18.100.247:27017/employee/runnob 不行失败
spring.data.mongodb.authentication-database=admin ----默认的必须这么写,不然报错
spring.data.mongodb.database=runoob
spring.data.mongodb.host=172.18.100.247
spring.data.mongodb.port=27017
spring.data.mongodb.username=admin
spring.data.mongodb.password=123456
#配置mongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=debug
4.2 实体类
@Data
@ToString
@Accessors(chain = true)
public class User {
/**
* 使用 @MongoID 能更清晰的指定 _id 主键
*/
@MongoId
private String id;
private String name;
private String sex;
private Integer salary;
private Integer age;
@JsonFormat( pattern ="yyyy-MM-dd", timezone ="GMT+8")
private Date birthday;
private String remake;
private Status status;
}
@Data
@ToString
@Accessors(chain = true)
public class Status {
private Integer weight;
private Integer height;
}
4.3 集合操作
4.3.1 创建集合
@SpringBootTest
public class CreateCollectionServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void createCollection(){
mongoTemplate.createCollection("lin");
System.out.println(mongoTemplate.collectionExists("lin"));
}
/**
* 创建集合,并且设置固定大小的集合,以及集合文档的数量
* 固定大小的集合:
* 假设文档设置最大为2m,当达到最大值时。这条文档的数据将会覆盖第一条文档(最早的文档)的数据
*/
@Test
public void creatCollection2(){
//设置集合的参数
long size = 1024L;
long max = 5L;
//创建固定大小集合
CollectionOptions collectionOptions = CollectionOptions.empty()
//创建固定集合。固定集合是指着有固定大小的集合,当达到最大值时,他会自动覆盖最早的文档。
.capped()
//固定集合指定一个最大值,以千字节计(KB),如果capped为true,也需要指定该字段
.size(size)
//指定固定集合中包含文档的最大数量
.maxDocuments(max);
//执行创建集合
mongoTemplate.createCollection("user2",collectionOptions);
}
}
4.3.2 查询集合
@SpringBootTest
public class QueryCollectionServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 获取集合名称列表
*/
@Test
public void getCollectionName(){
System.out.println(mongoTemplate.getCollectionNames());
}
/**
* 检测集合是否存在
*/
@Test
public void CollectionExists(){
System.out.println(mongoTemplate.collectionExists("lin"));
}
}
4.3.3 删除集合
@SpringBootTest
public class RemoveCollectionServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void dropCollection(){
mongoTemplate.getCollection("test").drop();
System.out.println(mongoTemplate.collectionExists("test"));
}
}
4.4 视图操作
@SpringBootTest
public class ViewServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 创建视图
*/
@Test
public void createView(){
//设置视图名
String newViewName = "usersView";
//设置获取数据的集合名称
String collectionName = "users";
//定义视图的管道,设置视图显示内容的多个筛选条件
ArrayList<Bson> pipeline = new ArrayList<>();
//设置条件,用于筛选集合中文档的数据,只有符合条件的数据才会被映射到视图中
pipeline.add(Document.parse("{\"$match\":{\"sex\":\"女\"}}"));
//执行创建视图
mongoTemplate.getDb().createView(newViewName,collectionName,pipeline);
}
/**
* 删除视图
*/
@Test
public void dropView(){
if (mongoTemplate.collectionExists("userView")){
mongoTemplate.getDb().getCollection("userView").drop();
}
}
}
4.5 文档操作
4.5.1 文档插入
@SpringBootTest
public class InsertServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 插入一条数据
*/
@Test
public void insert(){
// 设置用户信息
User user = new User()
.setId("10")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("zhangsan")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
//插入数据
User users = mongoTemplate.insert(user, "users");
System.out.println(users);
}
/**
* 插入多个,放到list里面进行传入
*/
@Test
public void dropView(){
// 设置用户信息
User user = new User()
.setId("14")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("zhangsan1111")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
User user2 = new User()
.setId("15")
.setAge(23)
.setSex("男1")
.setRemake("无")
.setSalary(1500)
.setName("zhangsan2222")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
List<User> userList = new ArrayList<>();
userList.add(user);
userList.add(user2);
//插入数据
Collection<User> users = mongoTemplate.insert(userList, "users");
for (User u : users) {
System.out.println(u);
}
}
}
4.5.2 文档存储
@SpringBootTest
public class SaveServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 存储一条用户信息,存在则更更新
*/
@Test
public void save(){
// 设置用户信息
User user = new User()
.setId("13")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("zhangsa0000000000000000n")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
//插入数据
User users = mongoTemplate.save(user, "users");
System.out.println(users);
}
}
4.5.3 文档查询
@SpringBootTest
public class QueryServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 查询全部数据
*/
@Test
public void finaAll(){
List<User> users = mongoTemplate.findAll(User.class, "users");
for (User user : users) {
System.out.println(user);
}
}
/**
* 根据id查询集合中的文档
*/
@Test
public void finaById(){
User users = mongoTemplate.findById(10, User.class, "users");
System.out.println(users);
}
/**
* 查询符合条件的第一条数据
*/
@Test
public void finaOne(){
//创建条件对象
Criteria criteria = Criteria.where("age").is(22);
//创建查询对象,将条件对象添加到其中
Query query = new Query((CriteriaDefinition) criteria);
// Query query = new Query();
// query.addCriteria(criteria);
User user = mongoTemplate.findOne(query, User.class, "users");
System.out.println(user);
}
/**
* 根据条件查询符合条件的文档列表
*/
@Test
public void find(){
//创建条件对象
Criteria criteria = Criteria.where("sex").is("男");
//创建查询对象,将条件对象添加到其中
Query query = new Query((CriteriaDefinition) criteria);
// Query query = new Query();
// query.addCriteria(criteria);
List<User> userList = mongoTemplate.find(query, User.class, "users");
for (User user : userList) {
System.out.println(user);
}
}
/**
* 根据条件查询符合条件的文档列表,并且按照字段进行排序,限制指定数目
*/
@Test
public void finaByConditionAndSortLimit(){
//创建条件对象
Criteria criteria = Criteria.where("sex").is("男");
//创建查询对象,将条件对象添加到其中
Query query = new Query((CriteriaDefinition) criteria).with(Sort.by("age")).limit(2);
// Query query = new Query();
// query.addCriteria(criteria);
List<User> userList = mongoTemplate.find(query, User.class, "users");
for (User user : userList) {
System.out.println(user);
}
}
/**
* 根据条件查询符合条件的文档列表,并且按照字段进行排序,跳过指定数目
*/
@Test
public void finaByConditionAndSorSkip(){
//创建条件对象
Criteria criteria = Criteria.where("sex").is("男");
//创建查询对象,将条件对象添加到其中
Query query = new Query((CriteriaDefinition) criteria).with(Sort.by("age")).skip(1);
// Query query = new Query();
// query.addCriteria(criteria);
List<User> userList = mongoTemplate.find(query, User.class, "users");
for (User user : userList) {
System.out.println(user);
}
}
/**
* 根据存在指定字段名称的文档数据
*/
@Test
public void finaByExistField(){
//创建条件对象
Criteria criteria = Criteria.where("sex").exists(true);
//创建查询对象,将条件对象添加到其中
Query query = new Query((CriteriaDefinition) criteria).with(Sort.by("age")).skip(1);
// Query query = new Query();
// query.addCriteria(criteria);
List<User> userList = mongoTemplate.find(query, User.class, "users");
for (User user : userList) {
System.out.println(user);
}
}
/**
* 使用and关联多个查询条件,查询集合中的数据
*/
@Test
public void finaByAndCondition(){
//创建条件对象
Criteria criteriaSex = Criteria.where("sex").is("男");
Criteria criteriaAge = Criteria.where("age").is(22);
Criteria criteria = new Criteria().andOperator(criteriaAge, criteriaSex);
//创建查询对象,将条件对象添加到其中
Query query = new Query(criteria);
// Query query = new Query();
// query.addCriteria(criteria);
List<User> userList = mongoTemplate.find(query, User.class, "users");
for (User user : userList) {
System.out.println(user);
}
}
}
4.5.4 文档更新
@SpringBootTest
public class UpdateServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 更新集合中满足条件的第一条数据,----------------如果没有找到就{创建并且插入一个新的文档}
*/
@Test
public void update(){
//创建条件对象
Criteria criteria = Criteria.where("age").is(22);
//创建查询对象,添加条件对象到查询对象里面
Query query = new Query(criteria);
//创建更新对象,并且设置更新内容
Update update = new Update().set("age", 30).set("name", "jjj");
//执行更新,如果没有匹配的文档,则直接插入
UpdateResult updateResult = mongoTemplate.upsert(query, update, User.class, "users");
//输出结果信息
System.out.println(updateResult.getMatchedCount());
}
/**
* 更新满足条件的第一个
*/
@Test
public void updateFirst(){
//创建条件对象
Criteria criteria = Criteria.where("name").is("zhangsa0000000000000000n");
//创建查询对象,添加条件对象到查询对象里面(根据age进行升序排序)
Query query = new Query(criteria).with(Sort.by("age").ascending());
//创建更新对象,并且设置更新内容
Update update = new Update().set("age", 30).set("name", "66666666666");
//执行更新,如果没有匹配的文档,则直接插入
UpdateResult updateResult = mongoTemplate.updateFirst(query, update, User.class, "users");
//输出结果信息
System.out.println(updateResult.getMatchedCount());
}
/**
* 更新满足条件的所有数据
*/
@Test
public void updateMany(){
//创建条件对象
Criteria criteria = Criteria.where("age").is(22);
//创建查询对象,添加条件对象到查询对象里面(根据age进行升序排序)
Query query = new Query(criteria).with(Sort.by("age").ascending());
//创建更新对象,并且设置更新内容
Update update = new Update().set("age", 30).set("name", "111111111");
//执行更新,如果没有匹配的文档,则直接插入
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, User.class, "users");
//输出结果信息
System.out.println(updateResult.getMatchedCount());
}
}
4.5.5 文档删除
@SpringBootTest
public class RemoveServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 删除集合中的满足条件的一个或者多个文档
*/
@Test
public void update(){
//创建条件对象
Criteria criteria = Criteria.where("age").is(22);
Query query = new Query(criteria);
DeleteResult deleteResult = mongoTemplate.remove(query, "users");
System.out.println(deleteResult.getDeletedCount());
}
/**
* 删除符合条件的单个围挡,并返回删除的文档
*/
@Test
public void updateFirst(){
Criteria criteria = Criteria.where("age").is(31);
Query query = new Query(criteria).with(Sort.by("age").ascending());
User users = mongoTemplate.findAndRemove(query, User.class, "users");
System.out.println(users);
}
/**
*删除符合条件的全部围挡,并返回删除的文档
*/
@Test
public void updateMany(){
Criteria criteria = Criteria.where("age").is(22);
Query query = new Query(criteria).with(Sort.by("age").ascending());
List<User> users = mongoTemplate.findAllAndRemove(query, User.class, "users");
for (User user : users) {
System.out.println(user);
}
}
}
4.6 聚合操作
import java.util.Map;
@SpringBootTest
public class AggregateGroupServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 使用管道操作符 $group 结合 $count 方法进行聚合统计
*/
@Test
public void update(){
//使用管道操作符$group进行分组,然后统计各个组的文档数量
GroupOperation operation = Aggregation.group("age").count().as("numCOunt");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $group 结合表达式操作符$max 进行聚合统计
*/
@Test
public void max(){
//使用管道操作符$group进行分组,然后统计各个文档某字段的最大值
GroupOperation operation = Aggregation.group("sex").max("salary").as("salaryMax");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $group 结合表达式操作符$sum 进行聚合统计
*/
@Test
public void sum(){
//使用管道操作符$group进行分组,然后统计各个文档某字段的最大值
GroupOperation operation = Aggregation.group("sex").sum("salary").as("salaryMax");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $group 结合表达式操作符$first 获取每个组的包含某个字段的第一条数据
* 同理可得last
*{_id=男, salaryFirst=1500}
*{_id=女, salaryFirst=1000.0}
*/
@Test
public void avg(){
//使用管道操作符$group进行分组,然后统计各个文档某字段的最大值
GroupOperation operation = Aggregation.group("sex").first("salary").as("salaryFirst");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $group 结合表达式操作符push 获取某字段列表
* 同理可得last
* {_id=女, salaryFirst=[1000.0, 1500]}
* {_id=男, salaryFirst=[1500, 1500, 1500]}
*/
@Test
public void push(){
//使用管道操作符$group进行分组,以数组的形式列出某字段的全部值
GroupOperation operation = Aggregation.group("sex").push("salary").as("salaryFirst");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
}
4.7 聚合管道操作符
- $project: 可以从文档中选择想要的字段,和不想要的字段(指定的字段可以是来自输入文档或新计算字段的现有字段 ,也可以通过管道表达式进行一些复杂的操作,例如数学操作,日期操作,字符串操作,逻辑操作。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytyasv8N-1674979529795)(https://g.yuque.com/gr/latex?match%EF%BC%9A%20%E7%94%A8%E4%BA%8E%E8%BF%87%E6%BB%A4%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%8F%AA%E8%BE%93%E5%87%BA%E7%AC%A6%E5%90%88%E6%9D%A1%E4%BB%B6%E7%9A%84%E6%96%87%E6%A1%A3%E3%80%82#card=math&code=match%EF%BC%9A%2A%2A%20%E7%94%A8%E4%BA%8E%E8%BF%87%E6%BB%A4%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%8F%AA%E8%BE%93%E5%87%BA%E7%AC%A6%E5%90%88%E6%9D%A1%E4%BB%B6%E7%9A%84%E6%96%87%E6%A1%A3%E3%80%82&id=hGeG6)]match使用MongoDB的标准查询操作。
- $limit: 用来限制MongoDB聚合管道返回的文档数。
- $skip: 在聚合管道中跳过指定数量的文档,并返回余下的文档。
- $unwind: 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
- $group: 将集合中的文档分组,可用于统计结果。
- $sort: 将输入文档排序后输出。
@SpringBootTest
public class AggregatePipelineServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 使用管道操作符 $group 和$match过滤岁数大于25的用户,然后按照性别分组,统计每组用户工资的最高值
* {_id=女, sexsalarymax=1500}
* {_id=男, sexsalarymax=1500}
*
* Aggregation aggregation = Aggregation.newAggregation(matchOperation,operation);
* 当进行条件匹配的时候,按照顺序进行匹配
*/
@Test
public void aggregateGroupMatch(){
//先使用match过滤岁数大于10的用户,然后按照性别分组,统计用户的工资最大值
MatchOperation matchOperation = Aggregation.match(Criteria.where("age").gt(10));
GroupOperation operation = Aggregation.group("sex").max("salary").as("sexsalarymax");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(matchOperation,operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $group 和$sort 聚合,先使用#group进行分组,然后使用$sort分组
* {_id=23, salaryMax=1500, ageCount=2}
* {_id=22, salaryMax=1500, ageCount=2}
* {_id=18, salaryMax=1000.0, ageCount=1}
*/
@Test
public void max(){
//设置聚合条件,按岁数分组,然后统计每组用户工资的最大值和用户数,按每组用户最大值进行升序排序
GroupOperation operation = Aggregation.group("age")
.max("salary").as("salaryMax")
.count().as("ageCount");
Aggregation.sort(Sort.by("ageSalary").ascending());
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $limit聚合,现先使用group进行分组,然后使用limit限制一定数目文档
* {_id=23, sumsalary=3000, maxsalary=1500, minsalary=1500, avgsalary=1500.0}
* {_id=22, sumsalary=3000, maxsalary=1500, minsalary=1500, avgsalary=1500.0}
*/
@Test
public void sum(){
GroupOperation operation = Aggregation.group("age")
.sum("salary").as("sumsalary")
.max("salary").as("maxsalary")
.min("salary").as("minsalary")
.avg("salary").as("avgsalary");
LimitOperation limitOperation = Aggregation.limit(2L);
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation,limitOperation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $skip聚合,现先使用group进行分组,然后使用skip跳过一定数目的文档
* {_id=22, sumsalary=3000, maxsalary=1500, minsalary=1500, avgsalary=1500.0}
* {_id=23, sumsalary=3000, maxsalary=1500, minsalary=1500, avgsalary=1500.0}
*/
@Test
public void skip(){
GroupOperation operation = Aggregation.group("age")
.sum("salary").as("sumsalary")
.max("salary").as("maxsalary")
.min("salary").as("minsalary")
.avg("salary").as("avgsalary");
SkipOperation skipOperation = Aggregation.skip(1L);
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation,skipOperation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用管道操作符 $project聚合,现先使用group进行分组,然后使用project限制显示的字段
* {_id=18, maxsalary=1000.0}
* {_id=22, maxsalary=1500}
* {_id=23, maxsalary=1500}
*/
@Test
public void project(){
GroupOperation operation = Aggregation.group("age")
.sum("salary").as("sumsalary")
.max("salary").as("maxsalary")
.min("salary").as("minsalary")
.avg("salary").as("avgsalary");
ProjectionOperation projectionOperation = Aggregation.project("maxsalary");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(operation,projectionOperation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
/**
* 使用$group和$unwind聚合,先使用project进行分组,然后使用unwind拆分文档中的数组为一条新文档记录
* 先用project进行限制显示的字段,然后选择限制的字段(为数组的。。),然后将这个字作为一个文档
* {_id=7df78ad8902c000000000000, title=MongoDB Overview, url=http://www.runoob.com, tags=mongodb}
* {_id=7df78ad8902c000000000000, title=MongoDB Overview, url=http://www.runoob.com, tags=database}
* {_id=7df78ad8902c000000000000, title=MongoDB Overview, url=http://www.runoob.com, tags=NoSQL}
* {_id=7df78ad8902d000000000000, title=NoSQL Overview, url=http://www.runoob.com, tags=mongodb}
* {_id=7df78ad8902d000000000000, title=NoSQL Overview, url=http://www.runoob.com, tags=database}
* {_id=7df78ad8902d000000000000, title=NoSQL Overview, url=http://www.runoob.com, tags=NoSQL}
* {_id=7df78ad8902e000000000000, title=Neo4j Overview, url=http://www.neo4j.com, tags=neo4j}
* {_id=7df78ad8902e000000000000, title=Neo4j Overview, url=http://www.neo4j.com, tags=database}
* {_id=7df78ad8902e000000000000, title=Neo4j Overview, url=http://www.neo4j.com, tags=NoSQL}
*/
@Test
public void unwind(){
ProjectionOperation project = Aggregation.project("title","url","tags");
UnwindOperation unwindOperation = Aggregation.unwind("tags");
//将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(project,unwindOperation);
//执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "mycol", Map.class);
for(Map result :results.getMappedResults()){
System.out.println(result);
}
}
}
4.8 索引操作
4.8.1 创建索引
@SpringBootTest
public class CreateIndexServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 创建降序索引
*/
@Test
public void createIndezx(){
mongoTemplate.getCollection("users").createIndex(Indexes.descending("age"));
}
/**
* 创建升序复合索引
*/
@Test
public void createIndezx2(){
mongoTemplate.getCollection("users").createIndex(Indexes.ascending("age","sex"));
}
/**
* 创建文字索引
*/
@Test
public void createIndezx3(){
mongoTemplate.getCollection("users").createIndex(Indexes.text("name"));
}
/**
* 创建哈希索引
*/
@Test
public void createIndezx4(){
mongoTemplate.getCollection("users").createIndex(Indexes.hashed("salary"));
}
/**
* 创建升序唯一索引
* 当建立唯一索引的时候,建立索引的字段对应的值必须唯一
*/
@Test
public void createIndezx5(){
//配置索引选项
IndexOptions options = new IndexOptions();
//设置为唯一索引
options.unique(true);
//创建索引
mongoTemplate.getCollection("users").createIndex(Indexes.ascending("name"),options);
}
/**
* 创建局部索引
*/
@Test
public void createIndezx6(){
//配置索引选项
IndexOptions options = new IndexOptions();
//设置过滤条件
options.partialFilterExpression(Filters.exists("name",true));
//创建索引
mongoTemplate.getCollection("users").createIndex(Indexes.ascending("salary"),options);
}
}
4.8.2 查询索引
@SpringBootTest
public class QueryIndexServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 获取当前集合,对应的所有索引的名称列表
* Document{{v=2, key=Document{{_id=1}}, name=_id_}}
* Document{{v=2, key=Document{{age=1, sex=1}}, name=age_1_sex_1}}
* Document{{v=2, key=Document{{_fts=text, _ftsx=1}}, name=name_text, weights=Document{{name=1}}, default_language=english, language_override=language, textIndexVersion=3}}
* Document{{v=2, key=Document{{salary=hashed}}, name=salary_hashed}}
* Document{{v=2, key=Document{{salary=1}}, name=salary_1, partialFilterExpression=Document{{name=Document{{$exists=true}}}}}}
* Document{{v=2, unique=true, key=Document{{name=1}}, name=name_1}}
*/
@Test
public void createIndezx6(){
//获取集合中的所有列表
ListIndexesIterable<Document> indexes = mongoTemplate.getCollection("users").listIndexes();
//创建字符串集合
ArrayList<Document> list = new ArrayList<>();
//获取集合中的全部索引信息
for (Document document : indexes) {
System.out.println(document);
list.add(document);
}
}
}
4.8.3 删除索引
@SpringBootTest
public class RemoveIndexServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 根据索引名称移除索引
*/
@Test
public void createIndezx6(){
mongoTemplate.getCollection("users").dropIndex("age_-1");
}
/**
* 移除全部索引
* 注意:默认的_id的主键索引是删除不掉的
*/
@Test
public void createIndezx7(){
mongoTemplate.getCollection("users").dropIndexes();
}
}
4.9 RunCommand命令
@SpringBootTest
public class RunCommandServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 执行mongodb自定义命令
* db.runCommand({"buildinfo":1})
* --返回mongodb服务器的版本号和主机操作系统信息
*/
@Test
public void createIndezx6(){
//自定义命令
String jsonCommand = "{\"buildInfo\":1}";
//将JSON字符串解析为mongodb命令
Bson bson = Document.parse(jsonCommand);
//执行自定义命令
System.out.println(mongoTemplate.getDb().runCommand(bson));
}
}
4.10 事务
配置事务管理器
package cn.iottepa.mongodb.Tran;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
/**
* 配置事务管理器
*/
@Configuration
public class TransactionConfig{
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory databaseFactory){
return new MongoTransactionManager(databaseFactory);
}
}
创建事务测试服务
package cn.iottepa.mongodb.Tran;
import cn.iottepa.mongodb.domain.Status;
import cn.iottepa.mongodb.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@Service
public class TransactionExample {
@Autowired
private MongoTemplate mongoTemplate;
@Transactional(rollbackFor = Exception.class)
public Object transactionTest(){
//设置两个用户的信息
User user1 = new User()
.setId("20")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("shiyi1111111")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
//插入数据
User user = mongoTemplate.insert(user1, "users");
//抛出异常,观察数据是否进行回滚
// int error = 1 / 0;
return user;
}
@Transactional
public Object transactionTest2(){
//设置两个用户的信息
User user1 = new User()
.setId("23")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("shiyi1111111")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
//插入数据
User user = mongoTemplate.insert(user1, "users");
//抛出异常,观察数据是否进行回滚
// int error = 1 / 0;
return user;
}
}
5.关于mongodb的副本集(集群,主从)
什么是副本集?
副本集是一组服务器(多个服务器,官网推荐最少三个),其中一个是主服务器,用于处理客户请求;还有多个备份服务器,用于保存主服务器的数据副本
当主服务器崩溃了,备份服务器自动将其中一个从服务器升级为主服务器。mongodb副本集具有多个副本保证了高性能,数据的一致性没容错性。
主从节点数据如何同步呢?
主机点记录其上的所有的操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证主从接节点的数据一致性。(有点类似redis的aof追加文件的持久化操作)
创建mongodb副本集操作实例 - Joans - 博客园 (cnblogs.com)
6.分片(数据分割)
MongoDB分片集群将数据分布在一个或多个分片上。每个分片部署成一个MongoDB副本集,该副本集保存了集群整体数据的一部分。因为每个分片都是一个副本集,所以他们拥有自己的复制机制,能够自动进行故障转移。你可以直接连接单个分片,就像连接单独的副本集一样。但是,如果连接的副本集是分片集群的一部分,那么只能看到部分数据。
分片集群由分片、mongos路由器和配置服务器组成。如下图所示
mongos路由器
如果每个分片都包含部分集群数据,那么还需要一个接口连接整个集群。这就是mongos。mongos进程是一个路由器,将所有的读写请求指引到合适的分片上。如此一来,mongos为客户端提供了一个合理的系统视图。
mongos进程是轻量级且非持久化的。它们通常运行与与应用服务器相同的机器上,确保对任意分片的请求只经过一次网络跳转。换言之,应用程序连接本地的mongos,而mongos管理了指向单独分片的连接。
配置服务器
配置服务器主要用于持久化了分片集群的元数据,用于一些特定的功能和集群的维护!!
每次有mongos进程启动,它都会从配置服务器中获取一份元数据的副本。没有这些数据,就无法获得一致的分片集群视图。该数据的重要性对配置服务器的设计和部署也有影响。
如果mongs进程是非持久化的,那么必须有地方能持久保存集群的公认状态;这就是配置服务器的工作,其中持久化了分片集群的元数据,改数据包括:每个数据库,集合和特定范围数据的位置;一份变更记录,保存了数据在分片之间进行迁移的历史信息。配置服务器中保存的元数据是某些特定功能和集群维护是的重中之重。
举例来说,每次有mongos进程启动,它都会从配置服务器中获取一份元数据的副本。没有这些数据,就无法获得一致的分片集群视图。该数据的重要性对配置服务器的设计和部署也有影响。
- 如上面结构图中所示,有三个配置服务器,但它们并不是以副本集的形式部署的。它们比异步复制要求更严格;
- mongos进程向配置服务器写入时,会使用两阶段提交。这能保证配置服务器之间的一致性。在各种生产环境的分片部署中,必须运行三个配置服务器,这 些服务器都必须部署在独立的机器上以实现冗余。
(5条消息) MongoDB实战-分片概念和原理_wanght89的专栏-优快云博客_mongodb分片
7.索引
什么是索引?
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
-----mongodb的索引与mysql的索引结构都是B+Tree,
默认索引:
所有mongodb默认都有一个**_id的索引字段**,如果我们不指定_id的值会自动生成一个ObjectId值。
该_id索引是唯一的,并且可以防止客户端对_id字段值相同插入两个。这个index无法被删除
注意:
我们可以创建单个索引(单个字段建立)
创建多个字段一起建立索引,则称作联合索引或者复合索引(在B+tree进行匹配查找的时候,根据建立索引的字段顺序进行比对!!!!!)