一、MongoDB 简介
1、NoSQL
NoSQL(NoSQL = Not Only SQL),意即反SQL运动,指的是非关系型的数据库。
为什么使用NoSQL :
- 对数据库高并发读写。
- 对海量数据的高效率存储和访问。
- 对数据库的高可扩展性和高可用性。
2、什么是 MongoDB
MongoDB 是由 C++ 语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。
MongoDB 旨在为 web 应用提供可扩展的高性能数据存储解决方案。将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象,字段值可以包含其他文档,数组及文档数组。
{
"_id" : ObjectId("61370c478841227bb484fa7b"),
"name" : "zhangsan",
"age" : 21,
"sex" : true
}
适用场景
1、网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
2、缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由M ongo搭建的持久化缓存层可以避免下层的数据源过载。
3、大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵, 在此之前,很多时候程序员往往会选择传统的文件进行存储。
4、高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对Map Reduce弓摩的内置支持。
5、用于对象及 JSON 数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储及查询。
不适用场景
1、高度事务性的系统:例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。
2、传统的商业智能应用:针对特定问题的BI数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。
3、安装 MongoDB
我们在 Linux 系统下的 docker 容器中安装,所以需要提前装上 docker。
#拉取镜像
docker pull mongo:latest
#创建和启动容器,默认端口27017
docker run -d --restart=always -p 27017:27017 --name mymongo -v /data/db:/data/db -d mongo
#进入容器
docker exec -it mymongo /bin/bash
#使用MongoDB客户端进行操作
mongo
> show dbs #查询所有的数据库
admin 0.000GB
config 0.000GB
local 0.000GB
4、MongoDB 相关概念
mongodb 中基本的概念是文档、集合、数据库
4.1、数据库
常用命令:
# 切换/创建数据库,如果数据库不存在,则创建数据库,否则切换到指定数据库
use test
# 查询所有数据库
show dbs;
# 删除当前使用数据库
db.dropDatabase();
# 查看当前使用的数据库
db.getName();
# 显示当前db状态
db.stats();
#当前db版本
db.version();
#查看当前db的链接机器地址
db.getMongo();
4.2、文档
文档是一组键值(key-value)对(BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
文档对应 SQL 中的行。
4.3、集合
集合就是 MongoDB 文档组,类似于 SQL 中的表。
集合存在于数据库中,集合没有固定的结构,这意味着在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
常用命令:
# 创建一个集合(table)
db.createCollection( "collName");
# 得到指定名称的集合(table )
db.getCollection("user");
4.4、MongoDB 数据类型
二、MongoDB 增删改查
1、insert
> db.User.save({name:'zhangsan',age:21,sex:true})
> db.User.find()
{"_id": Objectld("4f69e680c9106ee2ec95da66"), "name":"zhangsan","age": 21,"sex": true}
上面我们并没有添加 id,MongoDB 可以自己生成,Objectld是 id 的默认类型。Objectld使用12字节的存储空间,每个字节二位十六进制数字, 是一个24位的字符串。
1、时间戳:时间不断变化的
2、机器:主机的唯_标识码。通常是机器主机名的散列值,这样可以确保不同主机
生成不同的Objectld ,不产生冲突。
3、PID:为了确保在同一台机器上并发的多个进程产生的Objectld是唯一的,所以加上进程标识符(PID).
4、计数器:前9个字节保证了同一秒钟不同机器不同进程产生的Objectld是唯一的。 后3个字节就是一个自动增加的计数器,确保相同进程同一秒产生的 Objectld 也是不一样。
2、Query
2.1、WHERE
# select * from User where name = 'zhangsan'
> db.User.find({name:"zhangsan"})
2.2、FIELDS
# select name, age from User where age = 21
> db.User.find({age:21}, {'name':1, 'age':1})
2.3、SORT
在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
# select * from User order by age
> db.User.find().sort({age:1})
2.4、LIMIT,SKIP
在 MongoDB 中使用 limit() 方法来读取指定数量的数据,skip() 方法来跳过指定数量的数据
# select * from User skip 2 limit 3
> db.User.find().skip(0).limit(3)
2.5、IN
# select * from User where age in (21, 26, 32)
> db.User.find({age:{$in:[21,26,32]}})
2.6、COUNT
# select count(*) from User where age >20
> db.User.find({age:{$gt:20}}).count()
2.7、0R
# select * from User where age = 21 or age = 28
> db.User.find({$or:[{age:21}, {age:28}]})
3、Update
可直接用类似 T-SQL 条件表达式更新,或用 save() 更新从数据库返回到文档对象。
# update Userset age = 100, sex = 0 where name = 'zhangsan'
> db.User.update({name:"zhangsan"}, {$set:{age:100, sex:0}})
db.collection.update(criteria, objNew, upsert, mult)
criteria:需要更新的条件表达式
objNew:更新表达式
upsert:如FI标记录不存在,是否插入新文档。
multi:是否更新多个文档。
4、Remove
remove() 用于删除单个或全部文档,删除后的文档无法恢复。
> db.User.remove(id)
//移除对应id的行
> db.User.remove({})
//移除所有
5、aggregate
MongoDB 中聚合 (aggregate) 主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果,类似sql语句中的 count(*)
先添加几条数据:
>db.article.insert({
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
>db.article.insert({
title: 'NoSQL Overview',
description: 'No sql database is very fast',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 10
})
>db.article.insert({
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
url: 'http://www.neo4j.com',
tags: ['neo4j', 'database', 'NoSQL'],
likes: 750
})
5.1、统计 sum
统计每个作者所写的文章数
# select by_user, count(*) from article group by by_user
> db.article.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
5.2、常见的聚合表达式
6、索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
>db.User.createIndex({"name":1})
上命令中 name 值为要创建的索引字段,1 为指定按升序创建索引,如果想按降序来创建索引指定为 -1 即可
三、springboot 整合 MongoDB
spring-data-mongodb 提供了 MongoTemplate 与 MongoRepository 两种方式访问 mongodb,MongoRepository 操作简单,MongoTemplate 操作灵活,我们在项目中可以灵活适用这两种方式操作 mongodb,MongoRepository 的缺点是不够灵活,MongoTemplate 正好可以弥补不足。
1、准备工作
1.1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
1.2、添加配置
spring.data.mongodb.uri=mongodb://192.168.220.131:27017/test
1.3、添加启动类
@SpringBootApplication
public class MongoApplication {
public static void main(String[] args) {
SpringApplication.run(MongoApplication.class);
}
}
2、使用 MongoTemplate 实现增删改查
2.1、创建实体类 User
@Data
@Document("User")
public class User {
@Id
private String id;
private String name;
private Integer age;
private String email;
private String createDate;
}
2.2、实现
常用方法:
mongoTemplate.findAll(User.class) // 查询User文档的全部数据
mongoTemplate.findById(<id>, User.class) // 查询User文档id为id的数据
mongoTemplate.find(query, User.class) // 根据query内的查询条件查询
mongoTemplate.upsert(query, update, User.class) // 修改
mongoTemplate.remove(query, User.class) // 删除
mongoTemplate.insert(User) // 新增
Query对象:
1、创建一个query对象(用来封装所有条件对象),再创建一个criteria对象(用来构建条件)
2、 精准条件:criteria.and(“key”).is(“条件”)
模糊条件:criteria.and(“key”).regex(“条件”)
3、封装条件:query.addCriteria(criteria)
4、大于(创建新的criteria):Criteria gt = Criteria.where(“key”).gt(“条件”)
小于(创建新的criteria):Criteria lt = Criteria.where(“key”).lt(“条件”)
5、Query.addCriteria(new Criteria().andOperator(gt,lt));
6、一个query中只能有一个andOperator()。其参数也可以是Criteria数组。
7、排序 :query.with(new Sort(Sort.Direction.ASC, “age”). and(new Sort(Sort.Direction.DESC, “date”)))
2.3、测试增删改查
@SpringBootTest
public class MongoTest {
@Autowired
MongoTemplate mongoTemplate;
@Test
public void create() {
User user = new User();
user.setAge(12);
user.setName("Lay");
user.setEmail("122135@qq.com");
User insertUser = mongoTemplate.insert(user);
System.out.println(insertUser);
}
@Test
public void findAll() {
List<User> users = mongoTemplate.findAll(User.class);
for (User user : users) {
System.out.println(user);
}
}
@Test
public void findById() {
User user = mongoTemplate.findById("613711cf0c745d3670ef5cc8", User.class);
System.out.println(user);
}
// 条件查询
@Test
public void findUserList() {
Query query = new Query(Criteria.where("name").is("Jay").and("age").is(12));
List<User> users = mongoTemplate.find(query, User.class);
for (User user : users) {
System.out.println(user);
}
}
// 模糊查询
@Test
public void findLikeUserList() {
String name = "ay";
String regex = String.format("%s%s%s", "^.*", name, ".*$");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Query query = new Query(Criteria.where("name").regex(pattern));
List<User> users = mongoTemplate.find(query, User.class);
for (User user : users) {
System.out.println(user);
}
}
// 分页查询
@Test
public void findPageUserList() {
Integer current = 1;
Integer size = 2;
String name = "ay";
String regex = String.format("%s%s%s", "^.*", name, ".*$");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Query query = new Query(Criteria.where("name").regex(pattern));
long count = mongoTemplate.count(query, User.class);
List<User> users = mongoTemplate.find(query.skip((current - 1) * size).limit(size), User.class);
System.out.println(count + "---" + users);
}
// 改
@Test
public void update() {
// User user = mongoTemplate.findById("613711cf0c745d3670ef5cc8", User.class);
Query query = new Query(Criteria.where("id").is("613711cf0c745d3670ef5cc8"));
Update update = new Update();
update.set("name", "JJay");
UpdateResult upsert = mongoTemplate.upsert(query, update, User.class);
long modifiedCount = upsert.getModifiedCount();
System.out.println(modifiedCount);
}
// 删
@Test
public void delete() {
Query query = new Query(Criteria.where("id").is("613711cf0c745d3670ef5cc8"));
DeleteResult deleteResult = mongoTemplate.remove(query, User.class);
long count = deleteResult.getDeletedCount();
System.out.println(count);
}
}
3、使用 MongoRepository 实现增删改查
Spring Data 提供了对 mongodb 数据访问的支持,我们只需要继承 MongoRepository 类,按照 Spring Data 规范,一些方法就可以自动生成。
SpringData 方法定义规范:
注:
(1)查询方法以 find | read | get 开头
(2)涉及条件查询时,条件的属性用条件关键字连接
(3)条件属性首字母需要大写
(4)支持属性的级联查询,但若当前类有符合条件的属性则优先使用,而不使用级联属性,若需要使用级联属性,则属性之间使用_强制进行连接
3.1、UserRepository 接口继承 MongoRepository
@Repository
public interface UserRepository extends MongoRepository<User, String> {
}
可以看到 MongoRepository 类中已经有一些增删改查的方法了。
如果需要其他方法,则根据上述命名规则,MongoRepository 可以自动生成。
@Repository
public interface UserRepository extends MongoRepository<HUser, String> {
// 根据名字name查询
Hospital getUserByName(String name);
}
3.2、测试增删改查
@SpringBootTest
public class MongoRRepositoryTest {
@Autowired
UserRepository userRepository;
//添加
@Test
public void createUser() {
User user = new User();
user.setAge(20);
user.setName("张三");
user.setEmail("333@qq.com");
User user1 = userRepository.save(user);
}
//查询所有
@Test
public void findUser() {
List<User> userList = userRepository.findAll();
System.out.println(userList);
}
//id查询
@Test
public void getById() {
User user = userRepository.findById("613711cf0c745d3670ef5cc8").get();
System.out.println(user);
}
//条件查询
@Test
public void findUserList() {
User user = new User();
user.setName("张三");
user.setAge(20);
Example<User> userExample = Example.of(user);
List<User> userList = userRepository.findAll(userExample);
System.out.println(userList);
}
//模糊查询
@Test
public void findUsersLikeName() {
//创建匹配器,即如何使用查询条件
ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
.withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
User user = new User();
user.setName("三");
Example<User> userExample = Example.of(user, matcher);
List<User> userList = userRepository.findAll(userExample);
System.out.println(userList);
}
//分页查询
@Test
public void findUsersPage() {
Sort sort = Sort.by(Sort.Direction.DESC, "age");
//0为第一页
Pageable pageable = PageRequest.of(0, 10, sort);
//创建匹配器,即如何使用查询条件
ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
.withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
User user = new User();
user.setName("三");
Example<User> userExample = Example.of(user, matcher);
//创建实例
Example<User> example = Example.of(user, matcher);
Page<User> pages = userRepository.findAll(example, pageable);
System.out.println(pages);
}
//修改
@Test
public void updateUser() {
User user = userRepository.findById("613711cf0c745d3670ef5cc8").get();
user.setName("张三_1");
user.setAge(25);
user.setEmail("883220990@qq.com");
User save = userRepository.save(user);
System.out.println(save);
}
//删除
@Test
public void delete() {
userRepository.deleteById("613711cf0c745d3670ef5cc8");
}
}