MongoDB入门以及MongoDB项目实践

作者:SunlightBright GitHUb地址:SunlightBright

MongoDB通过key-value的形式存储 数据,而value的值可以是字符串,也可以是文档。所以我们在使用的过程中非常方便。

技术:MongoDB,SpringBoot,SpringDataMongoDB

MongoDB 简介

MongoDB 是非关系型数据库中,最接近关系型数据库的,文档型数据库。它支持的查询功能非常强大。
MongoDB 是为快速开发互联网Web应用而设计的数据库系统。他的数据模型是面向文档的,这种文档是一种类似于JSON的结构,准确来说是一种支持二进制的BSON(Binary JSON)结构。

非关系性数据库

非关系性数据库,也被称为 NoSQL(Not only sql),主要有以下类型

示例:

序号数据库类型代表数据库解释
1键值存储数据库Redis、memcache遵循“键——值”模型,是最简单的数据库管理系统
2列存储数据库HBase按照列(由“键——值”对组成的列表)在数据文件中记录数据,以获得更好的请求及遍历效率。一行中的列数允许动态变化,且列的数目可达数百万,每条记录的关键码不同,支持多值列。
3文档型数据库MongoDB、Couchbase无固定结构,不同的记录允许有不同的列数和列类型。列允许包含多值,记录允许嵌套
4图形数据库Neo4j以“点——边”组成的网络(图结构)来存储数据
5时序数据库InfluxDB、RRDtool存储时间序列数据,每条记录都带有时间戳。如存储从感应器采集到的数据
6搜索引擎Elasticsearch、Solr存储的目的是为了搜索,主要功能是搜索

Mongodb Shell 编程

插入数据

插入数据比较简单,insert() 可以向集合插入一个或多个文档,而insertOne() 和 insertMany() 细化了insert() 方法,语法是一样的,命名规则上更清晰。

  • db.collection.insert() 可以向集合中插入一个或多个文档
  • db.collection.insertOne() 向集合中插入一个文档
  • db.collection.insertMany()向集合中插入多个文档

插入建议:
1 插入数据不能破坏原有的数据结构,造成不必要的麻烦。
2 批量插入数据,尽量一次执行多个文档,而不是多个文档执行多次方法。

// 插入一条数据,类型有字符串,数字,对象,集合
db.Sunlightuser.insert({"name":"Sunlight","age":24,"address":{"province":"甘肃","city":"兰州"},"ability":["JAVA","VUE"]})
// 插入多条数据
db.Sunlightuser.insert([
{"name":"Sunlight","age":24,"address":{"province":"甘肃","city":"兰州"},"ability":["JAVA","VUE"]},
{"name":"SunlightGit","age":24,"address":{"province":"甘肃","city":"兰州"},"ability":["JAVA","VUE","GIT"]}
])

查询数据

Mongodb的查询功能十分强大,有find() 和 findOne()。支持的查询条件有: l t 、 lt、 ltlte、 g t 、 gt、 gtgte、 n e 、 ne、 neor、 i n 、 in、 innin、 n o t 、 not、 notexists、$and、正则表达式等。

  • db.collection.find() 根据查询条件返回所有文档
  • db.collection.findOne() 根据查询条件返回第一个文档

查询建议:
1 查询所有数据,建议使用分页查询。
2 查询key建议用引号,对象的属性可以省略引号,内嵌的对象属性不能省略。比如下面的name可以省略,但address.province则不能。
3 尽量少用$or, $in 查询,效率很低。

