初入MongoDB

1 MongoDB简介

1.1 什么是MongoDB

MongoDB 是一个跨平台的,面向文档的数据库,是当前 NoSQL 数据库产品中最热门的一种。它介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富,最像关系数据库的产品。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型,MongoDB 的官方网站地址:http://www.mongodb.org/。

1.2 MongoDB特点

对于数据量大、写入操作频繁、价值较低这样的数据,我们更适合使用MongoDB来实现数据的存储,MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库,具体特点总结如下:
    (1)面向集合存储,易于存储对象类型的数据
    (2)模式自由
    (3)支持动态查询
    (4)支持完全索引,包含内部对象
    (5)支持复制和故障恢复
    (6)使用高效的二进制数据存储,包括大型对象(如视频等)
    (7)自动处理碎片,以支持云计算层次的扩展性
    (8)支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++语言的驱动程序,社区中也提供了对 Erlang 及.NET 等平台的驱动程序
    (9) 文件存储格式为 BSON(一种 JSON 的扩展)


1.3 MongoDB体系结构

MongoDB 的逻辑结构是一种层次结构。主要由:文档(document)、集合(collection)、数据库(database)这三部分组成的。逻辑结构是面向用户的,用户使用 MongoDB 开发应用程序使用的就是逻辑结构。

    (1)MongoDB 的文档(document),相当于关系数据库中的一行记录。
    (2)多个文档组成一个集合(collection),相当于关系数据库的表。
    (3)多个集合(collection),逻辑上组织在一起,就是数据库(database)。
    (4)一个 MongoDB 实例支持多个数据库(database)。
      文档(document)、集合(collection)、数据库(database)的层次结构如下图:

下表是MongoDB与MySQL数据库逻辑结构概念的对比

MongoDb 关系型数据库Mysql
数据库(databases) 数据库(databases)
集合(collections) 表(table)
文档(document) 行(row)


 

 

