精华内容
下载资源
问答
  • 需求背景 Springboot集成:Mongodb...1. 集成mongodb依赖组件 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifa...

    需求背景

    Springboot集成:Mongodb实现

    技术点

    1. 集成mongodb依赖组件

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>

    2. 使用MongoTemplate对象(mongodb安装使用教程

    @Resource
    private MongoTemplate mongoTemplate;

    代码演示

    1. 项目目录结构

    2. pom.xml依赖组件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    
    	<parent>
    		<groupId>com.md</groupId>
    		<artifactId>spring-boot2-parent</artifactId>
    		<version>0.0.1-SNAPSHOT</version>
    		<relativePath>../pom.xml</relativePath>
    	</parent>
    
    	<artifactId>spring-boot2-mongodb</artifactId>
    	<packaging>jar</packaging>
    
    	<name>spring-boot2-mongodb</name>
    	<description>Spring Boot, MVC, Rest API for App</description>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter</artifactId>
    		</dependency>
    		<!-- 构建成可运行的Web项目 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>net.sf.json-lib</groupId>
    			<artifactId>json-lib-ext-spring</artifactId>
    		</dependency>
    		<!-- mongodb集成 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-data-mongodb</artifactId>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    </project>
    

    3. application.yml配置文件

    spring:
       application:
          name: spring-boot-mongodb
       data:
          mongodb:
             uri: mongodb://127.0.0.1/demo
    server:
       port: 9090

    注意:这里使用的db库名为demo(mongodb安装使用教程

    4. DemoDaoImpl.java文件

    package com.md.demo.dao.impl;
    
    import javax.annotation.Resource;
    
    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.Component;
    
    import com.md.demo.dao.DemoDao;
    import com.md.demo.vo.DemoEntity;
    
    @Component
    public class DemoDaoImpl implements DemoDao {
    
        @Resource
        private MongoTemplate mongoTemplate;
    
        @Override
        public void saveDemo(DemoEntity demoEntity) {
            mongoTemplate.save(demoEntity);
        }
    
        @Override
        public void removeDemo(DemoEntity demoEntity) {
            mongoTemplate.remove(demoEntity);
        }
    
        @Override
        public void updateDemo(DemoEntity demoEntity) {
            Query query = new Query(Criteria.where("id").is(demoEntity.getId()));
    
            Update update = new Update();
            update.set("title", demoEntity.getTitle());
            update.set("description", demoEntity.getDescription());
            update.set("by", demoEntity.getBy());
            update.set("url", demoEntity.getUrl());
    
            mongoTemplate.updateFirst(query, update, DemoEntity.class);
        }
    
        @Override
        public DemoEntity findDemoById(Long id) {
            Query query = new Query(Criteria.where("id").is(id));
            DemoEntity demoEntity = mongoTemplate.findOne(query, DemoEntity.class);
            return demoEntity;
        }
    
    }

    5. DemoServiceImpl.java文件

    package com.md.demo.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.md.demo.dao.DemoDao;
    import com.md.demo.service.DemoService;
    import com.md.demo.vo.DemoEntity;
    
    @Service
    public class DemoServiceImpl implements DemoService {
    
    	@Autowired
    	private DemoDao demoDao;
    
    	@Override
    	public void addDemo(DemoEntity demoEntity) {
    		this.demoDao.saveDemo(demoEntity);
    	}
    
    	@Override
    	public void removeDemo(Long id) {
    		DemoEntity demoEntity = new DemoEntity();
    		demoEntity.setId(id);
    		this.demoDao.removeDemo(demoEntity);
    	}
    
    	@Override
    	public void modifyDemo(DemoEntity demoEntity) {
    		this.demoDao.updateDemo(demoEntity);
    	}
    
    	@Override
    	public DemoEntity findDemoById(Long id) {
    		return this.demoDao.findDemoById(id);
    	}
    
    }

    案例演示

    InitRest.java文件(添删查改)

    package com.md.demo.rest;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.md.demo.service.DemoService;
    import com.md.demo.util.JsonResult;
    import com.md.demo.util.ResultCode;
    import com.md.demo.vo.DemoEntity;
    
    /**
     * @author Minbo
     */
    @RestController
    public class InitRest {
    
    	protected static Logger logger = LoggerFactory.getLogger(InitRest.class);
    
    	/**
    	 * http://localhost:9090/hello
    	 * 
    	 * @return
    	 */
    	@GetMapping("/hello")
    	public String hello() {
    		logger.info("hello");
    		return "Hello greetings from spring-boot2-mongodb";
    	}
    
    	@Autowired
    	private DemoService demoService;
    
    	/**
    	 * 新增
    	 */
    	@GetMapping("/saveDemoTest")
    	public JsonResult saveDemoTest() {
    
    		DemoEntity demoEntity = new DemoEntity();
    		demoEntity.setId(1L);
    		demoEntity.setTitle("使用mongodb");
    		demoEntity.setDescription("这是描述");
    		demoEntity.setBy("minbo");
    		demoEntity.setUrl("https://blog.csdn.net/hemin1003");
    
    		this.demoService.addDemo(demoEntity);
    
    		demoEntity = new DemoEntity();
    		demoEntity.setId(2L);
    		demoEntity.setTitle("使用mongodb2");
    		demoEntity.setDescription("这是描述2");
    		demoEntity.setBy("minbo2");
    		demoEntity.setUrl("https://blog.csdn.net/hemin1003");
    
    		this.demoService.addDemo(demoEntity);
    
    		return new JsonResult(ResultCode.SUCCESS);
    	}
    
    	/**
    	 * 删除
    	 */
    	@GetMapping("/removeDemoTest")
    	public JsonResult removeDemoTest(Long id) {
    		this.demoService.removeDemo(id);
    
    		return new JsonResult(ResultCode.SUCCESS);
    	}
    
    	/**
    	 * 修改
    	 */
    	@GetMapping("/updateDemoTest")
    	public JsonResult updateDemoTest() {
    
    		DemoEntity demoEntity = new DemoEntity();
    		demoEntity.setId(1L);
    		demoEntity.setTitle("使用mongodb3");
    		demoEntity.setDescription("这是描述3");
    		demoEntity.setBy("minbo3");
    		demoEntity.setUrl("https://blog.csdn.net/hemin1003");
    
    		this.demoService.modifyDemo(demoEntity);
    
    		return new JsonResult(ResultCode.SUCCESS, demoEntity);
    	}
    
    	/**
    	 * 查找
    	 */
    	@GetMapping("/findDemoByIdTest")
    	public JsonResult findDemoByIdTest(Long id) {
    		DemoEntity demoEntity = this.demoService.findDemoById(id);
    		System.out.println(demoEntity.toString());
    		return new JsonResult(ResultCode.SUCCESS, demoEntity);
    	}
    }

    例如:访问接口 http://localhost:9090/saveDemoTest

    完整源码下载

    我的Github源码地址:

    https://github.com/hemin1003/spring-boot-study/tree/master/spring-boot2-study/spring-boot2-parent/spring-boot2-mongodb

    参考资料

    mongodb java案例代码演示

    mongodb安装使用教程

    下一章教程

    SpringBoot从入门到精通教程(二十二)- Oauth2+Token详细用法/SpringSecurity

    该系列教程

    SpringBoot从入门到精通教程

     

     

    至此,全部介绍就结束了

     

    ------------------------------------------------------

    ------------------------------------------------------

     

    关于我(个人域名)

    我的开源项目集Github

     

    期望和大家一起学习,一起成长,共勉,O(∩_∩)O谢谢

    欢迎交流问题,可加个人QQ 469580884,

    或者,加我的群号 751925591,一起探讨交流问题

    不讲虚的,只做实干家

    Talk is cheap,show me the code

    展开全文
  • 摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence等等,但是没有MongoDB的,于是自己扩展了一个支持MongoDB的缓存组件(NHibernate.Caches.MongoDBCache.dll)。本篇先把组件...

    摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence等等,但是没有MongoDB的,于是自己扩展了一个支持MongoDB的缓存组件(NHibernate.Caches.MongoDBCache.dll)。本篇先把组件的源代码开放出来。

     

    一、背景

         在NHibernate的Contrib贡献项目官方网站(NHibernateContrib项目是由NHibernate开发团队或者终端用户根据需要自行编译并贡献的一系列的程序)中,拥有一个NHibernate.Caches的项目,里面包含汗多基于NHibernate二级缓存的组件,其中包括有:

    NHibernate.Caches.MemCache:基于memcached分布式存储的缓存组件。这个大家都比较熟悉了就不多说了,详细可查阅相关信息。

    NHibernate.Caches.Prevalence:基于Bamboo.Prevalence的缓存组件。它可产生一系列的缓存目录,通过缓存目录可以从文件中获取数据,并且在缓存目录中通过Snapshot,也就是快照,可以进行断点保存。详细介绍请看我的文章:(在Spring.Net中对于NHibernate.Caches.Prevalence的使用

    NHibernate.Caches.SharedCache:基于MergeSystem.Indexus.WinServiceCommon、MergeSystem.Indexus.WinService和MergeSystem.Indexus.Notify的分布式存储的缓存组件。用于在动态WEB或Win应用程序中减少数据库的负责,提高访问速度。

    NHibernate.Caches.SysCache:我们通常DotNet上所使用的System.Web.Caching.Cache。

    NHibernate.Caches.SysCache2:同上。不同的是增加了对于SQL2005的缓存依赖的支持。

    NHibernate.Caches.Velocity:基于微软推出的分布式缓存Velocity组件。跟memcached一样,“Velocity”维护一张大的哈希表,这张表可以跨越多个服务器,你可以通过添加或者减少服务器来平衡系统压力。

     

    二、什么是MongoDB?

          MongoDB是一个基于分布式文档存储的数据库。旨在为WEB应用提供可护展的高性能数据存储解决方案。它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。 它的特点是高性能、易部署、易使用,存储数据非常方便。

    MongoDB官方服务端下载地址:http://www.mongodb.org/downloads

    MongoDB官方客户端(.NET)下载地址:https://github.com/samus/mongodb-csharp

     

    三、准备工作

    服务器端下载下来后,首先要安装MongoDB,大家可以参考下这篇文章:http://www.cnblogs.com/mamboer/archive/2010/03/05/1679292.html

    在你开发之前必须先吧MongoDB的服务或者控制台启动。这里我采用启动控制台。

    image

    从图中看出,MongoDB采用的默认端口是27017,并且在我安装的时候,将MongoDB的数据库目录配置在:C:\data\db上。

          现在开始,我要增加一个支持MongoDB的缓存组件,那么首先要先了解它们二级缓存流程的一些机制,本篇先不具体谈它的原理(会在下篇具体描述),先谈下它是如何实现的,要研究如何实现其实很简单,依葫芦画瓢,去看人家写的代码。

     

    四、分析与实现

    1. 在Spring.NET关于NHibernate的配置中,可以启用二级缓存其中有个配置节点是:

    < entry  key ="cache.provider_class"  value ="NHibernate.Cache.HashtableCacheProvider" />

    HashtableCacheProvider是NHibernate二级缓存中自带的默认的缓存提供程序。而HashtableCacheProvider继承的是ICacheProvider接口,于是要创建一个支持MongoDB的缓冲提供程序,就必须继承它。

     

    2. 创建一个MongoDBCacheProvider类:

    ExpandedBlockStart.gif 代码
         ///   <summary>
        
    ///  MongoDB缓存提供程序
        
    ///   </summary>
         public   class  MongoDBCacheProvider : ICacheProvider
        {
            
    private   static   readonly  ILog log  =  LogManager.GetLogger( typeof (MongoDBCacheProvider));

            
    static  MongoDBCacheProvider()
            {

            }

            
    public  ICache BuildCache( string  regionName, IDictionary < string string >  properties)
            {
                
    if  (regionName  ==   null )
                {
                    regionName 
    =   string .Empty;
                }
                
    if  (properties  ==   null )
                {
                    properties 
    =   new  Dictionary < string string > ();
                }
                
    if  (log.IsDebugEnabled)
                {

                }

                
    return   new  MongoDBCache(regionName, properties);
            }

            
    public   long  NextTimestamp()
            {
                
    return  Timestamper.Next();
            }

            
    public   void  Start(IDictionary < string string >  properties)
            {
            }

            
    public   void  Stop()
            {
            }
        }

    这样就实现了一个初步的MongoDB缓存提供程序的构架。注意到BuildCache方法返回的是一个ICache对象。这里就必须实现一个继承ICache接口的MongoDB缓存对象。

     

    3. 看下ICache都定义了哪些接口方法和属性:

    ExpandedBlockStart.gif 代码
    public   interface  ICache 


        
    void  Clear(); 
        
    void  Destroy(); 
        
    object  Get( object  key); 
        
    void  Lock( object  key); 
        
    long  NextTimestamp(); 
        
    void  Put( object  key,  object  value); 
        
    void  Remove( object  key); 
        
    void  Unlock( object  key); 

        
    string  RegionName {  get ; } 
        
    int  Timeout {  get ; } 
    }

    从字面上解释,应该大家都能够明白的:Clear清空缓存,Destroy和Clear类似,但是具体问题具体分析,Get取缓存,Lock锁定缓存,在ReadWrite模式的缓存上需要使用到,NextTimestamp下一时间段的时间戳,Put设置缓存,Remove清除指定的缓存数据,Unlock解除锁定,同样在ReadWrite模式的缓存上需要使用,RegionName区域名称,Timeout缓存过期时间。

     

    4. 创建一个MongoDBCache的缓存类:

    在它的构造函数中的代码:

    ExpandedBlockStart.gif 代码
             public  MongoDBCache( string  regionName, IDictionary < string string >  properties)
            {
                _regionName 
    =  regionName;

                
    if  (properties  !=   null )
                {
                    
    string  dbName  =   string .Empty;
                    
    if  (properties.TryGetValue( " mongodb.dasebaseName " out  dbName))
                    {
                        
    if  ( ! string .IsNullOrEmpty(dbName))
                        {
                            _dbName 
    =  dbName;
                        }
                    }

                    
    string  connectionString  =   string .Empty;
                    
    if  (properties.TryGetValue( " mongodb.connectionString " out  connectionString))
                    {
                        
    if  ( ! string .IsNullOrEmpty(connectionString))
                        {
                            _connectionString 
    =  connectionString;
                        }
                    }

                    
    string  pattern  =   string .Empty;
                    
    if  (properties.TryGetValue( " mongodb.pattern " out  pattern))
                    {
                        
    if  ( ! string .IsNullOrEmpty(pattern))
                        {
                            _pattern 
    =  pattern;
                        }
                    }

                    
    string  regionPrefix  =   string .Empty;
                    
    if  (properties.TryGetValue( " regionPrefix " out  regionPrefix))
                    {
                        
    if  ( ! string .IsNullOrEmpty(regionPrefix))
                        {
                            _regionPrefix 
    =  regionPrefix;
                        }
                    }
                }

                mongo 
    =   new  Mongo(_connectionString);

                
    //  连接
                mongo.Connect();

                
    //  获取Mongo数据库实体
                db  =  mongo[_dbName];
            }

    其中可以看出这里需要连接mongo的对象,并且指定它的数据库。

    而在它的析构函数中:

    ExpandedBlockStart.gif 代码
    ~ MongoDBCache() 

        Dispose(); 


    ///   <summary>  
    ///  释放资源 
    ///   </summary>  
    public   void  Dispose() 

        
    //  关闭连接 
        mongo.Disconnect(); 
        
    //  释放mongo资源 
        mongo.Dispose(); 
    }

    必须关闭mongo的连接,并且释放mongo资源。

    对于存储缓存数据(存在Mongo数据库的表中):

    ExpandedBlockStart.gif 设置缓存数据Put
             public   void  Put( object  key,  object  value)
            {
                
    if  (key  ==   null )
                {
                    
    throw   new  ArgumentNullException( " key " " null key not allowed " );
                }
                
    if  (value  ==   null )
                {
                    
    throw   new  ArgumentNullException( " value " " null value not allowed " );
                }
                
    if  (log.IsDebugEnabled)
                {
                    log.DebugFormat(
    " setting value for item {0} " , key);
                }

                
    string  hashKey  =  GetAlternateKeyHash(key);

                GenerateTableName(key);

                Console.WriteLine(
    string .Format( " Put------Key:{0}, Value:{1} " , hashKey, value.ToString()));

                IMongoCollection
    < Document >  table  =  db.GetCollection < Document > (TableName);

                IDictionary
    < string object >  dict  =   new  Dictionary < string object > ();
                dict.Add(
    " Key " , hashKey);

                Document query 
    =   new  Document(dict);

                
    //  查询
                Document document  =  table.FindOne(query);

                
    try
                {
                    
    if  (document  ==   null )
                    {
                        IDictionary
    < string object >  newDict  =   new  Dictionary < string object > ();
                        newDict.Add(
    " Value " , SerializeHelper.BinarySerialize(value));
                        newDict.Add(
    " Key " , hashKey);
                        newDict.Add(
    " Type " , value.GetType().Name);
                        newDict.Add(
    " Date " , DateTime.Now.ToString());

                        document 
    =   new  Document(newDict);
                    }
                    
    else
                    {
                        document[
    " Value " =  SerializeHelper.BinarySerialize(value);
                        document[
    " Type " =  value.GetType().Name;
                        document[
    " Date " =  DateTime.Now.ToString();
                    }

                    
    //  保存Document
                    table.Save(document);
                }
                
    catch
                {
                }
                
    finally
                {
                }
            }

    这里会将value对象序列化为字节数组,有人会问为什么不直接存储对象呢,还需要序列化,这是由于它的存储的数据结构决定的,它最后在数据库中形成的结果为一个BSON结构;还有人会问可以把它序列化为JSON字符串吗,我也做过尝试,但是后来发现value实际上的类型是CacheItem或者CacheEntity,它们都没有无参的构造函数,所以无法反序列化。因此,这里我采用了字节转换的方式。

    从代码中,可以看到document包含Key,Value,Type,Date(非必须的)的字段,其中Type在获取缓存数据(Get)的时候非常有用。

    对于获取数据:

    ExpandedBlockStart.gif 获取缓存数据Get
             public   object  Get( object  key)
            {
                
    string  hashKey  =  GetAlternateKeyHash(key);

                GenerateTableName(key);

                Console.WriteLine(
    string .Format( " Get------Key:{0} " , hashKey));

                IMongoCollection
    < Document >  table  =  db.GetCollection < Document > (TableName);

                IDictionary
    < string object >  dict  =   new  Dictionary < string object > ();
                dict.Add(
    " Key " , hashKey);

                Document query 
    =   new  Document(dict);

                
    //  查询
                Document document  =  table.FindOne(query);

                
    if  (document  !=   null )
                {
                    
    try
                    {
                        
    byte []  bytes  =  ((MongoDB.Binary)document[ " Value " ]).Bytes;

                        
    #region  反序列化字节数组

                        
    if  ( string .Equals(document[ " Type " ].ToString(),  typeof (CacheEntry).Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            
    return  SerializeHelper.BinaryDeSerialize < CacheEntry > (bytes);
                        }
                        
    else   if  ( string .Equals(document[ " Type " ].ToString(),  typeof (CachedItem).Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            
    return  SerializeHelper.BinaryDeSerialize < CachedItem > (bytes);
                        }
                        
    else   if  ( string .Equals(document[ " Type " ].ToString(),  typeof (List < Object > ).Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            
    return  SerializeHelper.BinaryDeSerialize < List < Object >> (bytes);
                        }
                        
    else   if  ( string .Equals(document[ " Type " ].ToString(),  typeof (Int64).Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            
    return  SerializeHelper.BinaryDeSerialize < Int64 > (bytes);
                        }
                        
    else   if  ( string .Equals(document[ " Type " ].ToString(),  typeof (CacheLock).Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            
    return  SerializeHelper.BinaryDeSerialize < CacheLock > (bytes);
                        }
                        
    else
                        {
                            
    return   null ;
                        }

                        
    #endregion
                    }
                    
    catch
                    {
                        
    return   null ;
                    }
                }
                
    return   null ;
            }

    其中Document document = table.FindOne(query);是从表中根据指定的Document查询数据。并且对于字节数据Value字段,必须进行字节反序列化。

    在Spring.NET对于NH的配置节点中可以这样子写:

    ExpandedBlockStart.gif 代码
    <!--  MongoDB缓存机制  -->  
    < entry  key ="cache.provider_class"  value ="NHibernate.Caches.MongoDBCache.MongoDBCacheProvider, NHibernate.Caches.MongoDBCache"   />  
    < entry  key ="mongodb.dasebaseName"  value ="xinogxt"   />  
    < entry  key ="mongodb.connectionString"  value ="servers=127.0.0.1:27017"   />  
    < entry  key ="mongodb.pattern"  value ="^TestWebServer\.Model\..+?" />

    其中mongodb.dasebaseName是给MongoDB配置的数据库名称;mongodb.connectionString是MongoDB服务的连接字符串;mongodb.pattern是为了作为表名称的匹配正则表达式,可以看下这段代码:

    ExpandedBlockStart.gif 代码
    ///   <summary>  
    ///  生成表格名称 
    ///   </summary>  
    ///   <param name="key"></param>  
    private   void  GenerateTableName( object  key) 

        
    if  (key  is  CacheKey) 
        { 
            CacheKey cacheKey 
    =  (CacheKey)key; 

            
    //  判断是否匹配正则表达式 
             if  (Regex.IsMatch(cacheKey.EntityOrRoleName, _pattern)) 
            { 
                _tableName 
    =  cacheKey.EntityOrRoleName.Replace( " . " " _ " ); 
            } 
        } 
    }

    它是通过CacheKey的EntityOrRoleName属性,进行筛选,比如:这里的EntityOrRoleName为”“TestWebServer.Model.TblEnterprise”的字符串(这是一个NH自动生成的实体类),我给它的正则表达式为“^TestWebServer\.Model\..+?”,那么它匹配了,我就取它的这个字符串为表名称,最后的表名为:“TestWebServer_Model_TblEnterprise”。这样我缓存每一个实体,都能够自动创建相应的一个Mongo表。

     

    5. 看下运行的结果:

    测试代码如下:

    [Test] 
    public   void  EnterpriseDaoTest6() 

        IEnterpriseDao dao 
    =  (IEnterpriseDao)applicationContext.GetObject( " EnterpriseDao " ); 
        ITblEnterprise enterprise 
    =  dao.GetInfo( 1 );

        …

    }

    第一次执行:

    image 

    第一次的时候,执行了数据库的SELECT的SQL语句。

    我查看本地目录以及用MongoVUE客户端工具查看了下Mongo数据库:

    image

    image

    缓存数据已经存在目录(数据库)中。

    第二次执行:

    image

    发现这里没有执行SQL。

    说明MongoDB缓存成功。

     

    6. 通过对对于NHibernate二级缓存机制的理解,我们完全可以扩展属于我们自己的缓存组件。不仅仅是作为MongoDB为载体的缓存实现。

    因此,在下一篇文章中,我将重点介绍关于NHibernate二级缓存机制的原理,并且继续深入探讨MongoDB缓存组件的相关原理。

     

    NHibernate.Caches.MongoDBCache.dll项目源代码下载:NHibernate.Caches.MongoDBCache.rar

    转载于:https://www.cnblogs.com/liping13599168/archive/2010/12/30/1922542.html

    展开全文
  • Java EE--组件Mongodb

    2020-12-18 14:43:02
    Mongodb liunux下载安装mongodb 初识Mongodb SpringBoot集成Mongodb insert和save的区别? delete系列方法的注意点 QBE查询时的细节问题 QBC组合复杂条件查询 分组、聚合查询

    目录

    前言

    Mongodb

    下载安装

    初识Mongodb

    SpringBoot集成Mongodb

    insert和save的区别?

    delete系列方法的注意点

    QBE查询时的细节问题


    前言

    带着问题学java系列博文之java基础篇。从问题出发,学习java知识。


    Mongodb

    来自百度百科——MongoDB是一个基于分布式文件存储的数据库,由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

     

    下载安装

    linux下安装mongodb:https://blog.csdn.net/qq_41107231/article/details/108028319

    安装可视化工具:NoSqlBooster(免费)  或者 Studio3T(收费)

     

    初识Mongodb

    概念对比:以mysql为例,类比其中的概念

    mysql

    mongodb

    解释

    database

    database

    数据库

    table

    collection

    数据表

    row

    document

    数据表行记录/文档

    column

    field

    数据字段/域

    index

    index

    索引

    primary key

    primary key

    主键,mongodb自动在集合中添加_id主键

    table joins

     

    表连接,mongodb不支持表连接

     

    SpringBoot集成Mongodb

    1.引入依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-mongodb</artifactId>
            </dependency>

    2.起步配置

    spring:
      data:
        mongodb:
          ##uri的字串解释 mongodb://用户名:密码@ip:端口(/账号对应的数据库(可以不添加))
          uri: mongodb://zst:xxx@localhost:27017
          database: mydb
        ##这里使用host、port、username、password分别配置的方式无法连接,必须使用uri的方式
        ##问题原因:创建账号的时候,是在admin数据库下创建的,该账号的权限是admin数据库,但是要操作的数据却在mydb数据库中,因此存在冲突
        ##解决办法:修改对应用户的对应数据库为mydb即可(自己尝试并没有解决问题)。
        ##查看用户账号的方法:使用root用户连接mongodb,查看admin数据库下的system.user表
    
    #配置日志中打印执行语句
    #logging.level.org.springframework.data.mongodb.core=DEBUG
    logging:
      level:
        org:
          springframework:
            data:
              mongodb:
                core: debug

    3.实体类实现

    import org.springframework.data.mongodb.core.mapping.Document;
    
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Document( collection = "student")
    public class Student {
        @Id
        private String id;
    
        private String name;
        private int age;
        private String address;
    }

    实体类使用mongodb的注解Document,表示它是一个文档类。(类似于jpa的注解@Table,表示是对应一个数据库表的类)

    4.Repository实现

    @Repository
    public interface StudentRepository extends MongoRepository<Student,String> {}

    前面讲过hibernate、spring-data-jpa,当时使用的就是repository,这里也是一样。因为我们导入的依赖是spring-boot-starter-data-mongo,很明显是spring官方封装的mongodb启动器。它的使用也和hibernate中的repository类似。

    5.使用repository实现增删改查

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class StudentRepositoryTest {
    
        //使用repository实现CRUD操作
        @Autowired
        private StudentRepository repository;
    
        // insert 和 save 的区别
        @Test
        public void testAdd(){
            Student student = new Student();
            student.setName("Android程序员");
            student.setAge(7);
            student.setAddress("安徽合肥高新彩虹路幼儿园");
            repository.insert(student);
            //mongodb自动生成主键id,并保存
            System.out.println(student);
    
            student.setAge(5);
            repository.save(student);
            System.out.println(student);
        }
    
        //注意delete(entity)并不是条件删除,和deleteById其实是一样的,都是根据id删除
        //deleteAll(list<entity>)也并不是条件删除,也是遍历根据id删除
        @Test
        public void testDelete(){
            repository.deleteById("5fdc16099cf60746d0b8278d");
            repository.deleteAll();
        }
    
    
        @Test
        public void testQuery(){
            // 简单查询
            repository.findById("5fdc16099cf60746d0b8278d");
            repository.findAll();
            // QBE
            // 设置条件
            ExampleMatcher matcher = ExampleMatcher.matching()
                    // address字段包含参数值
                    .withMatcher("address",ExampleMatcher.GenericPropertyMatchers.contains())
                    // name字段以参数值结尾
                    .withMatcher("name",ExampleMatcher.GenericPropertyMatchers.endsWith())
                    // age 不作为条件(注意由于age是int型,默认值是0,不是null,所以查询时会被作为条件之一)
                    .withIgnorePaths("age")
                    // 忽略null属性(默认忽略,可以不添加)
                    .withIgnoreNullValues();
            // 设置条件值
            Student student = new Student();
            student.setAddress("幼儿园");
            student.setName("程序员");
            // 排序
            Sort sort = Sort.by(Sort.Order.desc("age"));
            // 执行条件查询(地址包含“幼儿园”,名字以“程序员”结尾,查询结果按照年龄倒序排列)
            List<Student> students = repository.findAll(Example.of(student, matcher),sort);
            for (Student student1 : students) {
                System.out.println(student1);
            }
            
            // repository不支持QBC
            
            //分页查询
            Page<Student> studentPage = repository.findAll(PageRequest.of(0, 3));
            System.out.println("totalCount"+studentPage.getTotalElements()+";totalPage"+studentPage.getTotalPages());
            for (Student student1 : studentPage.getContent()) {
                System.out.println(student1);
            }
    
            // 直接在repository中定义属性方法查询
            List<Student> studentList = repository.findStudentByNameIsContainingAndAddressEndingWith("程序员", "幼儿园");
            for (Student student1 : studentList) {
                System.out.println(student1);
            }
        }
    }

    insert和save的区别?

    insert强调新增,如果插入数据的主键id重复则会报错;

    save较为通用,当保存数据的主键id为空时,则等同于insert新增;当主键id重复时,则执行更新操作,相当于根据主键id更新对应数据;repository不具备update方法。

    delete系列方法的注意点

    和spring-data-jpa的repository一样,delete系列方法,虽然有delete(entity)和deleteAll(list<entity>),但是要注意它并不是条件删除,而是根据主键id删除。如果传入的实例没有设置主键,则删除方法会报错。

    QBE查询时的细节问题

    spring-data-mongo封装的repository支持QBE,不支持QBC(spring-data-jpa封装的specificationexecutor支持QBC)。如上例,再进行QBE查询时,需要注意几个细节问题:

    1.基础类型的属性不要忽视,即使没有在传参的实例中进行值设定,但是由于基础类型都有默认值(比如 int型默认为0),此时repository.findAll(example)时,也会把它作为条件之一,且是相等匹配。所以不要忘记添加上忽略该属性:

    // age 不作为条件(注意由于age是int型,默认值是0,不是null,所以查询时会被作为条件之一)
    .withIgnorePaths("age")

    2.其他属性值没有赋值,默认是null,repository查询时会默认去除,不作为条件,为了保险,建议也加上这句:

    // 忽略null属性(默认忽略,可以不添加)
    .withIgnoreNullValues();

    6.使用spring封装的mongoTemplate实现复杂查询(分组、聚合,复杂条件查询)

    如上,使用repository可以实现基本的条件查询,也支持QBE,进行一定的复杂条件查询,但是当遇到分组、聚合查询时,或者遇到既要条件查询、又要分页、还要排序时,repository就显得有点不够用了。这个时候就需要用到spring封装的mongoTemplate,它支持分组、聚合查询,也支持使用QBC进行更加复杂的条件查询(灵活组合更多)。

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class StudentRepositoryTest {
        //使用spring封装的mongoTemplate实现CRUD操作,可以实现复杂查询:聚合、分组、多表查询等,因此推荐使用template
        @Resource
        private MongoTemplate mongoTemplate;
    
        @Test
        public void testTemplate(){
            // insert 和 save与repository是一样的
            Student student = new Student();
            student.setName("hack");
            student.setAddress("合肥瑶海");
            student.setAge(27);
            mongoTemplate.insert(student);
    
            student.setAge(28);
            mongoTemplate.save(student);
    
    
            // remove(entity) 和 repository的delete(entity)是一样的,实例的其他属性值不作为条件,仅根据主键id删除
            Student student1 = new Student();
            student1.setId("5fdc1aa49cf6074668f55418");
            student1.setAddress("安徽合肥高新彩虹路幼儿园");
            student1.setName("李刚");
            mongoTemplate.remove(student1);
    
            //条件查询:QBC
            Query query = new Query();
            Criteria criteria = new Criteria();
            query.addCriteria(criteria.where("age").gte(4).lt(7));
            query.addCriteria(criteria.where("address").regex("合肥"));
            //查询4<=age<7且address包含"合肥"的所有结果
            List<Student> students = mongoTemplate.find(query, Student.class);
            for (Student stu : students) {
                System.out.println(stu.toString());
            }
            System.out.println("-----------------华丽的分割线-------------------");
    
            Pageable pageable = PageRequest.of(0,3);
            query.with(pageable);
            query.with(Sort.by(Sort.Order.desc("age")));
            //根据age倒序排序,并且分页查询
            List<Student> list = mongoTemplate.find(query, Student.class);
            for (Student stu : list) {
                System.out.println(stu);
            }
        }
    
        @Test
        public void testAggregation(){
            //分组聚合:注意分组的字段直接作为新数据集的实体类主键,所以这里NameGroup中用id来接收name,不能为其他属性名,否则为空值。
            AggregationResults<NameGroup> aggregate = mongoTemplate.aggregate(Aggregation.newAggregation(
                    Aggregation.group("name").count().as("count"))
                    , Student.class, NameGroup.class);
            Iterator<NameGroup> iterator = aggregate.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next().toString());
            }
            System.out.println("-----------------华丽的分割线-------------------");
            //使用Aggregation预设各种条件查询和分组规则
            Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(new Criteria().where("age").gte(4).lt(28))
                    ,Aggregation.group("name").count().as("count"));
            AggregationResults<NameGroup> nameGroups = mongoTemplate.aggregate(aggregation, Student.class, NameGroup.class);
            Iterator<NameGroup> groupIterator = nameGroups.iterator();
            while (groupIterator.hasNext()){
                System.out.println(groupIterator.next().toString());
            }
        }
    }

    ***:注意使用主键id接收分组的属性,这里不能写成其他,必须是id。


    以上系个人理解,如果存在错误,欢迎大家指正。原创不易,转载请注明出处!

    展开全文
  • MongoDB

    2021-07-22 19:48:46
    MongoDBMongoDB下载与安装环境下载与安装MongoDB的基本操作MongoDB集合数据操作(CURD)数据添加数据查询数据更新数据删除MongoDB 聚合操作聚合操作简介MongoDB 聚合操作分类单目的聚合操作聚合管道(Aggregation ...

    MongoDB


    里面已经收录上百套项目资料,自行获取。
    无套路分享:

    http://106.75.108.26:8001/index?empid=15
    建议收藏:会一直持续更新。。。

    MongoDB下载与安装

    环境

    软件版本
    centos7

    下载与安装

    1、这里提供 4.4.7的版本

    https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.7.tgz
    

    其他版本可以去官网自行选择
    官网:

    https://www.mongodb.com/try/download
    

    在这里插入图片描述
    2、使用上传工具将安装包上传到Linux虚拟机上(我这里使用的是rz)
    在这里插入图片描述
    3、将压缩包解压即可

    tar -zxvf mongodb-linux-x86_64-rhel70-4.4.7.tgz
    

    在这里插入图片描述
    4、指定配置文件方式的启动

    ./bin/mongod -f mongo.conf
    
    配置文件样例: 
    dbpath=/data/mongo/  # 需要保证该路径是存在的,不然启动会报错
    port=27017 
    bind_ip=0.0.0.0 
    fork=true 
    logpath = /data/mongo/MongoDB.log 
    logappend = true 
    auth=false
    

    在这里插入图片描述在这里插入图片描述
    MongoDB启动和参数说明

    参数 			说明 
    dbpath 			数据库目录,默认/data/db 
    port 			监听的端口,默认27017 
    bind_ip 		监听IP地址,默认全部可以访问 
    fork 			是否已后台启动的方式登陆 
    logpath 		日志路径 
    logappend 		是否追加日志 
    auth 			是开启用户密码登陆 config 指定配置文件
    

    mongo shell 的启动

    启动mongo shell 
    	./bin/mongo 
    指定主机和端口的方式启动 
    	./bin/mongo --host=主机IP --port=端口
    

    在这里插入图片描述

    MongoDB的基本操作

    查看数据库 
    	show dbs; 
    切换数据库 如果没有对应的数据库则创建 
    	use 数据库名; 
    创建集合 
    	db.createCollection("集合名") 
    查看集合 
    	show tables; show collections; 
    删除集合 
    	db.集合名.drop(); 
    删除当前数据库 
    	db.dropDatabase();
    

    MongoDB集合数据操作(CURD)

    数据添加

    1. 插入单条数据 db.集合名.insert(文档) 
      		文档的数据结构和JSON基本一样。
      		所有存储在集合中的数据都是BSON格式。
      		BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON。
    2. 例如: 
      		db.lg_resume_preview.insert({name:"张晓峰",birthday:new ISODate("2000-07-01"),expectSalary:15000,gender:0,city:"bj"})
      		没有指定 _id 这个字段 系统会自动生成 当然我们也可以指定 _id
      		( _id 类型是ObjectId 类型是一个12字节 BSON 类型数据,有以下格式:
      			前4个字节表示时间戳 ObjectId("对象Id字符串").getTimestamp() 来获取
      			接下来的3个字节是机器标识码
      			紧接的两个字节由进程id组成(PID)
      			最后三个字节是随机数。) 
    3. 插入多条数据
      			db.集合名.insert([文档,文档])
    

    数据查询

    比较条件查询
    db.集合名.find(条件)

    操作条件格式例子RDBMS中的条件
    等于{key:value}db.col.find({字段名:值}).pretty()where 字段名=值
    大于{key:{$gt:value}}db.col.find({字段名:{$gt:值}}).pretty()where 字段名>值
    小于{key:$lt:value}}db.col.find({字段名:{$lt:值}}).pretty()where 字段名<值
    大于等于{key:{$gte:value}}db.col.find({字段名:{$gte:值}}).pretty()where 字段名>=值
    小于等于{key:$lte:value}}db.col.find({字段名:{$lte:值}}).pretty()where 字段名<=值
    不等于{key:$ne:value}}db.col.find({字段名:{$ne:值}}).pretty()where 字段名!=值

    逻辑条件查询

    and 条件 
    	MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件 
    	db.集合名.find({key1:value1, key2:value2}).pretty()
    or 条件
    	db.集合名.find({$or:[{key1:value1}, {key2:value2}]}).pretty() 
    not 条件 
    	db.集合名.find({key:{$not:{$操作符:value}}).pretty()
    

    分页查询
    db.集合名.find({条件}).sort({排序字段:排序方式})).skip(跳过的行数).limit(一页显示多少数据)

    数据更新

    调用update

    $set :设置字段值 
    $unset :删除指定字段 
    $inc:对修改的值进行自增 
    db.集合名.update( 
    	<query>, 
    	<update>,
    	 { 
    	 	upsert: <boolean>, 
    	 	multi: <boolean>,
    	 	writeConcern: <document> 
    	 }
    )
    
    参数说明: 
    
    query : update的查询条件,类似sql update查询内where后面的。 
    update : update的对象和一些更新的操作符(如$set,$inc...)等,也可以理解为sql update中 set后面的 
    upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认 是false,不插入。 
    multi : 可选,MongoDB 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查 出来多条记录全部更新。 
    writeConcern :可选,用来指定mongod对写操作的回执行为比如写的行为是否需要确认。
    
    举例: db.集合名.update({条件},{$set:{字段名:值}},{multi:true})
    
    writeConcern 包括以下字段: 
    { w: <value>, j: <boolean>, wtimeout: <number> } 
    	w:指定写操作传播到的成员数量 
    比如:
    	 w=1(默认):则要求得到写操作已经传播到独立的Mongod实例或副本集的primary成员的确认 
    	 w=0:则不要求确认写操作,可能会返回socket exceptions和 networking errors 
    	 w="majority":要求得到写操作已经传播到大多数具有存储数据具有投票的(data-bearing voting )成员(也就是 members[n].votes 值大于0的成员)的确认 
    	
    	 j:要求得到Mongodb的写操作已经写到硬盘日志的确认 
    比如:
     j=true:要求得到Mongodb(w指定的实例个数)的写操作已经写到硬盘日志的确认。j=true本身并不保证 因为副本集故障而不会回滚。 wtimeout:指定write concern的时间限制,只适用于w>1的情况 
     wtimeout在超过指定时间后写操作会返回error,即使写操作最后执行成功,当这些写操作返回时, 
     MongoDB不会撤消在wtimeout时间限制之前执行成功的数据修改。 如果未指定wtimeout选项且未指定write concern级别,则写入操作将无限期阻止。 指定wtimeout值 为0等同于没有wtimeout选项。
    

    数据删除

    db.collection.remove( 
    	<query>, 
    	{
    		 justOne: <boolean>, 
    		 writeConcern: <document> 
    	} 
    )
    参数说明: 
    query :(可选)删除的文档的条件。 
    justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
     writeConcern :(可选)用来指定mongod对写操作的回执行为。
    

    MongoDB 聚合操作

    聚合操作简介

    聚合是MongoDB的高级查询语言,它允许我们通过转化合并由多个文档的数据来生成新的在单个文档
    里不存在的文档信息。一般都是将记录按条件分组之后进行一系列求最大值,最小值,平均值的简单操
    作,也可以对记录进行复杂数据统计,数据挖掘的操作。聚合操作的输入是集中的文档,输出可以是一
    个文档也可以是多个文档。

    MongoDB 聚合操作分类

    • 单目的聚合操作(Single Purpose Aggregation Operation)
    • 聚合管道(Aggregation Pipeline)
    • MapReduce 编程模型

    单目的聚合操作

    单目的聚合命令常用的有:count() 和 distinct()

    db.lg_resume_preview.find({}).count()
    

    聚合管道(Aggregation Pipeline)

    db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION) 
    如: 
    db.lg_resume_preview.aggregate([{$group:{_id:"$city",city_count:{$sum:1}}}])
    

    MongoDB中聚合(aggregate)主要用于统计数据(诸如统计平均值,求和等),并返回计算后的数据结果。
    表达式:处理输入文档并输出。表达式只能用于计算当前聚合管道的文档,不能处理其它的文档。

    表达式描述
    $sum计算总和
    $avg计算平均值
    $min获取集合中所有文档对应值的最小值
    $max获取集合中所有文档对应值的最大值
    $push在结果文档中插入值到一个数组中
    $addToSet在结果文档中插入值到一个数组中,但数据不重复
    $first根据资源文档的排序获取第一个文档数据
    $last根据文档资源文档的排序获取最后一个文档数据

    MongoDB 中使用 db.COLLECTION_NAME.aggregate([{},...])方法来构建和使用聚合管道,每个
    文档通过一个由一个或者多个阶段(stage)组成的管道,经过一系列的处理,输出相应的结果。
    MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作
    是可以重复的。
    这里我们介绍一下聚合框架中常用的几个操作:

    $group:将集合中的文档分组,可用于统计结果。
    $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及 嵌套文档。
    $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
    $limit:用来限制MongoDB聚合管道返回的文档数。 
    $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
    $sort:将输入文档排序后输出。 
    $geoNear:输出接近某一地理位置的有序文档。
    
    db.lg_resume_preview.aggregate( 
    	[	{$group : {_id: "$city", avgSal:{$avg:"$expectSalary"}}}, 
    		{$project : {city: "$city", salary : "$avgSal"}}
    	])
    
    db.lg_resume_preview.aggregate(
    	 [	{$group:{_id: "$city",count:{$sum : 1}}}, 
    		{$match:{count:{$gt:1}}} 
    	 ])
    

    MapReduce 编程模型

    Pipeline查询速度快于MapReduce,但是MapReduce的强大之处在于能够在多台Server上并行执行复
    杂的聚合逻辑。MongoDB不允许Pipeline的单个聚合操作占用过多的系统内存,如果一个聚合操作消
    耗20%以上的内存,那么MongoDB直接停止操作,并向客户端输出错误消息。
    MapReduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结
    果合并成最终结果(REDUCE)。

    >db.collection.mapReduce( 
    	function() {emit(key,value);}, //map 函数 
    	function(key,values) {return reduceFunction}, //reduce 函数 
    	{ 
    		out: collection, 
    		query: document, 
    		sort: document, 
    		limit: number, 
    		finalize: <function>, 
    		verbose: <boolean> 
    	}
    )
    

    使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历
    collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。
    参数说明:

    • map:是JavaScript 函数,负责将每一个输入文档转换为零或多个文档,生成键值对序列,作为reduce 函数参数
    • reduce:是JavaScript函数,对map操作的输出做合并的化简的操作(将key-value变成keyvalues,也就是把values数组变成一个单一的值value)
    • out:统计结果存放集合
    • query: 一个筛选条件,只有满足条件的文档才会调用map函数。
    • sort:和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
    • limit:发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)
    • finalize:可以对reduce输出结果再一次修改
    • verbose:是否包括结果信息中的时间信息,默认为fasle
    db.lg_resume_preview.mapReduce( 
    	function() { emit(this.city,this.expectSalary); },
    	function(key, value) {return Array.avg(value)},
    	{
    		query:{expectSalary:{$gt: 15000}}, 
    		out:"cityAvgSal" 
    	} 
    )
    

    MongoDB索引Index

    什么是索引

    索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表
    中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作
    用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引目标是提高数据库的查
    询效率,没有索引的话,查询会进行全表扫描(scan every document in a collection),数据量
    大时严重降低了查询效率。默认情况下Mongo在一个集合(collection)创建时,自动地对集合
    的_id创建了唯一索引。

    索引类型

    单键索引 (Single Field)

    MongoDB支持所有数据类型中的单个字段索引,并且可以在文档的任何字段上定义。
    对于单个字段索引,索引键的排序顺序无关紧要,因为MongoDB可以在任一方向读取索引。
    单个例上创建索引:
    db.集合名.createIndex({“字段名”:排序方式})
    特殊的单键索引 过期索引 TTL ( Time To Live)
    TTL索引是MongoDB中一种特殊的索引, 可以支持文档在一定时间之后自动过期删除,目前TTL索引
    只能在单字段上建立,并且字段类型必须是日期类型。

    db.集合名.createIndex({"日期字段":排序方式}, {expireAfterSeconds: 秒数})
    

    复合索引(Compound Index)

    通常我们需要在多个字段的基础上搜索表/集合,这是非常频繁的。 如果是这种情况,我们可能会考虑
    在MongoDB中制作复合索引。 复合索引支持基于多个字段的索引,这扩展了索引的概念并将它们扩展
    到索引中的更大域。
    制作复合索引时要注意的重要事项包括:字段顺序与索引方向。

    db.集合名.createIndex( { "字段名1" : 排序方式, "字段名2" : 排序方式 } )
    

    多键索引(Multikey indexes)

    针对属性包含数组数据的情况,MongoDB支持针对数组中每一个element创建索引,Multikey
    indexes支持strings,numbers和nested documents

    地理空间索引(Geospatial Index)

    针对地理空间坐标数据创建索引。
    2dsphere索引,用于存储和查找球面上的点
    2d索引,用于存储和查找平面上的点

    db.company.insert( 
    	{ 
    		loc : { type: "Point", coordinates: [ 116.482451, 39.914176 ] }, 
    		name: "大望路地铁", 
    		category : "Parks" 
    	}
    )
    db.company.ensureIndex( { loc : "2dsphere" } ) 
    参数不是1或-1,为2dsphere 或者 2d。还可以建立组合索引。 
    db.company.find({
    	 "loc" : {
    	 	"$geoWithin" : { 
    	 		"$center":[[116.482451,39.914176],0.05] 
    	 	} 
    	 } 
    })
    

    全文索引

    MongoDB提供了针对string内容的文本查询,Text Index支持任意属性值为string或string数组元素的
    索引查询。注意:一个集合仅支持最多一个Text Index,中文分词不理想 推荐ES。

    db.集合.createIndex({"字段": "text"}) 
    db.集合.find({"$text": {"$search": "coffee"}})
    

    哈希索引 Hashed Index

    针对属性的哈希值进行索引查询,当要使用Hashed index时,MongoDB能够自动的计算hash值,无
    需程序计算hash值。注:hash index仅支持等于查询,不支持范围查询。

    db.集合.createIndex({"字段": "hashed"})
    

    MongoDB实战

    MongoDB的适用场景

    • 网站数据:Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
    • 缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源过载。
    • 大尺寸、低价值的数据:使用传统的关系型数据库存储一些大尺寸低价值数据时会比较浪费,在此之前,很多时候程序员往往会选择传统的文件进行存储。
    • 高伸缩性的场景:Mongo 非常适合由数十或数百台服务器组成的数据库,Mongo 的路线图中已经包含对MapReduce引擎的内置支持以及集群高可用的解决方案。
    • 用于对象及JSON 数据的存储:Mongo 的BSON数据格式非常适合文档化格式的存储及查询。

    MongoDB的行业具体应用场景

    • 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存 储,方便查询、更新。
    • 物流场景,使用MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
    • 社交场景,使用 MongoDB存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引 实现附近的人、地点等功能。
    • 物联网场景,使用 MongoDB存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这 些信息进行多维度的分析。
    • 直播,使用 MongoDB存储用户信息、礼物信息等。

    如何抉择是否使用MongoDB

    应用特征Yes / No
    应用不需要事务及复杂 join 支持必须 Yes
    新应用,需求会变,数据模型无法确定,想快速迭代开发
    应用需要2000-3000以上的读写QPS(更高也可以)
    应用需要TB甚至 PB 级别数据存储?
    应用发展迅速,需要能快速水平扩展?
    应用要求存储的数据不丢失?
    应用需要99.999%高可用?
    应用需要大量的地理位置查询、文本查询

    Java 访问MongoDB

    maven 依赖

    <dependency> 
    	<groupId>org.mongodb</groupId> 
    	<artifactId>mongo-java-driver</artifactId> 
    	<version>3.10.1</version> 
    </dependency>
    

    文档添加

    MongoClient mongoClient = new MongoClient("192.168.211.133", 37017); 
    MongoDatabase database = mongoClient.getDatabase("lg_resume"); 
    MongoCollection<Document> collection = database.getCollection("lg_resume_preview"); 
    Document document = Document.parse(
    	 "{name:'lisi',city:'bj',birth_day:new ISODate('2001-08- 01'),
    	 expectSalary:18000}"); 
    collection.insertOne(document );
    mongoClient.close();
    

    文档查询

    MongoClient mongoClient = new MongoClient("192.168.211.133", 37017); 
    MongoDatabase database = mongoClient.getDatabase("lg_resume"); 
    MongoCollection<Document> collection = database.getCollection("lg_resume_preview"); 
    Document sdoc=new Document(); 
    //按expectSalary倒序 
    sdoc.append("expectSalary", -1); 
    FindIterable<Document> findIterable = collection.find().sort(sdoc);
    for (Document document : findIterable) {
    	System.out.println(document); 
    }
    mongoClient.close();
    

    文档查询过滤

    MongoClient mongoClient = new MongoClient("192.168.211.133", 37017); 
    MongoDatabase database = mongoClient.getDatabase("lg_resume");
    MongoCollection<Document> collection = database.getCollection("lg_resume_preview"); 
    Document sdoc=new Document(); 
    //按expectSalary倒序 
    sdoc.append("expectSalary", -1); 
    FindIterable<Document> findIterable = collection.find(Filters.gt("expectSalary",21000)).sort(sdoc); 
    for (Document document : findIterable) { 
    	System.out.println(document); 
    }
    mongoClient.close();
    

    Spring 访问MongoDB

    第1步:基于maven新建工程 导入依赖的包

    <dependency> 
    	<groupId>org.springframework.data</groupId> 
    	<artifactId>spring-data-mongodb</artifactId> 
    	<version>2.0.9.RELEASE</version> 
    </dependency>
    

    第2步:在配置文件中配置 MongoTemplate

    <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xmlns:context="http://www.springframework.org/schema/context" 
    	xmlns:mongo="http://www.springframework.org/schema/data/mongo" 
    	xsi:schemaLocation=" http://www.springframework.org/schema/beans 
    		http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context 
    		http://www.springframework.org/schema/context/spring-context.xsd 
    		http://www.springframework.org/schema/data/mongo 
    		http://www.springframework.org/schema/data/mongo/spring-mongo.xsd"> 
    
    	<!-- 构建MongoDb工厂对象 --> 
    	<mongo:db-factory id="mongoDbFactory" client-uri="mongodb://192.168.211.133:37017/lg_resume"></mongo:db-factory> 
    	<!-- 构建 MongoTemplate 类型的对象 --> 
    	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> 
    		<constructor-arg index="0" ref="mongoDbFactory"></constructor-arg> 
    	</bean> 
    	<!-- 开启组件扫描 --> 
    	<context:component-scan base-package="com.lagou"></context:component-scan> 
    </beans>
    

    第3步:DAO 实现类注入 MongoTemplate 完成增删改查

    @Autowired 
    protected MongoTemplate mongoTemplate;
    

    第4步: 从Spring容器中获取DAO对象 进行测试 (注意:要开启组件扫描)

    Spring Boot 访问 MongoDB

    MongoTemplate 的方式

    第1步:基于maven新建springboot工程

    <dependency> 
    	<groupId>org.springframework.boot</groupId> 
    	<artifactId>spring-boot-starter-data-mongodb</artifactId> 
    	<version>2.2.2.RELEASE</version> 
    </dependency>
    

    第2步: 配置文件application.properties

    spring.data.mongodb.host=192.168.211.133 
    spring.data.mongodb.port=37017 
    spring.data.mongodb.database=lg_resume
    

    第3步: DAO 实现类 注入 MongoTemplate 完成增删改查

    @Autowired 
    protected MongoTemplate mongoTemplate;
    

    第4步: 从Spring容器中获取DAO对象 进行测试

    MongoRepository 的方式

    第1步:基于maven新建springboot工程

    <dependency> 
    	<groupId>org.springframework.boot</groupId> 
    	<artifactId>spring-boot-starter-data-mongodb</artifactId> 
    	<version>2.2.2.RELEASE</version> 
    </dependency>
    

    第2步: 配置文件application.properties

    spring.data.mongodb.host=192.168.211.133 
    spring.data.mongodb.port=37017 
    spring.data.mongodb.database=lg_resume
    

    第3步:编写实体类 并在实体类上打@Document(“集合名”)
    第4步:编写 Repository 接口 继承 MongoRepository

    方法具体参考:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-m
    ethods.query-creation
    如果内置方法不够用 就自己定义 如:定义find|read|get 等开头的方法进行查询

    第5步: 从Spring容器中获取Repository对象 进行测试

    MongoDB架构

    MongoDB逻辑结构

    在这里插入图片描述
    MongoDB 与 MySQL 中的架构相差不多,底层都使用了可插拔的存储引擎以满足用户的不同需要。用
    户可以根据程序的数据特征选择不同的存储引擎,在最新版本的 MongoDB 中使用了 WiredTiger 作为默
    认的存储引擎,WiredTiger 提供了不同粒度的并发控制和压缩机制,能够为不同种类的应用提供了最
    好的性能和存储率。
    在存储引擎上层的就是 MongoDB 的数据模型和查询语言了,由于 MongoDB 对数据的存储与 RDBMS
    有较大的差异,所以它创建了一套不同的数据模型和查询语言。

    MongoDB的数据模型

    描述数据模型

    内嵌
    内嵌的方式指的是把相关联的数据保存在同一个文档结构之中。MongoDB的文档结构允许一个字
    段或者一个数组内的值作为一个嵌套的文档。
    引用
    引用方式通过存储数据引用信息来实现两个不同文档之间的关联,应用程序可以通过解析这些数据引
    用来访问相关数据。

    如何选择数据模型

    选择内嵌:

    1. 数据对象之间有包含关系 ,一般是数据对象之间有一对多或者一对一的关系 。
    2. 需要经常一起读取的数据。
    3. 有 map-reduce/aggregation 需求的数据放在一起,这些操作都只能操作单个 collection。

    选择引用:

    1. 当内嵌数据会导致很多数据的重复,并且读性能的优势又不足于覆盖数据重复的弊端 。
    2. 需要表达比较复杂的多对多关系的时候 。
    3. 大型层次结果数据集 嵌套不要太深。

    MongoDB 存储引擎

    存储引擎概述

    存储引擎是MongoDB的核心组件,负责管理数据如何存储在硬盘和内存上。MongoDB支持的存储引擎有 MMAPv1 ,WiredTiger和InMemory。InMemory存储引擎用于将数据只存储在内存中,只将少量的元数据 (meta-data)和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要Disk的IO操作,就能获取所需 的数据,InMemory存储引擎大幅度降低了数据查询的延迟(Latency)。从mongodb3.2开始默认的存储 引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1,mongodb4.x版本不再支持MMAPv1存储引 擎。

    storage: 
    	journal: 
    		enabled: true 
    	dbPath: /data/mongo/
    	##是否一个库一个文件夹 
    	directoryPerDB: true 
    	##数据引擎 
    	engine: wiredTiger 
    	##WT引擎配置 
    	WiredTiger: 
    		engineConfig: 
    			##WT最大使用cache(根据服务器实际情况调节) 
    			cacheSizeGB: 2
    			##是否将索引也按数据库名单独存储 
    			directoryForIndexes: true 
    			journalCompressor:none (默认snappy) 
    		##表压缩配置 
    		collectionConfig: blockCompressor: zlib (默认snappy,还可选none、zlib) 
    		##索引配置 
    		indexConfig: 
    			prefixCompression: true
    

    WiredTiger存储引擎优势

    1.文档空间分配方式 
    	WiredTiger使用的是BTree存储 MMAPV1 线性存储 需要Padding 
    2.并发级别 WiredTiger 
    	文档级别锁 MMAPV1引擎使用表级锁 
    3.数据压缩 
    	snappy (默认) 和 zlib ,相比MMAPV1(无压缩) 空间节省数倍。 
    4.内存使用 
    	WiredTiger 可以指定内存的使用大小。 
    5.Cache使用 
    	WT引擎使用了二阶缓存WiredTiger Cache, File System Cache来保证Disk上的数据的最终一 致性。
    	而MMAPv1 只有journal 日志
    

    WiredTiger引擎包含的文件和作用

    在这里插入图片描述

    • WiredTiger.basecfg: 存储基本配置信息,与 ConfigServer有关系
    • WiredTiger.lock: 定义锁操作
    • table*.wt: 存储各张表的数据
    • WiredTiger.wt: 存储table* 的元数据
    • WiredTiger.turtle:存储WiredTiger.wt的元数据
    • journal: 存储WAL(Write Ahead Log)

    WiredTiger存储引擎实现原理

    写请求
    WiredTiger的写操作会默认写入 Cache ,并持久化到 WAL (Write Ahead Log),每60s或Log文件达到2G
    做一次 checkpoint (当然我们也可以通过在写入时传入 j: true 的参数强制 journal 文件的同步 ,
    writeConcern
    { w: , j: , wtimeout: }) 产生快照文件。WiredTiger初始化时,恢复至最新的快照状态,然后再根据WAL
    恢复数据,保证数据的完整性。
    在这里插入图片描述
    Cache是基于BTree的,节点是一个page,root page是根节点,internal page是中间索引节点,leaf
    page真正存储数据,数据以page为单位读写。WiredTiger采用Copy on write的方式管理写操作
    (insert、update、delete),写操作会先缓存在cache里,持久化时,写操作不会在原来的leaf page
    上进行,而是写入新分配的page,每次checkpoint都会产生一个新的root page。

    checkpoint流程

    1.对所有的table进行一次checkpoint,每个table的checkpoint的元数据更新至WiredTiger.wt
    2.对WiredTiger.wt进行checkpoint,将该table checkpoint的元数据更新至临时文件 WiredTiger.turtle.set
    3.将WiredTiger.turtle.set重命名为WiredTiger.turtle。
    4.上述过程如果中间失败,WiredTiger在下次连接初始化时,首先将数据恢复至最新的快照状态,然后根 据WAL恢复数据,以保证存储可靠性。

    Journaling
    在数据库宕机时 , 为保证 MongoDB 中数据的持久性,MongoDB 使用了 Write Ahead Logging 向磁盘
    上的 journal 文件预先进行写入。除了 journal 日志,MongoDB 还使用检查点(checkpoint)来保证
    数据的一致性,当数据库发生宕机时,我们就需要 checkpoint 和 journal 文件协作完成数据的恢复工
    作。

    1. 在数据文件中查找上一个检查点的标识符
    2. 在 journal 文件中查找标识符对应的记录
    3. 重做对应记录之后的全部操作
      在这里插入图片描述

    MongoDB集群高可用

    MongoDB主从复制架构原理和缺陷

    master-slave架构中master节点负责数据的读写,slave没有写入权限只负责读取数据。
    在这里插入图片描述
    在主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在系统数据库local的 oplog.$main集合中,这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器 中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随 着操作过多,新的操作会覆盖旧的操作!

    主从结构没有自动故障转移功能,需要指定master和slave端,不推荐在生产中使用。
    mongodb4.0后不再支持主从复制!
    [main] Master/slave replication is no longer supported

    复制集replica sets

    什么是复制集

    在这里插入图片描述
    复制集是由一组拥有相同数据集的mongod实例做组成的集群。
    复制集是一个集群,它是2台及2台以上的服务器组成,以及复制集成员包括Primary主节点,secondary从节点和投票节点。 复制集提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,保证数据的安全 性。

    为什么要使用复制集

    1.高可用防止设备(服务器、网络)故障。 提供自动failover 功能。 技术来保证高可用
    2.灾难恢复 当发生故障时,可以从其他节点恢复 用于备份。
    3.功能隔离 我们可以在备节点上执行读操作,减少主节点的压力
    比如:用于分析、报表,数据挖掘,系统任务等。

    复制集集群架构原理

    一个复制集中Primary节点上能够完成读写操作,Secondary节点仅能用于读操作。Primary节点需要记
    录所有改变数据库状态的操作,这些记录保存在 oplog 中,这个文件存储在 local 数据库,各个Secondary
    节点通过此 oplog 来复制数据并应用于本地,保持本地的数据与主节点的一致。oplog 具有幂等性,即无
    论执行几次其结果一致,这个比 mysql 的二进制日志更好用。

    oplog的组成结构

    {
    "ts" : Timestamp(1446011584, 2), 
    "h" : NumberLong("1687359108795812092"),
     "v" : 2,
     "op" : "i", 
     "ns" : "test.nosql", 
     "o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), 
     "name" : "mongodb", "score" : "10"}
     }
    
    ts:操作时间,当前timestamp + 计数器,计数器每秒都被重置 
    h:	操作的全局唯一标识
    v:	oplog版本信息 
    op:操作类型 
    	i:	插入操作 
    	u:	更新操作 
    	d:	删除操作 
    	c:	执行命令(如createDatabase,dropDatabase) 
    n:	空操作,特殊用途 
    ns:操作针对的集合 
    o:	操作内容 
    o2:更新查询条件,仅update操作包含该字段
    

    复制集数据同步分为初始化同步和keep复制同步。初始化同步指全量从主节点同步数据,如果Primary
    节点数据量比较大同步时间会比较长。而keep复制指初始化同步过后,节点之间的实时同步一般是增量
    同步。
    初始化同步有以下两种情况会触发:
    (1) Secondary第一次加入。
    (2) Secondary落后的数据量超过了oplog的大小,这样也会被全量复制。

    MongoDB的Primary节点选举基于心跳触发。一个复制集N个节点中的任意两个节点维持心跳,每个节
    点维护其他N-1个节点的状态。
    在这里插入图片描述

    心跳检测:
    整个集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉。mongodb节点会向副本集中的其他节点每2秒就会发送一次pings包,
    如果其他节点在10秒钟之内没有返回就标示为不能访问。每个节点内部都会 维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息。如果主节点发现自己无法与 大部分节点通讯则把自己降级为secondary只读节点。

    主节点选举触发的时机:

    第一次初始化一个复制集
    Secondary节点权重比Primary节点高时,发起替换选举
    Secondary节点发现集群中没有Primary时,发起选举
    Primary节点不能访问到大部分(Majority)成员时主动降级

    当触发选举时,Secondary节点尝试将自身选举为Primary。主节点选举是一个二阶段过程+多数派协议。

    第一阶段: 检测自身是否有被选举的资格 如果符合资格会向其它节点发起本节点是否有选举资格的 FreshnessCheck,进行同僚仲裁
    第二阶段: 发起者向集群中存活节点发送Elect(选举)请求,仲裁者收到请求的节点会执行一系列合法性检查,如果检 查通过,则仲裁者(一个复制集中最多50个节点 其中只有7个具有投票权)给发起者投一票。 pv0通过30秒选举锁防止一次选举中两次投票。 pv1使用了terms(一个单调递增的选举计数器)来防止在一次选举中投两次票的情况。
    多数派协议: 发起者如果获得超过半数的投票,则选举通过,自身成为Primary节点。获得低于半数选票的原因,除了常 见的网络问题外,相同优先级的节点同时通过第一阶段的同僚仲裁并进入第二阶段也是一个原因。因此,当 选票不足时,会sleep[0,1]秒内的随机时间,之后再次尝试选举。

    复制集搭建

    在这里插入图片描述
    1.主节点配置 mongo_37017.conf

    # 主节点配置 
    dbpath=/data/mongo/data/server1 
    bind_ip=0.0.0.0 
    port=37017 
    fork=true 
    logpath=/data/mongo/logs/server1.log 
    replSet=lagouCluster
    

    2.从节点1配置 mongo_37018.conf

    dbpath=/data/mongo/data/server2 
    bind_ip=0.0.0.0 
    port=37018 
    fork=true 
    logpath=/data/mongo/logs/server2.log 
    replSet=lagouCluster
    

    3.从节点2配置 mongo_37019.conf

    dbpath=/data/mongo/data/server3 
    bind_ip=0.0.0.0 
    port=37019 
    fork=true 
    logpath=/data/mongo/logs/server3.log 
    replSet=lagouCluster
    

    4.初始化节点配置
    启动三个节点 然后进入任意一个节点 运行如下命令:

    var cfg ={"_id":"lagouCluster", 
    		"protocolVersion" : 1, 
    		"members":[ 
    			{"_id":1,"host":"192.168.211.133:37017","priority":10}, 
    			{"_id":2,"host":"192.168.211.133:37018"} 
    		] 
    	}
    rs.initiate(cfg) 
    rs.status()
    

    5.节点的动态增删

    增加节点 
    rs.add("192.168.211.133:37019") 
    删除slave 节点 
    rs.remove("192.168.211.133:37019")
    

    6.复制集操作演示
    进入主节点 ----- 插入数据 ------ 进入从节点验证
    注意:默认节点下从节点不能读取数据。调用 rs.slaveOk() 解决
    为了保证高可用,在集群当中如果主节点挂掉后,会自动 在从节点中选举一个 重新做为主节点。
    rs.status()
    节点说明:
    PRIMARY 节点: 可以查询和新增数据
    SECONDARY 节点:只能查询 不能新增 基于priority 权重可以被选为主节点
    ARBITER 节点: 不能查询数据 和新增数据 ,不能变成主节点

    复制集成员的配置参数

    参数字段类型说明取值说明
    _id整数_id:0复制集中的标识
    host字符串host:“主机:端口”节点主机名
    arbiterOnly布尔值arbiterOnly:true是否为仲裁(裁判)节点
    priority(权重)整数priority=0|1默认1,是否有资格变成主节点,取值范围0-1000,0永远不会变成主节点
    hidden布尔值hidden=true|false ,0|1隐藏,权重必须为0,才可以设置
    votes整数votes = 0|1投票,是否为投票节点,0不投票,1投票
    slaveDelay整数slaveDelay=3600从库的延迟多少秒
    buildIndexes布尔值buildIndexes=true|false,0|1主库的索引,从库也创建,_id索引无效

    举例:

    var cfg ={"_id":"lagouCluster",
    			"protocolVersion" : 1, 
    			"members":[ 
    				{"_id":1,"host":"192.168.211.133:37017","priority":10}, 
    				{"_id":2,"host":"192.168.211.133:37018","priority":0}, 
    				{"_id":3,"host":"192.168.211.133:37019","priority":5}, 
    				{"_id":4,"host":"192.168.211.133:37020","arbiterOnly":true}
    			] 
    		}; 
    // 重新装载配置,并重新生成集群节点。 
    rs.reconfig(cfg) 
    //重新查看集群状态 
    rs.status()
    

    有仲裁节点复制集搭建

    和上面的配置步骤相同 只是增加了 一个特殊的仲裁节点
    注入节点 执行 rs.addArb(“IP:端口”);
    rs.addArb(“192.168.211.133:37020”)

    分片集群 Shard Cluster

    什么是分片

    分片(sharding)是MongoDB用来将大型集合水平分割到不同服务器(或者复制集)上所采用的方法。 不需要功能强大的大型计算机就可以存储更多的数据,处理更大的负载。

    为什么要分片

    1.存储容量需求超出单机磁盘容量。
    2.活跃的数据集超出单机内存容量,导致很多请求都要从磁盘读取数据,影响性能。
    3.IOPS超出单个MongoDB节点的服务能力,随着数据的增长,单机实例的瓶颈会越来越明显。
    4.副本集具有节点数量限制。

    垂直扩展:增加更多的CPU和存储资源来扩展容量。
    水平扩展:将数据集分布在多个服务器上。水平扩展即分片。

    分片的工作原理

    在这里插入图片描述
    分片集群由以下3个服务组成:
    Shards Server: 每个shard由一个或多个mongod进程组成,用于存储数据。
    Router Server: 数据库集群的请求入口,所有请求都通过Router(mongos)进行协调,不需要在应用程 序添加一个路由选择器,Router(mongos)就是一个请求分发中心它负责把应用程序的请求转发到对应的 Shard服务器上。
    Config Server: 配置服务器。存储所有数据库元信息(路由、分片)的配置。

    片键(shard key)
    为了在数据集合中分配文档,MongoDB使用分片主键分割集合。
    区块(chunk)
    在一个shard server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard
    server内部一部分数据。MongoDB分割分片数据到区块,每一个区块包含基于分片主键的左闭右开的
    区间范围。
    分片策略

    • 范围分片(Range based sharding)
      在这里插入图片描述

    范围分片是基于分片主键的值切分数据,每一个区块将会分配到一个范围。
    范围分片适合满足在一定范围内的查找,例如查找X的值在[20,30)之间的数据,mongo 路由根据
    Config server中存储的元数据,可以直接定位到指定的shard的Chunk中。
    缺点: 如果shard key有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个chunk,无
    法扩展写的能力。

    • hash分片(Hash based sharding)
      在这里插入图片描述

    Hash分片是计算一个分片主键的hash值,每一个区块将分配一个范围的hash值。
    Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围
    分片的不足,缺点是不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找
    出满足条件的文档。

    • 组合片键 A + B(散列思想 不能是直接hash)
      数据库中没有比较合适的片键供选择,或者是打算使用的片键基数太小(即变化少如星期只有7天
      可变化),可以选另一个字段使用组合片键,甚至可以添加冗余字段来组合。一般是粗粒度+细粒
      度进行组合。

    合理的选择shard key
    无非从两个方面考虑,数据的查询和写入,最好的效果就是数据查询时能命中更少的分片,数据写入时
    能够随机的写入每个分片,关键在于如何权衡性能和负载。

    分片集群的搭建过程

    在这里插入图片描述

    1.配置 并启动config 节点集群

    节点1 config-17017.conf

    # 数据库文件位置 
    dbpath=config/config1 
    #日志文件位置 
    logpath=config/logs/config1.log 
    # 以追加方式写入日志 
    logappend=true 
    # 是否以守护进程方式运行 
    fork = true 
    bind_ip=0.0.0.0 port = 17017 
    # 表示是一个配置服务器 
    configsvr=true 
    #配置服务器副本集名称 
    replSet=configsvr
    

    节点2 config-17018.conf

    # 数据库文件位置
    dbpath=config/config2 
    #日志文件位置 
    logpath=config/logs/config.log 
    # 以追加方式写入日志 
    logappend=true 
    # 是否以守护进程方式运行 
    fork = true bind_ip=0.0.0.0 
    port = 17018 
    # 表示是一个配置服务器 
    configsvr=true 
    #配置服务器副本集名称 
    replSet=configsvr
    

    节点3 config-17019.conf

    # 数据库文件位置 
    dbpath=config/config3 
    #日志文件位置 
    logpath=config/logs/config3.log 
    # 以追加方式写入日志 
    logappend=true 
    # 是否以守护进程方式运行 
    fork = true 
    bind_ip=0.0.0.0 
    port = 17019 
    # 表示是一个配置服务器 
    configsvr=true 
    #配置服务器副本集名称 
    replSet=configsvr
    

    启动配置节点

    ./bin/mongod -f config/config-17017.conf 
    ./bin/mongod -f config/config-17018.conf 
    ./bin/mongod -f config/config-17019.conf
    

    进入任意节点的mongo shell 并添加 配置节点集群 注意use admin

    ./bin/mongo --port 17017 
    
    use admin 
    
    var cfg ={"_id":"configsvr", 
    		"members":[ 
    			{"_id":1,"host":"192.168.211.133:17017"}, 
    			{"_id":2,"host":"192.168.211.133:17018"}, 
    			{"_id":3,"host":"192.168.211.133:17019"}
    		]
    	};
    rs.initiate(cfg)
    

    2.配置shard集群

    shard1集群搭建37017到37019

    dbpath=shard/shard1/shard1-37017 
    bind_ip=0.0.0.0 
    port=37017 
    fork=true 
    logpath=shard/shard1/shard1-37017.log 
    replSet=shard1 
    shardsvr=true 
    
    dbpath=shard/shard1/shard1-37018 
    bind_ip=0.0.0.0 
    port=37018 
    fork=true 
    logpath=shard/shard1/logs/shard1-37018.log 
    replSet=shard1 
    shardsvr=true 
    
    dbpath=shard/shard1/shard1-37019 
    bind_ip=0.0.0.0 
    port=37019 
    fork=true 
    logpath=shard/shard1/logs/shard1-37019.log 
    replSet=shard1 
    shardsvr=true
    
    启动每个mongod 然后进入其中一个进行集群配置 
    var cfg ={"_id":"shard1", 
    	"protocolVersion" : 1, 
    	"members":[ 
    		{"_id":1,"host":"192.168.211.133:37017"}, 
    		{"_id":2,"host":"192.168.211.133:37018"}, 
    		{"_id":3,"host":"192.168.211.133:37019"} 
    	]
    };
    rs.initiate(cfg) 
    rs.status()
    

    shard2集群搭建47017到47019

    dbpath=shard/shard2/shard2-47017 
    bind_ip=0.0.0.0 
    port=47017 
    fork=true 
    logpath=shard/shard2/logs/shard2-47017.log 
    replSet=shard2 
    shardsvr=true 
    
    dbpath=shard/shard2/shard2-47018
    bind_ip=0.0.0.0 
    port=47018 
    fork=true 
    logpath=shard/shard2/logs/shard2-47018.log 
    replSet=shard2 
    shardsvr=true 
    
    dbpath=shard/shard2/shard2-47019 
    bind_ip=0.0.0.0 
    port=47019 
    fork=true 
    logpath=shard/shard2/logs/shard2-47019.log 
    replSet=shard2 
    shardsvr=true
    
    启动每个mongod 然后进入其中一个进行集群配置 
    var cfg ={"_id":"shard2", 
    	"protocolVersion" : 1, 
    	"members":[ 
    		{"_id":1,"host":"192.168.211.133:47017"}, 
    		{"_id":2,"host":"192.168.211.133:47018"}, 
    		{"_id":3,"host":"192.168.211.133:47019"} 
    	]
    };
    rs.initiate(cfg) 
    rs.status()
    

    3.配置和启动 路由节点

    route-27017.conf

    port=27017 
    bind_ip=0.0.0.0 
    fork=true 
    logpath=route/logs/route.log 
    configdb=configsvr/192.168.211.133:17017,192.168.211.133:17018,192.168.211.133:17019
    

    启动路由节点使用 mongos (注意不是mongod)

    ./bin/mongos -f route/route-27017.conf
    

    4. mongos(路由)中添加分片节点

    进入路由mongos

    mongo --port 27017 
    sh.status() 
    sh.addShard("shard1/192.168.211.133:37017,192.168.211.133:37018,192.168.211.133:37019"); 
    sh.addShard("shard2/192.168.211.133:47017,192.168.211.133:47018,192.168.211.133:47019"); 
    sh.status()
    

    5. 开启数据库和集合分片(指定片键)

    继续使用mongos完成分片开启和分片大小设置

    为数据库开启分片功能 
    sh.enableSharding("lagou_resume") 
    为指定集合开启分片功能 
    sh.shardCollection("lagou_resume.lagou_resume_datas",{"片键字段名如 name":索引说明})
    

    6. 向集合中插入数据测试

    通过路由循环向集合中添加数

    use lagou_resume; 
    
    for(var i=1;i<= 1000;i++){ 
    	db.lagou_resume_datas.insert({"name":"test"+i, 
    				salary:(Math.random()*20000).toFixed(2)}); 
    }
    

    7.验证分片效果

    分别进入 shard1 和 shard2 中的数据库 进行验证

    MongoDB安全认证

    安全认证概述

    MongoDB 默认是没有账号的,可以直接连接,无须身份验证。实际项目中肯定是要权限验证的,否则
    后果不堪设想。从2016年开始 发生了多起MongoDB黑客赎金事件,大部分MongoDB安全问题 暴露出
    了安全问题的短板其实是用户,首先用户对于数据库的安全不重视,其次用户在使用过程中可能没有养
    成定期备份的好习惯,最后是企业可能缺乏有经验和技术的专业人员。所以对MongoDB进行安全认证
    是必须要做的。

    用户相关操作

    切换到admin数据库对用户的添加

    use admin;
    db.createUser(userDocument):用于创建 MongoDB 登录用户以及分配权限的方法

    db.createUser( {
    	user: "账号", 
    	pwd: "密码", 
    	roles: [ 
    		{ role: "角色", db: "安全认证的数据库" }, 
    		{ role: "角色", db: "安全认证的数据库" } 
    	] 
    })
    

    user:创建的用户名称,如 admin、root 、lagou
    pwd:用户登录的密码
    roles:为用户分配的角色,不同的角色拥有不同的权限,参数是数组,可以同时设置多个
    role:角色,MonngoDB 已经约定好的角色,不同的角色对应不同的权限 后面会对role做详细解释
    db:数据库实例名称,如 MongoDB 4.0.2 默认自带的有 admin、local、config、test 等,即为哪个数
    据库实例 设置用户
    举例:

    db.createUser({
    	user:"root", 
    	pwd:"123321", 
    	roles:[
    		{role:"root",db:"admin"}
    	] 
    })
    

    修改密码

     db.changeUserPassword( 'root' , 'rootNew' );
    

    用户添加角色

     db.grantRolesToUser( '用户名' , [{ role: '角色名' , db: '数据库名'}])
    

    以auth 方向启动mongod

    ./bin/mongod -f conf/mongo.conf --auth
    (也可以在mongo.conf 中添加auth=true 参数)
    

    验证用户

     db.auth("账号","密码")
    

    删除用户

     db.dropUser("用户名")
    

    角色

    数据库内置的角色

    read:允许用户读取指定数据库 
    readWrite:允许用户读写指定数据库 
    dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问 system.profile 
    userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户 
    clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限 
    readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限 
    readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限 
    userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 
    dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限 
    root:只在admin数据库中可用。超级账号,超级权限 
    dbOwner:库拥有者权限,即readWrite、dbAdmin、userAdmin角色的合体
    

    各个类型用户对应的角色

    数据库用户角色:read、readWrite 
    数据库管理角色:dbAdmin、dbOwner、userAdmin 
    集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager 
    备份恢复角色:backup、restore; 
    所有数据库角色:readAnyDatabase、readWriteAnyDatabase、
    			userAdminAnyDatabase、 dbAdminAnyDatabase 
    超级用户角色:root 这里还有几个角色间接或直接提供了系统超级用户的访问
    			(dbOwner 、userAdmin、 userAdminAnyDatabase)
    

    单机安全认证实现流程

    创建 mydb1 数据库并创建了两个用户,zhangsan 拥有读写权限,lisi 拥有只读权限测试这两个账户的权限。
    以超级管理员登录测试权限。

    1. 创建管理员

    MongoDB 服务端开启安全检查之前,至少需要有一个管理员账号,admin 数据库中的用户都被视为管理员
    如果 admin 库没有任何用户的话,即使在其他数据库中创建了用户,启用身份验证,默认的连接方式
    依然会有超级权限,即仍然可以不验证账号密码照样能进行 CRUD,安全认证相当于无效。

    >use admin 
    switched to db admin 
    > db 
    admin 
    > db.createUser({ 
    	user:"root", 
    	pwd:"123456", 
    	roles:[
    		{role:"root",db:"admin"}
    	] 
    })
    

    2. 创建普通用户

    如下所示 mydb1 是自己新建的数据库,没安全认证之前可以随意 CRUD,其余的都是 mongoDB自带的数据库

    >show dbs
    admin 0.000GB 
    config 0.000GB 
    local 0.000GB 
    mydb1 0.000GB 
    > use mydb1 
    switched to db mydb1 
    > db.c1.insert({name:"testdb1"}) 
    > > db.c1.insert({name:"testdb1"}) 
    > show tables 
    c1
    c2
    > db.c1.find() 
    ...
    
    • 为 admin 库创建管理员之后,现在来为 普通数据库创建普通用户,以 mydb1 为例,方式与创建
      管理员一致,切换到指定数据库进行创建即可。
    • 如下所示,为 mydb1 数据库创建了两个用户,zhangsan 拥有读写权限,lisi 拥有只读权限,密码
      都是 123456.
    use mydb1 
    switched to db mydb1 
    > db 
    mydb1 
    > db.createUser({ 
    	user:"zhangsan",  
    	pwd:"123456", 
    	roles:[
    		{role:"readWrite",db:"mydb1"}
    	]  
    }) 
    
    > db.createUser({ 
    	user:"lisi", 
    	pwd:"123456", 
    	roles:[
    		{role:"read",db:"mydb1"}
    	] 
    })
    

    接着从客户端关闭 MongoDB 服务端,之后服务端会以安全认证方式进行启动

    > use admin 
    switched to db admin 
    > db.shutdownServer() 
    server should be down...
    

    3. MongoDB 安全认证方式启动

    mongod --dbpath=数据库路径 --port=端口 --auth
    也可以在配置文件中 加入 auth=true

    4. 分别以普通用户登录验证权限

    普通用户现在仍然像以前一样进行登录,如下所示直接登录进入 mydb1 数据库中,登录是成功的,只
    是登录后日志少了很多东西,而且执行 show dbs 命令,以及 show tables 等命令都是失败的,即使没
    有被安全认证的数据库,用户同样操作不了,这都是因为权限不足,一句话:用户只能在自己权限范围
    内的数据库中进行操作

    mongo localhost:57017/mydb1 
    > show dbs
    

    如下所示,登录之后必须使用 db.auth(“账号”,“密码”) 方法进行安全认证,认证通过,才能进行权限范
    围内的操作

    > db.auth("zhangsan","123456")
     1
    > show dbs 
    mydb1 0.000GB 
    > show tables 
    c1
    c2
    

    5.以管理员登录验证权限

    客户端管理员登录如下所示 管理员 root 登录,安全认证通过后,拥有对所有数据库的所有权限。

    mongo localhost:57017 
    > use admin 
    switched to db admin 
    > db.auth("root","root") 
    1
    > show dbs 
    ...
    

    分片集群安全认证

    1.开启安全认证之前 进入路由创建管理员和普通用户

    完全参考上一步即可

    use lagou_resume
    db.createUser({
    	 user:"chengdao",
    	 pwd:"123456",
    	 roles:[{role:"readWrite",db:"lagou_resume"}]
    })
    

    2.关闭所有的配置节点 分片节点 和 路由节点

    安装psmisc 
    yum install psmisc 
    安装完之后可以使用killall 命令 快速关闭多个进程 
    killall mongod
    

    3.生成密钥文件 并修改权限

    openssl rand -base64 756 > data/mongodb/testKeyFile.file 
    chmod 600 data/mongodb/keyfile/testKeyFile.file
    

    4.配置节点集群和分片节点集群开启安全认证和指定密钥文件

    auth=true 
    keyFile=data/mongodb/testKeyFile.file
    

    5.在路由配置文件中 设置密钥文件

    keyFile=data/mongodb/testKeyFile.file
    

    6.启动所有的配置节点 分片节点 和 路由节点 使用路由进行权限验证

    可以编写一个shell 脚本 批量启动

    ./bin/mongod -f config/config-17017.conf 
    ./bin/mongod -f config/config-17018.conf 
    ./bin/mongod -f config/config-17019.conf 
    ./bin/mongod -f shard/shard1/shard1-37017.conf 
    ./bin/mongod -f shard/shard1/shard1-37018.conf 
    ./bin/mongod -f shard/shard1/shard1-37019.conf 
    ./bin/mongod -f shard/shard2/shard2-47017.conf 
    ./bin/mongod -f shard/shard2/shard2-47018.conf 
    ./bin/mongod -f shard/shard2/shard2-47019.conf 
    ./bin/mongos -f route/route-27017.conf
    

    7.Spring boot 连接安全认证的分片集群

    spring.data.mongodb.username=账号 
    spring.data.mongodb.password=密码 
    #spring.data.mongodb.uri=mongodb://账号:密码@IP:端口/数据库名
    
    展开全文
  • 0-依赖组件准备

    2018-12-02 22:08:00
    mysql: ... mongo: https://github.com/mhcvs2/docker/blob/master/wekan/mongodb.yml rabbitmq: https://github.com/mhcvs2/docker/blob/master/ra...
  • 看字面意思,mongodb组件需要依赖一个名叫kerberos的东东 于是在项目目录(昨天新建的BlogWebSite) npm install kerberos (网上有说加上 -g参数全局安装,我尝试了下,还是会报这个warning,于是还是本地安装一份算...
  • mongodb

    2018-05-07 14:06:00
    1、下载安装包:mongodb-win32-x86_64-2008plus-ssl-3.4.7-signed 2、安装mongodb 3、启动mongodb 4、连接mongodb 5、接下来会展示库的增删改查,表的增删改查,记录的增删改查: 6、数据库的增删改查: ...
  • 安装MongoDB 4以上版本时出现Service ‘MongoDB Server' (MongoDB) failed to start...错误,导致安装不成功,经本人测试原来是缺少这个依赖文件,直接安装即可……
  • 个人网络应用 你好! 这是我的个人Web应用程序的存储库,该应用程序是使用MERN(MongoDB,Express,React和Node.js)... 当前,该项目的React代码严重依赖于类组件,但是很快它将通过更新的功能组件和挂钩进行更新。
  • springboot2.X 使用spring-data组件MongoDB做CURD 使用背景 基于快速开发,需求不稳定的情况, 我决定使用MongoDB作为存储数据库,搭配使用spring-data 因为快速开发,使用spring data可以直接在类上建表等其他操作,...
  • SpringBoot整合MongoDB

    万次阅读 2019-04-04 15:42:59
    MongoDB 和 Redis 一样,都属于NoSQL 型的数据库,即非关系型的数据库;区别在于 Redis 的读写速度非常快, 一些操作频繁的场景很适合,作为临时缓存;而 MongoDB 善于存储,面对的是百万级甚至千万级的数据。 ...
  • MongoDB介绍

    2020-01-12 15:09:03
    一、概述 1.MongoDB是什么?用一句话总结 ...(1)MongoDB提出的是文档、集合的概念,使用BSON(类JSON)作为其数据模型结构,其结构是面向对象的而不是二维表,存储一个用户在MongoDB中是这样子的。 { ...
  • vue-mall Vue + Koa2 + MongoDB 全栈商城WebApp UI组件库 有赞出品的移动端组件库Vant,基于Vue.js 说明 前端就不用说了,npm run dev起服务。主要这里服务端涉及mongoDB数据库,简单说...记得先npm i 把依赖装一下
  • centOS 安装mongodb

    2017-10-17 13:52:43
    MongoDB官方源中包含以下几个依赖包: mongodb-org: MongoDB元数据包,安装时自动安装下面四个组件包: 1.mongodb-org-server: 包含MongoDB守护进程和相关的配置和初始化脚本。 2.mongodb-org-mongos: 包含mongos...
  • MongoDB总结

    千次阅读 2017-09-08 14:53:55
    MongoDB的官方文档基本是how to do的介绍,而关于how it worked却少之又少,本人也刚买了《MongoDB TheDefinitive Guide》的影印版,还没来得及看,本文原作者将其书中一些关于MongoDB内部现实方面的一些知识介绍...
  • 安装MongoDB

    2019-01-20 19:14:45
    CentOS上安装epel-release的yum源之后就可以安装MongoDB,但是版本都是比较老的,如果使用MongoDB官方的yum就可以安装到比较新版本的MongoDB。需要联网 vi /etc/yum.repos.d/mongodb-4.0.repo 配置MongoDB的yum源: ...
  • 用于单元测试依赖于 Mongo 的组件(例如存储库)。 与mongo-component 兼容。 安装 将以下依赖项添加到您的 project.clj 文件中: 用法 需要库: ( require '[component.fongo :refer [new-fongo] '...
  • mongodb安装

    2019-02-09 15:34:52
    CentOS上安装epel-release的yum源之后就可以安装MongoDB,但是版本都是比较老的,如果使用MongoDB官方的yum就可以安装到比较新版本的MongoDB。 &nbsp; &nbsp; &nbsp; &nbsp; 配置MongoDB的yum源: ...
  • MongoDB 快速入门

    2021-06-01 01:38:51
    文章目录1 MongoDB介绍1.1 应用场景1.2 什么时候选择MongoDB1.3 MongoDB简介1.4 MongoDB体系结构1.5 数据类型1.6 MongoDB的特点2 安装2.1 windows系统安装2.2 Linux系统安装2.3 Docker安装2.4 连接mongodb2.4.1 ...
  • 相信使用MongoDB的你一定着迷于它横向扩展带来的强大集群性能,然而你有注意到使用Auto-sharding机制带来的性能损耗吗。Jbrandstettercs在comSysto Blog上对使用Auto-sharding进行分片带来的性能损耗进行了详细的...
  • MongoDB入门

    2020-02-25 22:22:19
    MongoDB 闪亮登场 自我介绍 MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系...
  • Jersey MongoDB的使用

    2016-11-21 15:33:26
    关于MongoDB数据库我们不做过多介绍,通过本次教程可以实现mongodb增删改查的简单使用,如果需要对mongodb有深入的学习,请参考MongoDB教程 引入依赖 <!--引入mongodb依赖 --> <!-- ===============
  • 最近比较忙,忙的都没空理csdn了,今天我继续迈着魔鬼般的步伐,摩擦摩擦……总结下最近学到的MongoDB的知识。 1.认识Spring Data MongoDB  之前还的确不知道Spring连集成Nosql的东西都实现了,还以为自己又要...
  • 单元测试需要将各个组件与其依赖项隔离开。 依赖被替换为嘲笑 ,这模拟某些使用情况。 这样,我们可以在各种外部上下文场景中验证测试中组件的行为。 可以使用模拟业务逻辑服务对Web组件进行单元测试。 可以针对...
  • 运行以下命令以启动mongodb [mongodb安装目录] / bin / mongod --dbpath。 打开另一个终端(dos dos Windows提示符),进入项目文件夹 运行以下命令以下载系统依赖项:npm install 执行以下命令以启动应用程序npm ...
  • MongoDB基础

    2018-03-15 09:08:00
    MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像...
  • 说明:Mongodb和redis是开发中常用的中间件,Redis的安装使用比较简单就不写了,只说本地也就是Windows安装Mongodb。 SpringBoot集成MongoDB和Redis 文中还有一个彩蛋Hutool 1.下载最新稳定版 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,920
精华内容 7,568
关键字:

mongodb依赖组件