// 查询所有(不推荐,一般使用分页查询)
db.Sunlightuser.find();
{"_id":ObjectId("5a9bbefa2f3fdfdf540a1be7"),"name":"Sunlight","age":25,"address":{"province":"甘肃省","city":"兰州"},"ability":["JAVA"]}
// 等于查询
db.Sunlightuser.find({"name":"Sunlight"});
// 模糊查询
db.Sunlightuser.find({"name":/Sunlight/});
// 或者查询
db.Sunlightuser.find({$or:[{"address.province":""},{"address.province":"甘肃"}]});
// 包含查询
db.Sunlightuser.find({"ability":{$in:["JAVA","VUE"]}});
// 不包含查询
db.Sunlightuser.find({"ability":{$nin:["JAVA","VUE"]}});
// 范围查询$gt , $lt , $gte , $lte , $ne
db.Sunlightuser.find({"age":{$gt:24}});
// 正则表达式查询(查询以WeiXin结尾的数据)
db.Sunlightuser.find({"name":/WeiXin$/});
// 按照条件统计数据
db.Sunlightuser.count({"name":/Sunlight/});
// 过滤重复内容(打印不重复的name值)
db.Sunlightuser.distinct("name");
// sort:排序(1表示升序 -1表示降序),skip:跳过指定数量,limit:每页查询数量
db.Sunlightuser.find().sort({"age":1}).skip(2).limit(3);
// 字段投影,(0表示不显示,1表示显示)
db.Sunlightuser.find({},{_id:0,name:1,address:1,aliblity:1});

更新数据

更新数据时,需要确保value的数据结构,是字符串,是集合,还是对象,不能破坏原有的数据结构。尽量使用修改器来帮忙完成操作。

  • db.collection.update() 可以修改、替换集合中的一个或多个文档,默认修改第一个,若要修改多个,则需要使用multi:true
  • db.collection.updateOne() 修改集合中的一个文档
  • db.collection.updateMany() 修改集合中的多个文档
  • db.collection.replaceOne() 替换集合中的一个文档

常用的修改器:
i n c ∗ ∗ : 数 值 类 型 属 性 自 增 ∗ ∗ inc** : 数值类型属性自增 ** inc:set : 用来修改文档中的指定属性
u n s e t ∗ ∗ : 用 来 删 除 文 档 的 指 定 属 性 ∗ ∗ unset** : 用来删除文档的指定属性 ** unset:push : 向数组属性添加数据
$addToSet : 向数组添加不重复的数据

更新建议:
1 更新数据不能破坏原有的数据结构。
2 正确使用修改器完成更新操作。

// 更新字符串属性
db.Sunlightuser.update({"name":"SunlightGit"},{$set:{"name":"Sunlight"}});
// 更新对象属性
db.Sunlightuser.update({"name":"Sunlight"},{$set:{"address.province":"甘肃省"}});
// 更新集合属性
db.Sunlightuser.update({"name":"Sunlight"},{$push:{"ability":"Node JS"}});
// 批量更新属性
db.Sunlightuser.updateMany({"name":"Sunlight"},{$set:{"age":25}});
// 批量更新属性,加参数multi:true
db.Sunlightuser.update({"name":"Sunlight"},{$set:{"age":25}},{multi:true});

删除数据

删除数据是一个非常谨慎的操作,实际开发中不会物理删除数据,只是逻辑删除。方便数据恢复和大数据分析。这里只简单介绍。

  • db.collection.remove() 删除集合中的一个或多个文档(默认删除多个)
  • db.collection.deleteOne() 删除集合中的一个文档
  • db.collection.deleteMany() 删除集合中的多个文档

SpringBoot MongoDB 整合

如果你觉得Spring整合MongoDB略显麻烦,那SpringBoot整合MongoDB就是你的福音。SpringBoot旨在零配置,只需简单的两个步骤即可。
第一步:在pom.xml文件中添加spring-boot-starter-data-mongodb

<!-- 添加对mongodb的支持 -->
<dependency>	
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

第二步:在application.properties文件中配置MongoDB数据库链接地址。
链接MongoDB数据库地址规则:spring.data.mongodb.uri=mongodb://account:password@ip:port/database
其中 account和password方便是链接数据库的账号和密码。而database是需要链接的数据库地址

# 没有账号密码可以简写
spring.data.mongodb.uri=mongodb://localhost:27017/Sunlightstu

Spring Data MongoDB 编程

Spring Data给我们提供了MongoTemplate类,极大的方便了我们的工作,但是若每个实体类都基于MongoTemplate重写一套CRUD的实现类,似乎显得有些笨重。于是我们可以将其简单的封装一下。步骤如下

第一步:创建用户实体类,其数据库表名就是类名首字母小写。
第二步:封装MongoTemplate类,实现增删改查,分页,排序,主键自增等常用功能。
第三步:创建封装类的Bean管理类,针对不同的实体类,需要配置不同的bean。
第四步:创建测试类,测试:注册,更新,分页,排序,查询用户功能。