1.5 数据类型

  • null:用于表示空值或者不存在的字段,{“x”:null}
  • 布尔型:布尔类型有两个值true和false,{“x”:true}
  • 数值:shell默认使用64为浮点型数值。{“x”:3.14}或{“x”:3}。
  • 对于整型值:可以使用NumberInt(4字节符号整数)或NumberLong(8字节符号整数),{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
  • 字符串:UTF-8字符串都可以表示为字符串类型的数据,{“x”:“呵呵”}
  • 日期:日期被存储为自新纪元依赖经过的毫秒数,不存储时区,{“x”:new Date()}
  • 正则表达式:查询时,使用正则表达式作为限定条件,语法与JavaScript的正则表达式相同,{“x”:/[abc]/}
  • 数组:数据列表或数据集可以表示为数组,{“x”: [“a“,“b”,”c”]}
  • 内嵌文档:文档可以嵌套其他文档,被嵌套的文档作为值来处理,{“x”:{“y”:3 }}
  • 对象Id:对象id是一个12字节的字符串,是文档的唯一标识,{“x”: objectId() }
  • 二进制数据:二进制数据是一个任意字节的字符串。它不能直接在shell中使用。如果要
  • 将非utf-字符保存到数据库中,二进制数据是唯一的方式。
  • 代码:查询和文档中可以包括任何JavaScript代码,{“x”:function(){/…/}}
     

2 走进MongoDB

2.1 MongoDB安装与启动

MongoDB 预编译二进制包下载地址:https://www.mongodb.com/download-center/community

       安装过程中,你可以通过点击 "Custom(自定义)" 按钮来设置你的安装目录。(4.0以下版本安装方式)

点击下一步直到安装完成,为了方便每次启动,我将MongoDB安装后的bin目录设置到环境变量path中

① 首先打开命令提示符,创建一个用于存放数据的目录

md  d:\data


       ② 启动服务(服务器端)
 

mongod ‐‐dbpath=d:\data

启动日志

在启动信息中可以看到,mongoDB的默认端口是27017,如果我们想改变默认的启动端口,可以通过--port来指定端口。

③ 在命令提示符输入以下命令即可连接上 MongoDB(客户端)

mongo

④ 退出mongodb

exit


2.2 Docker 环境下MongoDB安装
 

在宿主机创建mongo容器  

docker run ‐di ‐‐name=demo_mongo ‐p 27017:27017 mongo

远程连接

mongo 192.168.92.131

2.3 常用命令

以吐槽表spit为例演示MongoDB常用命令
 

字段名称 字段含义 字段类型 备注
_id ID 文本  
content 吐槽内容 文本  
publishtime 发布日期 日期  
userid 发布人ID 文本  
nickname 发布人暱称 文本  
visits 浏览量 整型  
thumbup 点赞数 整型  
share 分享数 整型  
comment 回复数 整型  
state 是否可见 文本  
parentid 上级ID 文本  
  •  选择和创建数据库, 如果不存在就自动创建
use 数据库名称
use spitdb
  • 插入集合和文档,集合如果不存在就自动创建
db.集合名称.insert(文档数据)
db.spit.insert({content:"内容1",userid:"7774",nickname:"云云",visits:NumberInt(60)})
  • 查询集合
db.集合名称.find()
db.spit.find()

每条文档会有一个叫_id的字段,这个相当于关系数据库中表的主键,当你在插入文档记录时没有指定该字段,MongoDB会自动创建,其类型是ObjectID类型。如果我们在插入文档记录时指定该字段也可以,其类型可以是ObjectID类型,也可以是MongoDB支持的任意类型。如下:

db.spit.insert({_id:"1",content:"内容123",userid:"232232",nickname:"小明"})
  • 条件查询
db.spit.find(条件)
//查询userid为232232的记录
db.spit.find({userid:"232232"})

如果你只需要返回符合条件的第一条数据,我们可以使用findOne命令来实现
 

db.spit.findOne({nickname:"小明"})

如果你想返回指定条数的记录,可以在find方法后调用limit来返回结果,例如:

db.spit.find({nickname:"小明"}).limit(3)
  • 修改文档
db.集合名称.update(条件,修改后的数据)

执行后,我们会发现,这条文档除了visits字段其它字段都不见了,为了解决这个问题,我们需要使用修改器$set来实现,命令如下:
 

db.spit.update({_id:"2"},{$set:{visits:NumberInt(2000)}})
  • 列值增长

如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用$inc运算符来实现

db.spit.update({_id:"1"},{$inc:{visits:NumberInt(1)}})

 

  • 删除文档
db.集合名称.remove(条件)
//删除userid=1211的文档
db.spit.remove({userid:"1211"})

以下语句可以将数据全部删除,请慎用 

db.spit.remove({})
  • 统计条数

统计记录条件使用count()方法。以下语句统计spit集合的记录数

db.spit.count()//统计全部
db.spit.count({userid:"1013"})//条件统计
  • 模糊查询

MongoDB的模糊查询是通过正则表达式的方式实现的。格式为:

/模糊查询字符串/

例如,我要查询吐槽内容包含“内容”的所有文档,代码如下:

db.spit.find({content:/内容/})

如果要查询吐槽暱称中以“小”开头的,代码如下:

db.spit.find({nickname:/^小/})

 

  • 大于 小于 不等于

<, <=, >, >=  这个操作符也是很常用的,格式如下:

db.集合名称.find({ "field" : { $gt: value }}) // 大于: field > value
db.集合名称.find({ "field" : { $lt: value }}) // 小于: field < value
db.集合名称.find({ "field" : { $gte: value }}) // 大于等于: field >= value
db.集合名称.find({ "field" : { $lte: value }}) // 小于等于: field <= value
db.集合名称.find({ "field" : { $ne: value }}) // 不等于: field != value

示例:查询吐槽浏览量大于1000的记录

db.spit.find({visits:{$gt:1000}})

 

  •  包含与不包含

包含使用$in操作符,示例:查询吐槽集合中userid字段包含1013和1014的文档

db.spit.find({userid:{$in:["1013","1014"]}})

不包含使用$nin操作符。示例:查询吐槽集合中userid字段不包含1013和1014的文档

db.spit.find({userid:{$nin:["1013","1014"]}})
  • 条件连接

我们如果需要查询同时满足两个以上条件,需要使用$and操作符将条件进行关联。(相当于SQL的and)

格式为:$and:[ { },{ },{ } ]

示例:查询吐槽集合中visits大于等于1000 并且小于2000的文档

db.spit.find({$and:[{visits:{$lt:2000}},{visits:{$gte:1000}}]})

如果两个以上条件之间是或者的关系,我们使用$or操作符进行关联,与前面and的使用方式相同

格式为:$or:[ { },{ },{ } ]

示例:查询吐槽集合中userid为1013,或者浏览量小于2000的文档记录

db.spit.find({$or:[{userid:"1013"},{visits:{$lt:2000}}]})

 

3 Java操作MongoDB

mongodb-driver是mongo官方推出的java连接mongoDB的驱动包,相当于JDBC驱动,我们通过一个入门的案例来了解mongodb-driver的基本使用
      ①  查询全部记录

  • 创建工程 mongoDemo, 引入依赖
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>3.8.2</version>
</dependency>
  • 创建测试类
public class MongoDemo {
    public static void main(String[] args) {
        //创建连接
        MongoClient mongoClient = new MongoClient("192.168.92.132");
        //获取数据库
        MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
        //获取集合
        MongoCollection<Document> spits = spitdb.getCollection("spit");
        //获取文档数据集合
        FindIterable<Document> documents = spits.find();
        //遍历集合
        for(Document document:documents){
            //取值时注意值的类型,不然会报类型转换错误
            System.out.println("id:"+document.getObjectId("_id"));
            System.out.println("内容:"+document.getString("content"));
            System.out.println("浏览量:"+document.getInteger("visits"));
        }
        //关闭连接
        mongoClient.close();
    }
}

② 条件查询

BasicDBObject对象:表示一个具体的记录,BasicDBObject实现了DBObject,是keyvalue的数据结构,用起来和HashMap是基本一致的。

  • 查询userid为2732的记录
public static void main(String[] args) {
    //创建连接
    MongoClient mongoClient = new MongoClient("192.168.92.132");
    //获取数据库
    MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
    //获取集合
    MongoCollection<Document> spits = spitdb.getCollection("spit");
    //构建条件
    BasicDBObject bosn = new BasicDBObject();
    bosn.put("userid","2732");
    //查询符合条件的文档记录
    FindIterable<Document> documents = spits.find(bosn);
    for(Document document:documents){
        //取值时注意值的类型,不然会报类型转换错误
        System.out.println("id:"+document.getObjectId("_id"));
        System.out.println("内容:"+document.getString("content"));
        System.out.println("浏览量:"+document.getInteger("visits"));
    }
    //关闭连接
    mongoClient.close();
}
  • 查询浏览量大于1000的记录
public static void main(String[] args) {
    //创建连接
    MongoClient mongoClient = new MongoClient("192.168.92.132");
    //获取数据库
    MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
    //获取集合
    MongoCollection<Document> spits = spitdb.getCollection("spit");
    //构建条件
    BasicDBObject bosn = new BasicDBObject("visits",new BasicDBObject("$gt",1000));
    //查询符合条件的文档记录
    FindIterable<Document> documents = spits.find(bosn);
    for(Document document:documents){
        //取值时注意值的类型,不然会报类型转换错误
        System.out.println("id:"+document.getObjectId("_id"));
        System.out.println("内容:"+document.getString("content"));
        System.out.println("浏览量:"+document.getInteger("visits"));
    }
    //关闭连接
    mongoClient.close();
}

③ 插入记录

public static void main(String[] args) {
    //创建连接
    MongoClient mongoClient = new MongoClient("192.168.92.132");
    //获取数据库
    MongoDatabase spitdb = mongoClient.getDatabase("spitdb");
    //获取集合
    MongoCollection<Document> spits = spitdb.getCollection("spit");
    //组装数据
    Map data=new HashMap();
    data.put("content","这是一个内容");
    data.put("userid","3232");
    data.put("nickname","明明");
    data.put("visits",1560);
    Document document = new Document(data);
    spits.insertOne(document);
    //关闭连接
    mongoClient.close();
}

3  SpringDataMongoDB

SpringData家族成员之一,用于操作MongoDb的持久层框架,封装了底层的mongodbdriver,官网主页: https://projects.spring.io/spring-data-mongodb

3.1 采用SpringDataMongoDB框架实现吐槽

  ① pom.xml引入依赖  

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

  ② 创建application.yml

#配置tomcat端口
server.port: 9006
#配置mongodb数据库连接
spring:
  data:
    mongodb:
      host: 192.168.92.132
      port: 27017
      database: spitdb

  ③ 创建启动类

@SpringBootApplication
public class SpitApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpitApplication.class);
    }

    @Bean
    public IdWorker idWorker(){
        return new IdWorker(1,1);
    }
}

