mongodb安装以及常用语法
拉取镜像
sudo docker pull mongo:latest
查看镜像是否拉取成功:
sudo docker images
启动容器
1.运行镜像(通过docker后台启动下载的mongo镜像,容器取名为mongo,端口映射为27017,将数据库数据文件挂载到/usr/local/docker/mongodb/data目录,并设置了用户名密码,官方有对应说明)
docker run --name mongo -p 27017:27017 -v /usr/local/docker/mongodb/data:/data/db -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=root -d mongo
2.登录到 MongoDB 容器中
docker exec -it mongo bash
3.通过 Shell 连接 MongoDB 然后就可以执行mongo中的相应命令
mongosh -u root -p root
4.mongodb存在多个数据库,使用 use [数据库名] 切换到指定数据库
springboot整合mongodb
引入相关依赖pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
添加mongodb配置信息application.yml
spring:
data:
mongodb:
host: 127.0.0.1 #指定MongoDB服务地址
port: 27017 #指定端口,默认就为27017
database: xxx #指定使用的数据库(集合)
authentication-database: admin # 登录认证的逻辑库名
username: root #用户名
password: root #密码
引入实体类
创建实体类,mongodb注解及作用
1、@Id
主键,不可重复,自带索引,如果自己不设置@Id主键,mongo会自动生成一个唯一主键,并且插入时效率远高于自己设置主键。在实体类字段添加@Id 注解,其作用会将该字段的数据插入集合的`_id`字段中,如果没有使用@Id注解,mongodb会总的生成_id,其类型是ObjectID类型
2、@Document
注解来标记文档对象
3、@Indexed
声明该字段需要加索引,加索引后以该字段为条件检索将大大提高速度。
4、@CompoundIndex
复合索引,加复合索引后通过复合索引字段查询将大大提高速度。
5、@Field
代表一个字段,可以不加,不加的话默认以参数名为列名。
6、@Transient
被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性
package com.demo.mongo.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Set;
/**
* @author zzc
* @since 2023/1/31
*/
@Document(collection = "user")
public class User {
@Id
private Integer uid;
private String name;
private Integer age;
private String favor;
private Integer nameCount;
private Integer ageSum;
private Set<String> ageSet;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getFavor() {
return favor;
}
public void setFavor(String favor) {
this.favor = favor;
}
public Integer getNameCount() {
return nameCount;
}
public void setNameCount(Integer nameCount) {
this.nameCount = nameCount;
}
public Integer getAgeSum() {
return ageSum;
}
public void setAgeSum(Integer ageSum) {
this.ageSum = ageSum;
}
public Set<String> getAgeSet() {
return ageSet;
}
public void setAgeSet(Set<String> ageSet) {
this.ageSet = ageSet;
}
}
工具类 MongodbUtils
package com.demo.mongo.utils;
import com.demo.mongo.entity.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.LookupOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author zzc
* @since 2023/1/31
* Mongodb工具类
*/
@Component
public class MongodbUtils {
public static MongodbUtils mongodbUtils;
public static final int INIT_PAGE_NUM = 1;
public static final int INIT_PAGE_SIZE = 10;
@PostConstruct
public void init() {
mongodbUtils = this;
mongodbUtils.mongoTemplate = this.mongoTemplate;
}
@Autowired
private MongoTemplate mongoTemplate;
/**
* 保存单个文档
* 集合为数据对象中@Document 注解所配置的collection
* @author : zzc
* @date : 2023/2/3 14:49
*/
public static void saveDoc(Object obj) {
mongodbUtils.mongoTemplate.save(obj);
}
/**
* 一次性保存多个文档信息
* @param list 数据对象
* @author : zzc
* @date : 2023/2/3 10:09
*/
public static void batchSaveDoc(Collection<?> list){
mongodbUtils.mongoTemplate.insertAll(list);
}
/**
* 根据条件删除document
* @author : zzc
* @date : 2023/2/3 15:10
*/
public static void removeDoc(Query query, String collectionName) {
mongodbUtils.mongoTemplate.remove(query, collectionName);
}
/**
* 根据key,value到指定集合删除数据
* @author : zzc
* @param key 字段名称
* @param value id值
* @param collectionName 集合名
*
*/
public static void removeDocBykey(String key, Object value, String collectionName) {
Criteria criteria = Criteria.where(key).is(value);
Query query = Query.query(criteria);
mongodbUtils.mongoTemplate.remove(query, collectionName);
}
/**
* 更新文档
* @author : zzc
* @date : 2023/2/6 10:04
*/
public static void updateDoc(Query query, Map<String,Object> updateMap, String collectionName) {
Assert.isTrue(!updateMap.isEmpty(), "更新的对象不能为空");
Update update = new Update();
updateMap.forEach((key,value) ->{
System.out.println("key----->"+key);
update.set(key, value);
});
mongodbUtils.mongoTemplate.updateMulti(query, update, collectionName);
}
/**
* 查询出所有结果集
* @author : zzc
* @date : 2023/2/6 10:05
*/
public static <T> List<T> findAllDoc(Class<T> clazz){
List<T> resultList = mongodbUtils.mongoTemplate.findAll(clazz);
return resultList;
}
/**
* 普通查询
* 根据条件查询出所有结果集
* 集合为数据对象中@Document 注解所配置的collection
*
* @return
*/
public static <T> List<T> findDoc(Query query, Class<T> entityClass) {
List<T> resultList = mongodbUtils.mongoTemplate.find(query, entityClass);
return resultList;
}
/**
* 聚合查询 可实现 分页 分组 排序 聚合函数
* @author : zzc
* @date : 2023/2/7 8:43
*/
public static <T> List<T> findDoc(List<AggregationOperation> aggregationOperations, String collectionName, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(aggregationOperations);
AggregationResults<T> aggregate = mongodbUtils.mongoTemplate.aggregate(aggregation, collectionName, entityClass);
List<T> mappedResults = aggregate.getMappedResults();
return mappedResults;
}
/**
* 分页查询
* @author : zzc
* @date : 2023/2/7 14:14
*/
public static <T> Page<T> findPageDoc(Query query, Class<T> entityClass, Integer pageNum, Integer pageSize ) {
pageSize = null == pageSize || pageSize == 0 ? INIT_PAGE_SIZE : pageSize;
pageNum = null == pageNum || pageNum <= 0 ? INIT_PAGE_NUM : pageNum;
long total = mongodbUtils.mongoTemplate.count(query, entityClass); //总数
final Integer pages = (int) Math.ceil(total / (double) pageSize); // 总页数
Assert.isTrue(pageNum <= pages,"当前页数大于总页数,查询失败");
query.skip((pageNum - 1) * pageSize).limit(pageSize);
List<T> result = mongodbUtils.mongoTemplate.find(query, entityClass);
final Page<T> page = new Page<>();
page.setTotal(total);
page.setPages(pages);
page.setPageSize(pageSize);
page.setPageNum(pageNum);
page.setList(result);
return page;
}
}
SpringData-MongoDB相关api
在SpringData操作MongoDB有两种方式:1、MongoTemplate实现CRUD, 2、使用 MongoRepository 实现CRUD。但第二种方式相对不够灵活;
在使用SpringData操作MongoDB前首先需要先了解相关api的使用:
Query 方法详解
使用Spring Data来查询MongoDB的最常用方法之一是使用Query和Criteria类,来构建查询条件
方法名 | 举例 | 释义 |
addCriteria() | // 设置查询条件 Query query = new Query(); query.addCriteria(Criteria.where("name").is("abc")); | name=abc |
fields() | // 返回字段 Query query = new Query(); query.fields().include("age").include("name").exclude("_id"); //只返回age,name | 返回指定字段 |
skip() limit() | // 跳过前3条数据,查询5条数据 Query query = new Query(); query.skip(3).limit(5); | 使用这两个方法实现分页 |
with() | Query query = new Query(); query.with(Sort.by(Sort.Direction.ASC,"age")); | 排序 |
Criteria 方法详解
通过使用criteria对象指定的,该对象具有一个静态工厂方法,该方法名为where,用于实例化新的criteria。MongoDB中提供的运算符相对应的Criteria和Query类的方法。大多数方法都返回Criteria对象,为API提供流畅的样式。
Criteria类提供以下方法,所有这些方法都对应于MongoDB中的运算符:
方法名 | 举例 | 释义 | MongoDB中的运算符 |
where() | Criteria.where(“name”).is(“abc”) | where | |
is() | Criteria.where(“name”).is(“abc”) | == | |
not() | Criteria.where(“name”).not(“abc”) | != | $not |
orOperator() | //以下两种等同where name="abc" or age=666 Criteria.where("name").is("abc").orOperator(Criteria.where("age").is(66); Criteria.orOperator(Criteria.where("name").is("abc"), Criteria.where("age").is(66); | or | $or |
andOperator() | 一个query中只能有一个andOperator() //以下两种等同 where name="abc" and age=66 Criteria.where("name").is("abc").andOperator(Criteria.where("age").is(66)); Criteria.andOperator(Criteria.where("name").is("abc"),Criteria.where("age").is(66)); | and | $and |
regex() | //等同 where name like %abc% Criteria.where("name").regex("." + abc + "."); Criteria.where("name").regex(".\" + abc + "\."); Criteria.where("name").regex(".?" + abc + "."); //前缀查询 Criteria.where("name").regex(".^" + abc + ".")); | 正则表达式 | $regex |
in() | Criteria.where(“name”).in(paramList) | in | $in |
gte() | Criteria.where(“age”).gte(66) | >= | $gte |
gt() | Criteria.where(“age”).gt(66) | > | $gt() |
ite() | Criteria.where(“age”).ite(66) | <= | $ite() |
it() | Criteria.where(“age”).it(66) | < | $it() |
Aggregation 方法详解
Aggregation简单来说,就是提供数据统计、分析、分类的方法,这与mapreduce有异曲同工之处,只不过mongodb做了更多的封装与优化,让数据操作更加便捷和易用。Aggregation操作,接收指定collection的数据集,通过计算后返回result数据;一个aggregation操作,从input源数据到output结果数据,中间会依次经过多个stages,整体而言就是一个pipeline;
AggregationOperation 聚类操作: 表示一个MongoDB aggregation pipeline 操作,描述聚类执行的步骤 。 尽管可以手工创建一个AggregationOperation , 但是建议使用Aggregate类提供的静态工厂方法来创建。
AggregationResults 聚类结果: 聚类操作的结果容器。 提供以document形式访问的操作。
管道操作符及其含义
操作符合 | 含义 |
$project | 增加,删除,重命名字段 |
$match | 过滤条件 |
$limit | 限制结果数量 |
$skip | 条件过文档数量 |
$sort | 条件排序 |
$group | 条件组合结果 |
$lookup | 用以引入其它集合的数据(表关联查询) |
$unwind | 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值 |
SQL和NOSQL对比
NOSQL | SQL |
$project | SELECT |
$match | WHERE |
$limit | LIMIT |
$sum | SUM() |
$sort | ORDER BY |
$group | GROUP BY |
$lookup | JOIN |
$count | COUNT() |
$min | min() |
$max | max() |
java操作mongodb
Spring Data MongoDB 是Spring框架访问mongodb的神器,可以非常方便的读写mongo库,使用MongoTemplate实现CRUD
新增数据
/**
* 保存单个文档
* 集合为数据对象中@Document 注解所配置的collection
* @author : zzc
* @date : 2023/2/3 14:49
*/
public static void saveDoc(Object obj) {
mongodbUtils.mongoTemplate.save(obj);
//mongodbUtils.mongoTemplate.insert(obj)
}
/**
* 一次性保存多个文档信息
* @param list 数据对象
* @author : zzc
* @date : 2023/2/3 10:09
*/
public static void batchSaveDoc(Collection<?> list){
mongodbUtils.mongoTemplate.insertAll(list);
}
ps: inser和save区别
insert: 若新增数据的主键已经存在,则会抛org.springframework.dao.DuplicateKeyException 异常提示主键重复,不保存当前数据。
save: 若新增数据的主键已经存在,则会对当前已经存在的数据进行修改操作。
删除数据
/**
* 根据条件删除document
* @author : zzc
* @date : 2023/2/3 15:10
*/
public static void removeDoc(Query query, String collectionName) {
mongodbUtils.mongoTemplate.remove(query, collectionName);
}
/**
* 根据key,value到指定集合删除数据
* @author : zzc
* @param key 字段名称
* @param value id值
* @param collectionName 集合名
*
*/
public static void removeDocBykey(String key, Object value, String collectionName) {
Criteria criteria = Criteria.where(key).is(value);
Query query = Query.query(criteria);
mongodbUtils.mongoTemplate.remove(query, collectionName);
}
修改数据
updateFirst:使用更新的文档更新与查询文档条件匹配的第一个文档。
updateMulti:使用更新的文档更新符合查询文档条件的所有对象。
/**
* 更新文档
* @author : zzc
* @date : 2023/2/6 10:04
*/
public static void updateDoc(Query query, Map<String,Object> updateMap, String collectionName) {
Assert.isTrue(!updateMap.isEmpty(), "更新的对象不能为空");
Update update = new Update();
updateMap.forEach((key,value) ->{
System.out.println("key----->"+key);
update.set(key, value);
});
mongodbUtils.mongoTemplate.updateMulti(query, update, collectionName);
}
ps: updateFirst does not support ordering. Please use findAndModify to apply Sort.
查询数据
普通查询
/**
* 普通查询
* 根据条件查询出所有结果集
* 集合为数据对象中@Document 注解所配置的collection
*
* @return
*/
public static <T> List<T> findDoc(Query query, Class<T> entityClass) {
List<T> resultList = mongodbUtils.mongoTemplate.find(query, entityClass);
return resultList;
}
@Test
public void findDoc() {
Query query = new Query();
Criteria criteria = new Criteria();
Pattern pattern=Pattern.compile(".*"+"文"+".*", Pattern.CASE_INSENSITIVE);
criteria.orOperator(
Criteria.where("name").is("郭法美"),
Criteria.where("name").regex(pattern),
Criteria.where("favor").regex(pattern),
Criteria.where("uid").gte(1).lt(3)
);
//显示自己需要展示的字段
query.fields().include("name").include("age");
//根据age 排序
query.with( Sort.by(Sort.Direction.DESC, "age"));
query.addCriteria(criteria);
List<User> doc = MongodbUtils.findDoc(query, User.class);
for (User user : doc) {
System.out.println(user.getAge());
}
}
聚合查询
下面代码块25-28行主要作用是对条件进行组装相当于sql中where后面的条件;33-34行主要实现分页
37-45代码主要实现分组同时对字段别名命名已经相关聚合函数的处理,47行代码主要是进行排序
.first("name").as("name")
.first("_id").as("uid")
.first("favor").as("favor")
.first("age").as("age")
.count().as("nameCount") //计数
.sum("age").as("ageSum")
.addToSet("age").as("ageSet") //把出现的所有年龄都加到set集合并用ageSet表示
.min("age").as("ageMin")
.max("age").as("ageMax"));
/**
* 聚合查询 可实现 分页 分组 排序 聚合函数
* @author : zzc
* @date : 2023/2/7 8:43
*/
public static <T> List<T> findDoc(List<AggregationOperation> aggregationOperations, String collectionName, Class<T> entityClass) {
Aggregation aggregation = Aggregation.newAggregation(aggregationOperations);
AggregationResults<T> aggregate = mongodbUtils.mongoTemplate.aggregate(aggregation, collectionName, entityClass);
List<T> mappedResults = aggregate.getMappedResults();
return mappedResults;
}
/**
* 聚合查询 可实现 分页 分组 排序 聚合函数
* @author : zzc
* @date : 2023/2/7 13:38
*/
@Test
public void findAggDoc() {
Integer pageSize =10;
Integer pageNum = 1;
Query query = new Query();
Criteria criteria = new Criteria();
Pattern pattern=Pattern.compile(".*"+"文"+".*", Pattern.CASE_INSENSITIVE);
criteria.orOperator(
Criteria.where("name").is("郭法美"),
Criteria.where("favor").regex(pattern)
);
List<AggregationOperation> objects = new ArrayList<>();
//查询条件,相当于sql 的 where
objects.add( Aggregation.match(criteria));
//分页
objects.add( Aggregation.skip((long) (pageNum > 0 ? (pageNum - 1) * pageSize : 0)));
objects.add( Aggregation.limit(pageSize));
//分组聚合
objects.add( Aggregation.group("favor")
.first("name").as("name")
.first("_id").as("uid")
.first("favor").as("favor")
.first("age").as("age")
.count().as("nameCount") //计数
.sum("age").as("ageSum")
.addToSet("age").as("ageSet") //把出现的所有年龄都加到set集合并用ageSet表示
.min("age").as("ageMin")
.max("age").as("ageMax"));
// 排序(根据某字段排序 倒序)
objects.add( Aggregation.sort(Sort.by(Sort.Order.desc("uid"))));
// 返回需要显示的字段
// objects.add(Aggregation.project("uid","name","favor","age","nameCount","ageSum","ageSet"));
List<UserVo> users = MongodbUtils.findDoc(objects, "user", UserVo.class);
// AggregationResults<User> user1 = MongodbUtils.findDoc(aggregation, "user", User.class);// 会报错---》 猜测是因为实体的注解,插入的时候是String对象
System.out.println("--------------");
}
PS : 如果不对字段重新命名,可能会导致查询出来的字段值为null(47-50行代码)
分页查询
/**
* 分页查询
* @author : zzc
* @date : 2023/2/7 14:14
*/
public static <T> Page<T> findPageDoc(Query query, Class<T> entityClass, Integer pageNum, Integer pageSize ) {
pageSize = null == pageSize || pageSize == 0 ? INIT_PAGE_SIZE : pageSize;
pageNum = null == pageNum || pageNum <= 0 ? INIT_PAGE_NUM : pageNum;
long total = mongodbUtils.mongoTemplate.count(query, entityClass); //总数
final Integer pages = (int) Math.ceil(total / (double) pageSize); // 总页数
Assert.isTrue(pageNum <= pages,"当前页数大于总页数,查询失败");
query.skip((pageNum - 1) * pageSize).limit(pageSize);
List<T> result = mongodbUtils.mongoTemplate.find(query, entityClass);
final Page<T> page = new Page<>();
page.setTotal(total);
page.setPages(pages);
page.setPageSize(pageSize);
page.setPageNum(pageNum);
page.setList(result); //查询的结果集
return page;
}
/**
* 分页查询 测试类
* @author : zzc
* @date : 2023/2/7 14:37
*/
@Test
public void findPageDoc() {
Integer pageSize = 5;
Integer pageNum = 1;
Query query = new Query();
Criteria criteria = new Criteria();
Pattern pattern=Pattern.compile(".*"+"文"+".*", Pattern.CASE_INSENSITIVE);
criteria.orOperator(
Criteria.where("favor").regex(pattern)
);
query.addCriteria(criteria);
Page<User> pageDoc = MongodbUtils.findPageDoc(query, User.class, pageNum, pageSize);
System.out.println("----------------------");
}
/**
*
* @author : zzc
* @date : 2023/2/7 16:00
* 将数据中的list 进行拆分
*
*
*/
@Test
public void findUnwindDoc() {
List<AggregationOperation> objects = new ArrayList<>();
objects.add(Aggregation.unwind("users","index",false));
// objects.add(Aggregation.project("users"));
objects.add(Aggregation.project("users"));
List<JSONObject> result = MongodbUtils.findDoc(objects,"log", JSONObject.class);
System.out.println(result);
}
PS: springData官方文档描述Aggregation相关api比较少,想了解更多api可以到mongodb官网学习。mongodb官方文档