创建用户实体类

用户实体类有五个字段,除了主键ID,其他四个分别代表四个常用的类型(字符串,数字,对象,集合)。为了简化开发,实体类建议不实用@Document注解重命名User在MongoDB数据库中的表名。
省略get/set方法和toString方法

import java.io.Serializable;
import java.util.ArrayList;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 用户实体类
 * @author Sunlight
 */
//@Document(collection = "Sunlight_user")  如果为了代码的通用性,建议不要使用
public class User implements Serializable{
	
	private static final long serialVersionUID = 1L;
	@Id
	private Long id;
	private String name;
	private Integer age;
	private Address address;
	private ArrayList ability;
}

public class Address{
	private Long id;
	private String province;
	private String city;
}

封装MongoTemplate类

SpringData提供的MongoTemplate类,极大的方便我们操作MongoDB数据库。可是它的很多方法都涉及到了Class,和CollectionName。针对不同的实体类,我们需要重复写不同的方法。这里,我们进一步封装,实现代码的高可用。
实现的思路大致:将Class作为一个参数,在初始化MongoTemplate的封装类时赋值。这里有一个约束条件是:CollectionName是Class类名的首字母小写。

import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;

@Repository
@SuppressWarnings({"unchecked", "rawtypes"})
public class SunlightMongoHelper {
	
	@Autowired(required = false)
	private MongoTemplate mongoTemplate; 
    /**
     * 实体类
     */
	private Class entityClass;
    /**
     * 数据库表名
     */
	private String collectionName;
    /**
     * 升序字段
     */
	private String orderAscField;
    /**
     * 降序字段
     */
	private String orderDescField;
	
	private static final String ID = "id";
    
	private static final String MONGODB_ID = "_id";
	
	public SunlightMongoHelper() {
	}

	public SunlightMongoHelper(Class entityClass) {
		this.entityClass = entityClass;
		this.collectionName = _getCollectionName();
	}

	public SunlightMongoHelper(Class entityClass, String collectionName) {
		this.entityClass = entityClass;
		this.collectionName = collectionName;
	}
	
	/**
	 * 通过Map创建实体类
	 * @param requestArgs Map,无需自带ID
	 */
	public Boolean save(Map<String, Object> requestArgs) {
		try {
			Object object = getEntityClass().newInstance();
			if (null == requestArgs.get(ID)) {
				requestArgs.put(ID, getNextId());
			}
			BeanUtils.populate(object, requestArgs);
			saveOrUpdate(object);
		} catch (Exception e) {
			e.printStackTrace();
			return Boolean.valueOf(false);
		}
		return Boolean.valueOf(true);
	}
	
	/**
	 * 通过对象创建实体类
	 */
	public Boolean saveOrUpdate(Object object) {
		try {
			this.mongoTemplate.save(object, this.collectionName);
		} catch (Exception e) {
			e.printStackTrace();
			return Boolean.valueOf(false);
		}
		return Boolean.valueOf(true);
	}
	
	/**
	 * 通过Map更新实体类具体字段,可以减少更新出错字段,执行的销率更高,需严格要求数据结构的正确性
	 * 
	 * @param requestArgs Map,需自带ID, 形如:{id: idValue, name: nameValue, ....}
	 */
	public Boolean update(Map<String, Object> requestArgs) {
		Object id = requestArgs.get(ID);
		if (null == id) {
			return Boolean.valueOf(false);
		}
		try {
			Update updateObj = new Update();
			requestArgs.remove(ID);
			for (String key : requestArgs.keySet()) {
				updateObj.set(key, requestArgs.get(key));
			}
			findAndModify(Criteria.where(ID).is(id), updateObj);
		} catch (Exception e) {
			e.printStackTrace();
			return Boolean.valueOf(false);
		}
		return Boolean.valueOf(true);
	}
	
	/**
	 * 根据查询条件返回所有数据,不推荐
	 */
	public List find(Criteria criteria) {
		Query query = new Query(criteria);
		_sort(query);
		return this.mongoTemplate.find(query, this.entityClass, this.collectionName);
	}
	