3.2 基本增删改查API实现

  ① 创建实体类

public class Spit implements Serializable {
    private String _id;
    private String content;//吐槽内容
    private Date publishtime;//发布日期
    private String userid;//发布人ID
    private Integer visits;//浏览量
    private Integer thumbup;//点赞数
    private Integer share;//分享数
    private Integer comment;//回复数
    private String state;//是否可见
    private String parentid;//上级ID

    // getter and setter .....
}

  ② 创建数据访问接口

/
**
* 吐槽数据访问层
* @author Administrator
* *
/
public interface SpitDao extends MongoRepository<Spit, String>{
}

  ③ 创建业务逻辑类

@Service
public class SpitService {
    @Autowired
    private SpitDao spitDao;
    @Autowired
    private IdWorker idWorker;
	/**
	* 查询全部记录
	* @return
	*/
	public List<Spit> findAll(){
		return spitDao.findAll();
	} 
	/**
	* 根据主键查询实体
	* @param id
	* @return
	*/
	public Spit findById(String id){
		Spit spit = spitDao.findById(id).get();
		return spit;
	} 
	/**
	* 增加
	* @param spit
	*/
	public void add(Spit spit) {
		spit.set_id(idWorker.nextId()+""); //主键值
		spitDao.save(spit);
	} 
	
