文章目录
一、MongoDB简介
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构
非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点
类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。MongoDB服务端可
运行在Linux、Windows或mac os x平台,支持32位和64位应用,默认端口为27017。推荐运行在64位平台,因为MongoDB在32位模式
运行时支持的最大文件尺寸为2GB。
在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。
MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
结构
集合
集合就是一组文档,类似于关系数据库中的表。
数据库
MongoDB中多个文档组成集合,多个集合组成数据库。
数据模型
一个MongoDB 实例可以包含一组数据库,一个DataBase 可以包含一组Collection(集合),一个集合可以包含一组Document(文档)。
一个Document包含一组field(字段),每一个字段都是一个key/value pair
。
-
key: 必须为字符串类型。
-
value:可以包含如下类型。
-
- 基本类型,例如,string,int,float,timestamp,binary 等类型。
- 一个document。
- 数组类型。
二、主要特点
- MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
- 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName=“Sameer”,Address=“8 Gandhi Road”)来实现更快的排序。
- 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
- 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
- Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
- MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
- Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
- Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
- Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
- GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
- MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
- MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
- MongoDB安装简单。
- 支持的系统Linux,OSX,Solaris,Windows
三、为何选择MongoDB
优点
不存在sql注入、不需要提前创建表、可以任意添加或减少字段、字段数据格式自由、可以处理json结构
可以使用upsert操作,即修改的数据不存在时直接插入。
充分利用了计算机内存,所以查询和插入效率要远大于MySQL。(400w随机数据)的时候只有在没有索引的情况下MongoDB的查询效率要远大于MySQL,插入效率和MySQL差不多都是8w条左右1分钟。在有索引的时候MySQL的查询要速度要高于MongoDB。
缺点
MongoDB是个nosql数据,所以关系能力薄弱,不能像MySQL一样使用join,union来进行联合查找,只能通过结合一些特殊语法来达到类似的结果。
事务能力薄弱,虽然MongoDB里事务,但是好像只能针对单条语句(查了好多但是有些看不懂),不能像MySQL一样利用事务执行多条语句,可以根据情况来选着全部提交执行或者全部取消回滚。
相比MySQL,MongoDB的效率存在一定的波动性,不是很稳定。
总结
总结起来,如果你的业务满足一个或多个特点,那么选择MongoDB是个正确的决定:
-
无需要跨文档或跨表的事务及复杂的join查询支持 // 目前已经支持事务,join的支持也越来越好。
-
敏捷迭代的业务,需求变动频繁,数据模型无法确定
-
存储的数据格式灵活,不固定,或属于半结构化数据
-
业务并发访问量大,需数千的QPS
-
TB级以上的海量数据存储,且数据量不断增加
-
要求存储的数据持久化、不丢失
-
需要99.999%的数据高可用性
-
需要大量的地理位置查询、文本查询
除了MongoDB之外,唯一的著名文档型数据库就是Apache CouchDB。尽管CouchDB的数据是使用JSON格式的纯文本存储的,而MongoDB使用的是BSON二进制格式,但两者的文档模型是相似的。与MongoDB一样,CouchDB也支持二级索引,不同之处是CouchDB中的索引是通过编写MapReduce函数来定义的,这比MySQL和MongoDB使用的声明式语法更复杂一些。两者伸缩的方式也有所不同,CouchDB不会把数据分散到多台服务器上,每个CouchDB节点都是其他节点的完整副本。
目前开源数据库众多,大家可选的余地很大,就会出现这样的问题:MySQL、MongoDB、Redis、Hbase等这些数据库哪个更好?其实这是一个伪命题,脱离了具体的业务场景来讨论好坏是纸上谈兵,没有最好的,只有最合适的,谁也无法保证完全取代谁,上面的每种数据库都在变得更好,都在不停地完善自身。比如MySQL在不断提升其JSON和地理位置处理能力、组复制(group replication)已在开发等;而MongoDB在增强join类型支持,提供更为复杂的多集合查询能力,计划支持事务等;Redis也加入了地理位置处理能力。
四、安装mongo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfQlc2Dc-1609209485333)(file:///C:/Users/CP-CUI/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]
登录完之后,
1.创建 admin
角色:userAdminAnyDatabase (这是一个账号管理员的角色)
admin用户用于管理账号,不能进行关闭数据库等操作,目标数据库是admin
创建用户名密码:
use admin
db.createUser({
"user":"ueh","pwd":"abc123ABC",
"roles":[
{role:"userAdminAnyDatabase", db: "admin"},
{role:"readWriteAnyDatabase", db: "admin"} ]});
db.system.users.find()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWastW7T-1609209485335)(file:///C:/Users/CP-CUI/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg)]
2.创建root
创建完admin管理员,创建一个 超级管理员 root 角色:root
root角色用于 关闭数据库 db.shutdownServer()
db.createUser({user: “root”,pwd: “root123”,roles: [ { role: “root”, db: “admin” } ]})
3.创建用户自己的数据库的角色
当账号管理员和超级管理员,可以为自己的数据库创建用户了
(坑)这时候一定,一定要切换到所在数据库上去创建用户,不然创建的用户还是属于admin。
use ebip
db.createUser({user: “ueh”,pwd: “abc123ABC”,roles: [ { role: “readWrite”, db: “ebip” } ]})
4、配置注册表 使密码生效
5、启动
进入cmd
cd C:\Program Files\MongoDB\Server\4.4\bin
启动服务建立连接执行 : mongod
重新打开一个cmd :
cd C:\Program Files\MongoDB\Server\4.4\bin
执行命令
mongo
测试是否启动成功show dbs
连接远程mongodb
进入mongodb安装目录 执行 mongo ip:27017/数据库 -u 用户名 -p 密码
五、基本应用
1、添加依赖
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.0.4</version>
</dependency>
2、连接数据库
连接数据库,你需要指定数据库名称,如果指定的数据库不存在,mongo会自动创建数据库。
连接数据库的Java代码如下:
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
使用配置连接
- application.yml
spring:
data:
mongodb:
uri: mongodb://<username>:<password>@ip:27017/test
本实例中 Mongo 数据库无需用户名密码验证。如果你的 Mongo 需要验证用户名及密码,可以使用以下代码:
import java.util.ArrayList;
import java.util.List;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC {
public static void main(String[] args){
try {
//连接到MongoDB服务 如果是远程连接可以替换“localhost”为服务器所在IP地址
//ServerAddress()两个参数分别为 服务器地址 和 端口
ServerAddress serverAddress = new ServerAddress("localhost",27017);
List<ServerAddress> addrs = new ArrayList<ServerAddress>();
addrs.add(serverAddress);
//MongoCredential.createScramSha1Credential()三个参数分别为 用户名 数据库名称 密码
MongoCredential credential = MongoCredential.createScramSha1Credential("username", "databaseName", "password".toCharArray());
List<MongoCredential> credentials = new ArrayList<MongoCredential>();
credentials.add(credential);
//通过连接认证获取MongoDB连接
MongoClient mongoClient = new MongoClient(addrs,credentials);
//连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("databaseName");
System.out.println("Connect to database successfully");
} catch (Exception e) {
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
3、创建集合
我们可以使用 com.mongodb.client.MongoDatabase 类中的createCollection()来创建集合
代码片段如下:
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
mongoDatabase.createCollection("test");
System.out.println("集合创建成功");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
编译运行以上程序,输出结果如下:
Connect to database successfully
集合创建成功
4、获取集合
我们可以使用com.mongodb.client.MongoDatabase类的 getCollection() 方法来获取一个集合
代码片段如下:
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
MongoCollection<Document> collection = mongoDatabase.getCollection("test");
System.out.println("集合 test 选择成功");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
编译运行以上程序,输出结果如下:
Connect to database successfully
集合 test 选择成功
5、插入文档
我们可以使用com.mongodb.client.MongoCollection类的 insertMany() 方法来插入一个文档
代码片段如下:
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
MongoCollection<Document> collection = mongoDatabase.getCollection("test");
System.out.println("集合 test 选择成功");
//插入文档
/**
* 1. 创建文档 org.bson.Document 参数为key-value的格式
* 2. 创建文档集合List<Document>
* 3. 将文档集合插入数据库集合中 mongoCollection.insertMany(List<Document>) 插入单个文档可以用 mongoCollection.insertOne(Document)
* */
Document document = new Document("title", "MongoDB").
append("description", "database").
append("likes", 100).
append("by", "Fly");
List<Document> documents = new ArrayList<Document>();
documents.add(document);
collection.insertMany(documents);
System.out.println("文档插入成功");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
编译运行以上程序,输出结果如下:
Connect to database successfully
集合 test 选择成功
文档插入成功
6、检索所有文档
我们可以使用 com.mongodb.client.MongoCollection 类中的 find() 方法来获取集合中的所有文档。
此方法返回一个游标,所以你需要遍历这个游标。
代码片段如下:
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
MongoCollection<Document> collection = mongoDatabase.getCollection("test");
System.out.println("集合 test 选择成功");
//检索所有文档
/**
* 1. 获取迭代器FindIterable<Document>
* 2. 获取游标MongoCursor<Document>
* 3. 通过游标遍历检索出的文档集合
* */
FindIterable<Document> findIterable = collection.find();
MongoCursor<Document> mongoCursor = findIterable.iterator();
while(mongoCursor.hasNext()){
System.out.println(mongoCursor.next());
}
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
编译运行以上程序,输出结果如下:
Connect to database successfully
集合 test 选择成功
Document{{_id=56e65fb1fd57a86304fe2692, title=MongoDB, description=database, likes=100, by=Fly}}
7、更新文档
你可以使用 com.mongodb.client.MongoCollection 类中的 updateMany() 方法来更新集合中的文档。
代码片段如下:
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
MongoCollection<Document> collection = mongoDatabase.getCollection("test");
System.out.println("集合 test 选择成功");
//更新文档 将文档中likes=100的文档修改为likes=200
collection.updateMany(Filters.eq("likes", 100), new Document("$set",new Document("likes",200)));
//检索查看结果
FindIterable<Document> findIterable = collection.find();
MongoCursor<Document> mongoCursor = findIterable.iterator();
while(mongoCursor.hasNext()){
System.out.println(mongoCursor.next());
}
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
编译运行以上程序,输出结果如下:
Connect to database successfully
集合 test 选择成功
Document{{_id=56e65fb1fd57a86304fe2692, title=MongoDB, description=database, likes=200, by=Fly}}
8、删除第一个文档
要删除集合中的第一个文档,首先你需要使用com.mongodb.DBCollection类中的 findOne()方法来获取第一个文档,然后使用remove 方法删除。
代码片段如下:
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
public class MongoDBJDBC{
public static void main( String args[] ){
try{
// 连接到 mongodb 服务
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 连接到数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("mycol");
System.out.println("Connect to database successfully");
MongoCollection<Document> collection = mongoDatabase.getCollection("test");
System.out.println("集合 test 选择成功");
//删除符合条件的第一个文档
collection.deleteOne(Filters.eq("likes", 200));
//删除所有符合条件的文档
collection.deleteMany (Filters.eq("likes", 200));
//检索查看结果
FindIterable<Document> findIterable = collection.find();
MongoCursor<Document> mongoCursor = findIterable.iterator();
while(mongoCursor.hasNext()){
System.out.println(mongoCursor.next());
}
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
进阶
mongoTemplate
mongoTempate对mongodb的CURD做了封装,使操作和使用变得更加简单方便。
常用方法
mongoTemplate.findAll(Student.class)
: 查询Student文档的全部数据
mongoTemplate.findById(<id>, Student.class)
: 查询Student文档id为id的数据
mongoTemplate.find(query, Student.class);
: 根据query内的查询条件查询
mongoTemplate.upsert(query, update, Student.class)
: 修改
mongoTemplate.remove(query, Student.class)
: 删除
mongoTemplate.insert(student)
: 新增
Query对象
1 创建一个query对象(用来封装所有条件对象),再创建一个criteria对象(用来构建条件)
2 精准条件:criteria.and(“key”).is(“条件”)
模糊条件:criteria.and(“key”).regex(“条件”)
3、封装条件:query.addCriteria(criteria)
4、大于(创建新的criteria):Criteria gt = Criteria.where(“key”).gt(“条件”)
小于(创建新的criteria):Criteria lt = Criteria.where(“key”).lt(“条件”)
5、Query.addCriteria(new Criteria().andOperator(gt,lt));
6、一个query中只能有一个andOperator()。其参数也可以是Criteria数组。
7、排序 :query.with(new Sort(Sort.Direction.ASC, “age”). and(new Sort(Sort.Direction.DESC, “date”)))
- 实体类
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.LocalDateTime;
@Data
@Document("student")
public class Student{
private String id;
private String username;
private String password;
private int age;
private String gender;
private LocalDateTime createTime;
}
- 实现
package com.fzy.javastudy.java.day_0906.service;
import com.fzy.javastudy.java.day_0906.model.*;
import com.fzy.javastudy.spring.apimodel.*;
import com.fzy.javastudy.spring.config.ServiceException;
import com.fzy.javastudy.spring.config.SystemException;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
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.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.regex.Pattern;
/**
* https://blog.csdn.net/sinat_35821285/article/details/83511203
*/
@Slf4j
@Service
public class MongoService {
@Resource
private MongoTemplate mongoTemplate;
/**
* 查询全部
*
* @return ApiResponse
*/
public ApiResponse findStudents() {
mongoTemplate.dropCollection(Customer.class);
List<Student> students = mongoTemplate.findAll(Student.class);
System.out.println(students);
long count = mongoTemplate.count(new Query().with(new Sort(Sort.Direction.ASC, "username")), Student.class);
return Api.ok(students, String.format("%s%d%s", "查询到", count, "条"));
}
/**
* 根据id查询
*
* @param id _id
* @return ApiResponse
*/
public ApiResponse findStudentByID(String id) {
Student student = mongoTemplate.findById(id, Student.class);
return Api.ok(student);
}
/**
* 准确查询
*
* @param student Student对象
* @return ApiResponse
*/
public ApiResponse findStudentListByMany(Student student) {
Query query = new Query(Criteria
.where("username").is(student.getUsername())
.and("gender").is(student.getGender())
.and("age").gt(student.getAge()));
List<Student> students = mongoTemplate.find(query, Student.class);
return Api.ok(students);
}
/**
* 模糊查询
* 模糊查询以 【^】开始 以【$】结束 【.*】相当于Mysql中的%
*
* @param username 用户名
* @return ApiResponse
*/
public ApiResponse findStudentsLikeName(String username) {
String regex = String.format("%s%s%s", "^.*", username, ".*$");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Query query = new Query(Criteria.where("username").regex(pattern));
List<Student> students = mongoTemplate.find(query, Student.class);
return Api.ok(students);
}
/**
* 分页查询
*
* @param request
* @return
*/
public PageApiResponse findStudentsPage(StudentRequest request) {
try {
Query query = new Query();
if (StringUtils.isNotEmpty(request.getUsername())) {
String regex = String.format("%s%s%s", "^.*", request.getUsername(), ".*$");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
query.addCriteria(Criteria.where("username").regex(pattern));
}
int totalCount = (int) mongoTemplate.count(query, Student.class);
List<Student> studentList = mongoTemplate.find(query.skip(request.getOffset()).limit(request.getPageSize()), Student.class);
PageApiResponse response = PageApi.ok(studentList, totalCount, "获取列表成功");
response.setTotalPage(PageCounter.toTalPage(totalCount, request.getPageSize()));
response.handleRequest(request);
return response;
} catch (Exception e) {
throw new SystemException(-1, "获取分页数据出错");
}
}
/**
* 修改
* @param student Student
* @return ApiResponse
*/
public ApiResponse updateStudent(Student student) {
try {
Query query = new Query(Criteria.where("_id").is(student.getId()));
Update update = new Update();
update.set("username", student.getUsername());
update.set("password", student.getPassword());
update.set("age", student.getAge());
update.set("gender", student.getGender());
UpdateResult result = mongoTemplate.upsert(query, update, Student.class);
long count = result.getModifiedCount();
if (count > 0) {
return Api.ok(null, "更新成功");
}
throw new ServiceException(-1, "更新失败");
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new SystemException(-1, "更新出错");
}
}
/**
* 删除
* @param id id
* @return ApiResponse
*/
public ApiResponse delete(String id) {
try {
Query query = new Query(Criteria.where("_id").is(id));
DeleteResult result = mongoTemplate.remove(query, Student.class);
long count = result.getDeletedCount();
if (count > 0) {
return Api.ok(count, "删除成功");
}
throw new ServiceException(-1, String.format("【%s】%s", id, "不存在"));
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new SystemException(-1, "删除出错");
}
}
/**
* 新增
*
* @param student
* @return
*/
public ApiResponse createStudent(Student student) {
try {
Student insert = mongoTemplate.insert(student);
return Api.ok(insert);
} catch (Exception e) {
e.printStackTrace();
throw new SystemException(-1, "创建出错");
}
}
}
GridFS
GridFS是Mongo的一个子模块,使用GridFS可以基于MongoDB来持久存储文件。并且支持**分布式应用(**文件分布存储和读取)。作为
MongoDB中二进制数据存储在数据库中的解决方案,通常用来处理大文件,对于MongoDB的BSON格式的数据(文档)存储有尺寸限制,
最大为16M。但是在实际系统开发中,上传的图片或者文件可能尺寸会很大,此时我们可以借用GridFS来辅助管理这些文件
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。
GridFS 可以更好的存储大于16M的文件。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。
使用场景
▲如果您的文件系统在一个目录中存储的文件的数量有限,你可以使用GridFS存储尽可能多的文件。
▲当你想访问大型文件的部分信息,却不想加载整个文件到内存时,您可以使用GridFS存储文件,并读取文件部分信息,而不需要加载整个文件到内存。
▲当你想让你的文件和元数据自动同步并部署在多个系统和设施,你可以使用GridFS实现分布式文件存储。
GridFS不是MongoDB自身特性,只是一种将大型文件存储在MongoDB的文件规范,所有官方支持的驱动均实现了GridFS规范。GridFS制定大文件在数据库中如何处理,通过开发语言驱动来完成、通过API接口来存储检索大文件。
以下是简单的 fs.files 集合文档:
{
"filename": "test.txt",
"chunkSize": NumberInt(261120),
"uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
"md5": "7b762939321e146569b07f72c62cca4f",
"length": NumberInt(646)
}
以下是简单的 fs.chunks 集合文档:
{
"files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
"n": NumberInt(0),
"data": "Mongo Binary Data"
}
GridFsTemplate
gridFsTemplate类实现了GridFsOperations的接口,非常丰富,可以参照GridFsTemplate文档
GridFS 上传文档示例
Mongodb 是一个开源的no-sql分布式数据库,Mongodb也为我们提供了基于文件的GFS分布式存储系统。因此利用Mongodb我们完全可以实现一个分布式的文件存储以及管理。
下面的内容主要为大家介绍,如何利用java,将大文件存入Mongodb数据库中。我们这里所说的大文件,是指大小在16M以上的文件,这也符合MongodbGFS的说明。mongodb是将文件进行分块,存储,当查询时,mongodb会帮你把你所需要的块进行组合然后展示给你,因此结合mongodb分布式的特性,我们可以轻易的构建一个分布式的文件存储。
在利用java驱动存储时,当我们获得需要存储的数据库连接之后,我们需要先创建一个bucket,官方的说明如下:
Create a GridFS Bucket
GridFS stores files in two collections: a chunks
collection stores the file chunks, and a files
collection stores file metadata. The two collections are in a common bucket and the collection names are prefixed with the bucket name.
mongodb是将文件分为两部分存储,一个是chunks,另一个是files。并且在 collection 的名字将会有你bucket的前缀。mongodb支持自定义的bucket的名字,当然也有默认,默认是files。
package com.aisino.uwcloud;
import com.mongodb.Block;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSUploadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
* Created by CP-CUI on 2020/12/25.
*/
public class MongodbGFS {
private MongoClient mongoClient;
// 我们进行操作的数据库
private MongoDatabase useDatabase;
// bucket
private GridFSBucket gridFSBucket;
public static void main(String[] args) {
MongodbGFS mongodbGFS = new MongodbGFS();
ObjectId objectId = mongodbGFS.saveFile("E:\\workFile\\平台建设_技术方案_合成New.doc");
System.out.println(objectId);
}
// 初始化
{
ServerAddress serverAddress = new ServerAddress("localhost",27017);
List<ServerAddress> addrs = new ArrayList<ServerAddress>();
addrs.add(serverAddress);
//MongoCredential.createScramSha1Credential()三个参数分别为 用户名 数据库名称 密码
MongoCredential credential = MongoCredential.createScramSha1Credential("ueh", "ebip", "abc123ABC".toCharArray());
List<MongoCredential> credentials = new ArrayList<MongoCredential>();
credentials.add(credential);
//通过连接认证获取MongoDB连接
MongoClient mongoClient = new MongoClient(addrs,credentials);
//连接到数据库
useDatabase = mongoClient.getDatabase("ebip");
// 自定义bucket name
gridFSBucket = GridFSBuckets.create(useDatabase, "zt_files");
// 使用默认的名字
// gridFSBucket=GridFSBuckets.create(useDatabase);
}
// 将文件存储到mongodb,返回存储完成后的ObjectID
public ObjectId saveFile(String url) {
InputStream ins = null;
ObjectId fileid = null;
// 配置一些参数
GridFSUploadOptions options = null;
// 截取文件名
String filename = url.substring((url.lastIndexOf("/") + 1), url.length());
try {
ins = new FileInputStream(new File(url));
options = new GridFSUploadOptions().chunkSizeBytes(358400).metadata(new Document("type", "presentation"));
// 存储文件,第一个参数是文件名称,第二个是输入流,第三个是参数设置
fileid = gridFSBucket.uploadFromStream(filename, ins, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
ins.close();
} catch (IOException e) {
}
}
return fileid;
}
// 通过OpenUploadStream存储文件
/**
*
* The GridFSUploadStream buffers data until it reaches the chunkSizeBytes and
* then inserts the chunk into the chunks collection. When the
* GridFSUploadStream is closed, the final chunk is written and the file
* metadata is inserted into the files collection.
*
*/
public ObjectId saveFile2(String url) {
ObjectId fileid = null;
GridFSUploadStream gfsupload = null;
// 配置一些参数
GridFSUploadOptions options = null;
// 截取文件名
String filename = url.substring((url.lastIndexOf("/") + 1), url.length());
try {
options = new GridFSUploadOptions().chunkSizeBytes(358400).metadata(new Document("type", "presentation"));
// 存储文件,第一个参数是文件名称,第二个是输入流,第三个是参数设置
gfsupload = gridFSBucket.openUploadStream(filename, options);
byte[] data = Files.readAllBytes(new File(url).toPath());
gfsupload.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
gfsupload.close();
fileid = gfsupload.getObjectId();
}
return fileid;
}
// 查询所有储存的文件
public List<String> findAllFile() {
List<String> filenames = new ArrayList<>();
gridFSBucket.find().forEach(new Block<GridFSFile>() {
@Override
public void apply(GridFSFile t) {
filenames.add(t.getFilename());
}
});
return filenames;
}
// 删除文件
public void deleteFile(ObjectId id) {
gridFSBucket.delete(id);
}
// 重命名文件
public void rename(ObjectId id, String name) {
gridFSBucket.rename(id, name);
}
// 将数据库中的文件读出到磁盘上,参数,文件路径
public String downFile(String url, ObjectId id) {
FileOutputStream out = null;
String result=null;
try {
out = new FileOutputStream(new File(url));
gridFSBucket.downloadToStream(id, out);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
out.close();
result=out.toString();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
六、MongoDB数据迁移
Bin目录下没有mongoimport.exe mongoexeport.exe文件
解压后目录,将这些文件copy到mongo安装路径下的bin下,即可做数据迁移和备份操作
报Access is denied异常解决,打开cmd时需要以管理员身份运行
执行mongoexport --uri=“mongodb://用户名:密码@远程服务器ip:27017/ebip” --collection=sys_file --out=c:\sys_file.json
或者
mongoexport -h 远程服务器ip --port 27017 -u 用户名 -p 密码 -d 数据库 -c 表名 -o c:\文件名.dat (速度比第一种方式慢)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YBz8bg1p-1609209485354)(file:///C:/Users/CP-CUI/AppData/Local/Temp/msohtmlclip1/01/clip_image024.jpg)]
导入本地
进入安装目录bin下 执行命令:
mongoimport -h 127.0.0.1 --port 27017 -u ueh -p abc123ABC -d ebip -c sys_file c:\sys_file.dat