	/**
	 * 根据查询条件返回指定数量数据
	 */
	public List find(Criteria criteria, Integer pageSize) {
		Query query = new Query(criteria).limit(pageSize.intValue());
		_sort(query);
		return this.mongoTemplate.find(query, this.entityClass, this.collectionName);
	}

	/**
	 * 根据查询条件分页返回指定数量数据
	 */
	public List find(Criteria criteria, Integer pageSize, Integer pageNumber) {
		Query query = new Query(criteria).skip((pageNumber.intValue() - 1) * pageSize.intValue()).limit(pageSize.intValue());
		_sort(query);
		return this.mongoTemplate.find(query, this.entityClass, this.collectionName);
	}
	
	public Object findAndModify(Criteria criteria, Update update) {
		// 第一个参数是查询条件,第二个参数是需要更新的字段,第三个参数是需要更新的对象,第四个参数是MongoDB数据库中的表名
		return this.mongoTemplate.findAndModify(new Query(criteria), update, this.entityClass, this.collectionName);
	}
	
	/**
	 * @Title findById
	 * @Description 通过ID查询数据
	 * @param id 实体类ID
	 * @return
	 */
	public Object findById(Object id) {
		return this.mongoTemplate.findById(id, this.entityClass, this.collectionName);
	}
	
	/**
	 * @Title findOne
	 * @Description 通过查询条件返回一条数据
	 * @param id 实体类ID
	 * @return
	 */
	public Object findOne(Criteria criteria) {
		Query query = new Query(criteria).limit(1);
		_sort(query);
		return this.mongoTemplate.findOne(query, this.entityClass, this.collectionName);
	}
	
	// id自增长
	public String getNextId() {
		return getNextId(getCollectionName());
	}

	public String getNextId(String seq_name) {
		String sequence_collection = "seq";
		String sequence_field = "seq";
		DBCollection seq = this.mongoTemplate.getCollection(sequence_collection);
		DBObject query = new BasicDBObject();
		query.put(MONGODB_ID, seq_name);
		DBObject change = new BasicDBObject(sequence_field, Integer.valueOf(1));
		DBObject update = new BasicDBObject("$inc", change);
		DBObject res = seq.findAndModify(query, new BasicDBObject(), new BasicDBObject(), false, update, true, true);
		return res.get(sequence_field).toString();
	}
	
	private void _sort(Query query) {
		if (null != this.orderAscField) {
			String[] fields = this.orderAscField.split(",");
			for (String field : fields) {
				if (ID.equals(field)) {
					field = MONGODB_ID;
				}
				query.with(new Sort(Sort.Direction.ASC, new String[] { field }));
			}
		} else {
			if (null == this.orderDescField) {
				return;
			}
			String[] fields = this.orderDescField.split(",");
			for (String field : fields) {
				if (ID.equals(field)) {
					field = MONGODB_ID;
				}
				query.with(new Sort(Sort.Direction.DESC, new String[] { field }));
			}
		}
	}
	
	// 获取Mongodb数据库中的表名,若表名不是实体类首字母小写,则会影响后续操作
	private String _getCollectionName() {
		String className = this.entityClass.getName();
		Integer lastIndex = Integer.valueOf(className.lastIndexOf("."));
		className = className.substring(lastIndex.intValue() + 1);
		return StringUtils.uncapitalize(className);
	}
	
	public Class getEntityClass() {
		return entityClass;
	}
	public void setEntityClass(Class entityClass) {
		this.entityClass = entityClass;
	}
	public String getCollectionName() {
		return collectionName;
	}
	public void setCollectionName(String collectionName) {
		this.collectionName = collectionName;
	}
	public String getOrderAscField() {
		return orderAscField;
	}
	public void setOrderAscField(String orderAscField) {
		this.orderAscField = orderAscField;
	}
	public String getOrderDescField() {
		return orderDescField;
	}
	public void setOrderDescField(String orderDescField) {
		this.orderDescField = orderDescField;
	}
}

创建封装类的Bean管理类

这里用Bean注解修饰的方法名和测试类中SunlightMongoHelper 的变量名要保持一致。这样才能具体知道是哪个实体类的数据操作。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.Sunlight.pojo.User;
import com.Sunlight.repository.SunlightMongoHelper;

/**
 * SunlightMongoHelper的bean配置管理类 
 * @author Sunlight
 */