	/**
	* 修改
	* @param spit
	*/
	public void update(Spit spit) {
		spitDao.save(spit);
	} 
	/**
	* 删除
	* @param id
	*/
	public void deleteById(String id) {
		spitDao.deleteById(id);
	}
}

  ④ 创建controller类
 

@RestController
@CrossOrigin
@RequestMapping("/spit")
public class SpitController {
    @Autowired
    private SpitService spitService;
    /**
    * 查询全部数据
    * @return
    */
    @RequestMapping(method= RequestMethod.GET)
    public Result findAll(){
        return new Result(true, StatusCode.OK,"查询成功",spitService.findAll());
    } 
    /**
    * 根据ID查询
    * @param id ID
    * @return
    */
    @RequestMapping(value="/{id}",method=RequestMethod.GET)
    public Result findOne(@PathVariable String id){
        return new Result(true,StatusCode.OK,"查询成功",spitService.findById(id));
    } 
    /**
    * 增加
    * @param spit
    */
    @RequestMapping(method=RequestMethod.POST)
    public Result add(@RequestBody Spit spit ){
        spitService.add(spit);
        return new Result(true,StatusCode.OK,"增加成功");
    } 
    /**
    * 修改
    * @param spit
    */
    @RequestMapping(value="/{id}",method=RequestMethod.PUT)
    public Result update(@RequestBody Spit spit,@PathVariable String id )
    {
        spit.set_id(id);
        spitService.update(spit);
        return new Result(true,StatusCode.OK,"修改成功");
    } 
    /**
    * 删除
    * @param id
    */
    @RequestMapping(value="/{id}",method=RequestMethod.DELETE)
    public Result deleteById(@PathVariable String id ){
        spitService.deleteById(id);
        return new Result(true,StatusCode.OK,"删除成功");
    }
}

3.3 根据上级ID查询吐槽列表

    ① SpitDao新增方法定义

/**
* 根据上级ID查询吐槽列表(分页)
* @param parentid
* @param pageable
* @return
*/
public Page<Spit> findByParentid(String parentid,Pageable pageable)

   ② SpitService新增方法

/**
 * 根据上级ID查询吐槽数据(分页)
 * 查询一个吐槽下面的评论
 */
public PageResult<Spit> findByParentid(String pid,int page,int size){
    Pageable pageable =PageRequest.of(page-1,size);
    Page<Spit> pages = spitDao.findByParentid(pid, pageable);
   return new PageResult<Spit>(pages.getTotalElements(),pages.getContent());

}

 ③ SpitController新增方法

//根据上级ID查询吐槽数据(分页)
@GetMapping("/comment/{parentid}/{page}/{size}")
public Result<PageResult> findByParentId(@PathVariable("parentid") String id,@PathVariable int page,@PathVariable int size){
    return new Result<>(StatusCode.SUCCESS,true,"查询成功",spitService.findByParentid(id,page,size));
}

3.4 吐槽点赞

/**
* 点赞
* @param id
*/
public void updateThumbup(String id){
	Spit spit = spitDao.findById(id).get();
	spit.setThumbup(spit.getThumbup()+1);
	spitDao.save(spit);
}

以上方法虽然实现起来比较简单,但是执行效率并不高,因为我只需要将点赞数加1就可以了,没必要查询出所有字段修改后再更新所有字段,我们可以使用MongoTemplate类来实现对某列的操作。

 ① 修改SpitService

@Autowired
private MongoTemplate mongoTemplate;
/**
* 点赞
* @param id
*/
public void updateThumbup(String id){
    Query query=new Query();
    query.addCriteria(Criteria.where("_id").is(id));
    Update update=new Update();
    update.inc("thumbup",1);
    mongoTemplate.updateFirst(query,update,"spit");
}

② SpitController新增方法

/**
* 点赞
* @param id
* @return
*/
@RequestMapping(value="/thumbup/{id}",method=RequestMethod.PUT)
public Result updateThumbup(@PathVariable String id){
    spitService.updateThumbup(id);
    return new Result(true,StatusCode.OK,"点赞成功");
}

3.5 取消点赞

  我们可以通过redis控制用户点赞或者取消点赞

  ① pom.xml中加入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐data‐redis</artifactId>
</dependency>

  ② 修改application.yml

redis:
  host: 192.168.184.135

  ③  修改SpitService 点赞方法

//吐槽点赞
public String  updateThumbup(String id) {
    String message="";
    Query query = new Query();
    //添加条件
    query.addCriteria(Criteria.where("_id").is(id));
    Update update=new Update();
    String userid="7777";//模拟数据,实际应该取当前登陆的用户
    if("1".equals(redisTemplate.opsForValue().get("thumbup_"+userid+"_"+id))){
        //点赞取消
        update.inc("thumbup",-1);
        redisTemplate.opsForValue().set("thumbup_"+userid+"_"+id,"-1");
        message="点赞已取消";

    }else {
        //点赞
        update.inc("thumbup",1);
        redisTemplate.opsForValue().set("thumbup_"+userid+"_"+id,"1");
        message="点赞成功";
    }

    mongoTemplate.updateFirst(query,update,"spit" );
    return  message;
}

3.6 发布吐槽

修改SpitService的add方法

/**
* 发布吐槽(或吐槽评论)
* @param spit
*/
public void add(Spit spit){
    spit.set_id( idWorker.nextId()+"" );
    spit.setPublishtime(new Date());//发布日期
    spit.setVisits(0);//浏览量
    spit.setShare(0);//分享数
    spit.setThumbup(0);//点赞数
    spit.setComment(0);//回复数
    spit.setState("1");//状态
    if(spit.getParentid()!=null && !"".equals(spit.getParentid())){
    //如果存在上级ID,则是评论
    Query query=new Query();
    query.addCriteria(Criteria.where("_id").is(spit.getParentid()));
        Update update=new Update();
        update.inc("comment",1);
        mongoTemplate.updateFirst(query,update,"spit");
    } 
    spitDao.save(spit);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章