@Configuration
public class MongodbBeansConfig {
	
	@Bean // 该方法名很重要
	public SunlightMongoHelper userMongoHelper() {
		return new SunlightMongoHelper(User.class);
	}

}

MongoDB的测试类

主要测试MongoDB保存数据,更新字符串,更新数值,更新对象(文档),更新集合,分页查询几个常用方法。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.test.context.junit4.SpringRunner;
import com.Sunlight.StartApplication;
import com.Sunlight.pojo.Address;
import com.Sunlight.pojo.User;
import com.Sunlight.repository.SunlightMongoHelper;

@RunWith(SpringRunner.class)
@SpringBootTest(classes=StartApplication.class)
public class SpringbootStudyApplicationTests {
    
	@Autowired
	private SunlightMongoHelper userMongoHelper; 
    
	@Test
	public void createUser() {
		System.out.println("================> createUser");
		for (int i = 0; i < 25; i++) {	// 插入25条数据
			User user = new User();
			user.setId(Long.valueOf(userMongoHelper.getNextId(User.class.getName())));
			user.setAge(25 + i);
			user.setName("Sunlight-" + i);
			Address address = new Address();
			address.setId(Long.valueOf(userMongoHelper.getNextId(Address.class.getName()))); 
			address.setProvince("甘肃省");
			address.setCity("兰州市");
			user.setAddress(address);
			ArrayList<String> ability = new ArrayList<>();
			ability.add("Java");
			user.setAbility(ability);
			userMongoHelper.saveOrUpdate(user);
			System.out.println("user : " + user.toString());
		}
	}
    
	@Test
	public void updateUserName() {
		System.out.println("================> updateUserName");
		Map<String, Object> updateMap = new HashMap<>();
		// 查询name为Sunlight-1的数据,将name修改为SunlightBlog
		User user = (User) userMongoHelper.findOne(Criteria.where("name").is("Sunlight-1"));
		if (null == user) {
			System.out.println("================> User non-existent");
			return ;
		}
		updateMap.put("id", user.getId());
		updateMap.put("name", "SunlightBlog");
		userMongoHelper.update(updateMap);
	}
    
	@Test
	public void updateUserAddress() {
		System.out.println("================> updateUserAddress");
		Map<String, Object> updateMap = new HashMap<>();
		User user = (User) userMongoHelper.findOne(Criteria.where("name").is("Sunlight-3"));
		if (null == user) {
			System.out.println("================> User non-existent");
			return ;
		}
		Address address = new Address();
		address.setId(Long.valueOf(userMongoHelper.getNextId(Address.class.getName()))); 
		address.setProvince("湖南省");
		address.setCity("长沙");
		updateMap.put("id", user.getId());
		updateMap.put("address", address);
		userMongoHelper.update(updateMap);
	}
    
	@Test
	public void updateUserAbility() {
		System.out.println("================> updateUserAbility");
		Map<String, Object> updateMap = new HashMap<>();
		User user = (User) userMongoHelper.findOne(Criteria.where("name").is("Sunlight-4"));
		if (null == user) {
			System.out.println("================> User non-existent");;
			return ;
		}
		ArrayList<String> abilitys = user.getAbility();
		abilitys.add("APP");
		updateMap.put("id", user.getId());
		updateMap.put("ability", abilitys);
		userMongoHelper.update(updateMap);
	}
    
	@Test
	public void findUserPage() {
		System.out.println("================> findUserPage");
		userMongoHelper.setOrderAscField("age"); // 排序
		Integer pageSize = 5; // 每页页数
		Integer pageNumber = 1; // 当前页
		List<User> users = userMongoHelper.find(Criteria.where("age").gt(25), pageSize, pageNumber); // 查询age大于25的数据
		for (User user : users) {
			System.out.println("user : " + user.toString());
		}
	}
}

1 MongoDB是最接近关系型数据的非关系型数据库中的文档型数据库。

2 MongoDB支持非常丰富的查询语句,功能强大,但容易犯错。

3 MongoDB表结构的设计需谨慎,尽量减少嵌套层数,各嵌套的文档属性名尽量避免相同。

参考文档

MongoDB官方文档: https://docs.mongodb.com

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值