MongoDB
MongoDB 是非关系数据库当中功能最丰富,最像关系数据库的
1、介绍
MongoDB优势:
1:JSON结构和对象模型接近,开发代码量少
2:JSON动态模型意味着更容易响应新的业务需求
3:复制集提供了 99.999%高可用
4:分片架构支持海量数据无缝扩容
数据库结构
MongoDB属于NoSQL数据库,自然也是没有表相关概念的,该数据库存储使用的是集合,集合中存储的是文档(树状结构数据)
核心概念
数据库
MongoDB 的一个实例可以拥有一个或者多个相互独立的数据库, 每个数据库都有自己的集合
命令: show databases;
“show dbs” 命令可以显示所有有数据的列表。
运行"use"命令,可以连接到一个指定的数据库
有三个保留数据库:
-
admin
从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器
-
local
这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
- config
当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息
集合
类似mysql中的表
集合可以看做是拥有动态模式的表,json格式,可以更加业务需求自动拓展,一个集合可以用于n个文档
合法的集合名
- 集合名不能是空字符串""
- 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾
- 集合名不能以"system."开头,这是为系统集合保留的前缀
文档
文档是 MongoDB 中基本的数据单元,类似于 mysql 的行
文档是键值对的一个有序集合,语法格式就是 JSON 格式
MongoDB区分类型和大小写
MongoDB的文档不能有重复的键
文档键命名规范:
- 键不能含有\0 (空字符),这个字符用来表示键的结尾
- .和$有特别的意义,只有在特定环境下才能使用
- 以下划线"_"开头的键是保留的(不是严格要求的)
字段
域:field
数据类型
String(字符串): mongodb中的字符串是UTF-8有效的
Integer(整数): 存储数值。整数可以是32位或64位,具体取决于您的服务器
Boolean(布尔): 存储布尔(true/false)值
Double(双精度): 存储浮点值
Arrays(数组): 将数组或列表或多个值存储到⼀个键中
Timestamp(时间戳): 存储时间戳
Object(对象): 嵌⼊式⽂档
Date(⽇期): 以UNIX时间格式存储当前⽇期或时间
Null (空值): 存储Null值
Symbol(符号): 与字符串相同,⽤于具有特定符号类型的语⾔
Object ID(对象ID) : 存储⽂档ID
Binary data(⼆进制数据): 存储⼆进制数据
Code(代码): 将JavaScript代码存储到⽂档中
Regular expression(正则表达式): 存储正则表达式
注意
1:mongodb 自带javascript shell语法,即可以使用js方式操作 mongodb
2:mongodb 没有主外键的概念
3:mongodb 的没有id自增的概念, 但是提供了Object Id
2、安装和连接
https://www.mongodb.com/download-center/community
安装客户端
Linux
1:将mongodb安装包拉入linux路径: /usr/local/software
2:安装mongodb
cd /usr/local/software
rpm -ivh mongodb-org-server-4.0.10-1.el7.x86_64.rpm
3:修改mongodb配置文件满足远程连接
vi /etc/mongod.conf
bindIp: 0.0.0.0 # 0.0.0.0 绑定所有的网卡
4:启动mongodb
systemctl start mongod
5:开放防火墙端口
firewall-cmd --zone=public --add-port=27017/tcp --permanent
firewall-cmd --reload
6:客户端连接
window
双击安装, 一路next下去
安装好后,默认会启动MongoDB服务器,可以在服务列表中找到MongoDB Server查看是否处于运行状态,已经处于运行状态则直接进入安装目录/Server/bin,运行mongo.exe,出现以下画面表示安装成功了
MongoDB默认端口27017
可以在 bin 目录下创建txt文件,改名为 startup.bat ,在文件内写入
mongod -config "F:\Program Files\MongoDB\Server\4.2\bin\mongod.cfg"
客户端
3、增删改操作
在线文档教程: https://www.runoob.com/mongodb/mongodb-tutorial.html
数据库与集合
当双击打开 mongo.exe 的时候,默认就是使用 localhost:27017 连接服务器
mongo localhost:27017
show dbs 查询所有数据库
use 数据库名 创建并且选中数据库。数据库已存在则直接选中,不存在则会创建
db 查询当前选择的数据库
db.dropDatabase() 删除当前选中的数据库
show collections或者show tables 查询当前库中的集合
db.createCollection("集合名") 创建集合
db.集合名.stats(); 查看集合信息
db.集合名.drop() 删除集合
db.集合名.remove(条件); 仅仅删除文档,集合不删。如果不加条件, 表示删除所有文档
注意: db.集合名 == db.getCollection("集合名")
MongoDB没有专门的创建数据库的命令, 可以使用use 来选择某个数据库, 如果库不存在, 将会创建,但是只有往该库加入文档后才保存成文件
MongoDB中,不用创建集合, 因为没有固定的结构, 直接使用db.集合名称.命令 来操作就可以了, 如果非要显示创建的话
使用: db.createCollection(“集合名称”)
注意:
1:没有显示指定 id 时, 会自动添加 id
2:MongoDB 每个文档大小不能超过16MB
3:可以使用 Object.bsonsize(“文档名”) 来看大小
文档
1、文档添加
语法:
db.集合名.insertOne( 文档 ) : 往集合中插入一个文档
db.集合名.insertMany(文档):往集合中插入多个文档
db.集合名.insert( 文档 ) : 往集合中插入一个文档或者多个
注意:
- 往集合中新增文档,当集合不存在时会自动先创建集合,再往集合中添加文档,但是不要依赖自动创建集合的特性
- 当操作成功时,集合会给文档生成一个**_id**字段,该字段就是文档的主键,也能在插入数据时自己指定该字段的值,但是不建议这样做
示例:
# 给users集合添加一个文档:name:"dafei" age:18
db.users.insert({id: 1, name: "dafei", age: 18})
db.users.insert({id: 2, name: "xiaofei", age: 17})
2、文档更新
语法:
在 MongoDB 中,所有条件都是使用 花括号
# update users set xx=1 where xx = 1
db.集合名.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
简化方法:
db.集合名.updateOne # 等价于 multi:false
db.集合名.updateMany # 等价于 multi:true
参数说明:
# query——update的查询条件,类似sql update查询内where后面的
# update——update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql查询内set后面的
# upsert——可选,如果query查询不出满足条件的记录,是否插入,true为插入;默认是false,不插入
# multi——可选,默认是false,只更新找到的第一条记录;如果为true,就把按条件查出来多条记录全部更新
# writeConcern——可选,抛出异常的级别
示例:
# 把一个带有name=dafei的文档,修改其age值为30
db.users.updateOne(
{name: "dafei"},
{$set: {age: 30}},
{
upsert:false # 如果满足条件数据则修改,如果没有则不添加
#multi:false true表示所有满足条件都改,false表示只改第一条
}
)
# 修改所有name=dafei的文档,修改其name=大飞,age=20
db.users.updateMany(
{name: "dafei"},
{$set: {name: "大飞", age: 20}}
)
# 修改所有的文档,修改其name=xxx,age=10
db.users.updateMany(
{},
{$set: {name: "xxx", age: 10}}
)
添加列
直接set一个不存在的列即可
# 给 age=18 文档添加 sex=男 属性
db.users.update(
{age:18},
{$set:{sex:'男'}},
{
multi:true
}
)
删除列
使用$unset
# 将 name=xiaofei 的文档中 sex 列删除
db.users.update(
{name:'xiaofei'},
{$unset:{sex:''}},
{
multi:true
}
)
3、文档删除
语法:
db.集合名.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
简化方法:
删除1个:db.集合名.deleteOne( ... )
删除所有:db.集合名.deleteMany( ... )
参数说明:
# query :(可选)删除的文档的条件
# justOne : (可选)如果设为 true 或 1,则只删除一个文档;默认 false,表示删除所有匹配条件的文档
# writeConcern :(可选)抛出异常的级别
示例:
# 删除_id=xxx的文档
db.users.deleteOne({_id: ObjectId("621c8e73fb279b933fd47623")})
# 删除所有带有name=dafei的文档
db.users.deleteMany({name: "dafei"})
# 删除当前数据库中所有文档
db.users.deleteMany({})
4、查询操作
数据准备
db.users.insert({"id":NumberLong(1),"name":"dafei","age":NumberInt(18)})
db.users.insert({"id":NumberLong(2),"name":"xiaofei","age":NumberInt(20)})
db.users.insert({"id":NumberLong(3),"name":"zhang quan dan","age":NumberInt(33)})
db.users.insert({"id":NumberLong(4),"name":"zhang kun","age":NumberInt(26)})
db.users.insert({"id":NumberLong(5),"name":"afei","age":NumberInt(18)})
db.users.insert({"id":NumberLong(6),"name":"cai xv kun","age":NumberInt(29)})
db.users.insert({"id":NumberLong(7),"name":"jia nai liang","age":NumberInt(25)})
db.users.insert({"id":NumberLong(8),"name":"fu rong wang","age":NumberInt(28)})
db.users.insert({"id":NumberLong(9),"name":"wang da","age":NumberInt(31)})
db.users.insert({"id":NumberLong(10),"name":"da wang","age":NumberInt(32)})
db.users.insert({"id":NumberLong(11),"name":"will","age":NumberInt(26)})
查询全部
语法:
db.集合名.find(query, projection)
# query: 是条件
# projection: 指定返回哪些列,里面是列名和boolean值 :{name:true}
注意:
_id是默认返回,设置 {"_id": 0}就表示不显示
0、null、undefined 都表示是false
除了"_id"之外,所有属性要么都是0、要么都是1 。错误写法:{name:1,age:0}
示例:
# 查所有用户
db.users.find()
db.users.find({}) # {} 是条件,表示查询集合中所有文档
db.users.find({}, {name:1}) # 第二个 {} 表示查询哪些列
1、排序
语法:
db.users.find({}).sort({字段: 1}) #升序
db.users.find({}).sort({字段: -1}) #降序
db.users.find({}).sort({列1:1 , 列2:1}) #多列排序
示例:
# 查询所有用户按年龄排序
db.users.find({}).sort({age:1}) #正序、升序
db.users.find({}).sort({age:-1}) #倒序、降序
# 表示按年龄降序,如果年龄一致,按 id 正序排序
db.users.find({}).sort({age:-1, id:1}) #多列排序
2、分页
语法:
db.集合.find({}).skip(n).limit(每页显示条数)
# sikp(num): 跳过num个文档,相当于start
# limit(num): 限制显示num个文档,相当于pageSize
db.users.find().skip((currentPage-1) * pageSize).limit(pageSize)
示例:
# 显示第一页,每页三条
db.users.find({}).skip(0).limit(3)
# 显示第二页,每页三条
db.users.find({}).skip(3).limit(3)
# 显示第n页,每页三条
db.users.find({}).skip(3*(n-1)).limit(3)
带条件
1、比较运算符
语法:
find( {字段: {比较操作符1: 值1, 比较操作符2: 值2, ...} } ) #若有多个操作符,则其之间的关系是and
比较操作符:
- (>) 大于 - $gt
- (<) 小于 - $lt
- (>=) 大于等于 - $gte
- (<= ) 小于等于 - $lte
- (!=) 不等 - $ne
- in 运算 - KaTeX parse error: Expected '}', got 'EOF' at end of input: … 如:**{name: {in: [“xiaoyao”,“bunny”]}}
- 判断存在 - KaTeX parse error: Expected '}', got 'EOF' at end of input: … 如:**{name: {exists:true}}
示例:
# 查询age > 30的用户
db.users.find(
{
age: {$gt:30}
}
)
# 查询名字为 dafei 或 xiaofei的用户
db.users.find(
{ name: {$in: ["dafei","xiaofei"]} }
)
# 判断判断指定列是否存在,查询有 name 列的所有文档
db.users.find(
{ name: {$exists:true} }
)
可以和前面结合使用
db.users.find({name: {$exists:true}},{name:1})
2、逻辑运算符
语法:
find( {逻辑操作符: [条件1, 条件2, ...]} )
逻辑操作符
- (&&) 与 - $and
- (||) 或 - $or
- (!) 非 - $not
示例:
# 查年龄在28 到 30间的用户信息[28,30]
db.users.find(
{
age:{ $gte:28, $lte:30 } #表示 and 关系
}
)
db.users.find(
{
$and:[
{age:{$gte:28}},
{age:{$lte:30}}
]
}
)
# 查看年龄小于28或者年龄大于30用户信息
db.users.find(
{$or: [{age: {$lt: 28}}, {age: {$gt:30}}]}
)
# 查看年龄等于28或者等于30用户信息
db.users.find(
{$or: [{age:28}, {age: 30}]}
#或者是 { age:{$in:[28, 30]}}
)
3、模糊查询
MongoDB的模糊查询使用的是正则表达式的语法 如:{name: {KaTeX parse error: Undefined control sequence: \* at position 11: regex: /^.\̲*̲keyword\.*/}}
实际上MongoDB也是不擅长执行模糊查询的,在实际开发中也是不使用的,该功能了解即可
语法:
db.集合.find({列: {$regex: /关键字/}})
db.集合.find({列: {$regex: "关键字"}})
{name:/xxx/} ---> %xxx%
{name:/^xxx/} ---> xxx%
{name:/xxx$/} ---> %xxx
{name:/xxx/i} 忽略大小写
示例:
# 查询name带有fei的用户信息
# sql: select * from users where name like '%fei%'
db.users.find(
{name: {$regex:/fei/}} #正则对象
#{name: {$regex:"fei"}} #正则表达式
)
# 查name中包含fei字样,并且年龄在28 到 30间的用户信息,
# sql: select * from users where name like '%fei%' and age >= 28 and age <=30
db.users.find(
{
$and: [
{name: {$regex:/fei/}},
{age: {$gte:28, $lte:30}}
]
}
)
# 查询所有name=逍遥的文档
db.users.find({name:"逍遥"})
# 查询所有name=bunny 或者 age<30的文档
db.users.find({$or:[{name:"bunny"},{age:{$lt:30}}]})
# 查询所有 name含有wang 并且 30<=age<=32 的文档
db.users.find({$and:[{name:{$regex:/^.*wang.*$/}},{age:{$gte:30,$lte:32}
}]})
5、数组操作
给所有数据加数组字段
db.users.updateMany({}, { $set:{ hobby:[] } })
添加
1、$push
$push : 给数组字段添加数据, 允许数据元素重复
# 给name等于dafei的用户添加java 爱好
db.users.update(
{name:'dafei'},
{ $push:{ hobby:'java' } }
)
# 给name等于dafei的用户添加'java', 'c', 'vue' 爱好
db.users.update({name:'dafei'}, {
$push:{hobby:{$each:['java', 'c', 'vue']}}
})
如果是 {hobby:['java','c']} 这样是将后面作为一整个元素加进文档
2、$addToSet
$addToSet: 给数组字段添加数据, 不允许数据元素重复
db.users.update({name:'dafei'}, {$addToSet:{hobby:'java'}})
删除
1、$pop
$pop 删除数组中数据, 1:删除最后一个, -1: 删除第一个
局限:只能从头或者从尾删除,不能通过指定元素删除
# 需求删除name=dafei用户最后一个兴趣
db.users.update(
{name:'dafei'},
{ $pop:{ hobby:1 } }
)
2、$pull
$pull 删除数组中数据, 通过指定内容删除
# 删除name=dafei用户java兴趣选项
db.users.update(
{name:'dafei'},
{ $pull:{hobby:'java'} }
)
修改
1、通过索引修改
索引是从 0 开始的
如果索引位置没有数据,那么此语句就直接添加值
# 将name=dafei的用户索引为 1 的兴趣改为 go
db.users.update({name:'dafei'}, {$set:{'hobby.1':'go'}})
2、通过数组元素修改
# 将name=dafei的用户 c 兴趣 改为 c#
db.users.update({name:'dafei', hobby:'c'}, {$set:{'hobby.$':'c#'}})
查询
db.users.update({name:'dafei', hobby:'c'}
6、聚合操作[拓展]
聚合查询、统计、分组、平均值、最大值、最小值、总数
比如:
计算一段时间内的销售总额, 均值
计算一段时间的净利润
分析购买人的年龄分布
分析学生成绩
统计员工的绩效
聚合框架
什么是Mongodb聚合框架
MongoDB聚合框架(Aggregation Framewordk) 是一个计算框架,他可以作用在一个或集合集合中,对集合中的数据进行一系列运算,将这些数据转化为期望的形式
从效果而言, 聚合框架相当于SQL查询中 Group by 、left join
原理理解
管道(Pipeline) 与 步骤(Stage)
整个聚合运算工程称之为管道, 它是由多个步骤组成
每个管道:
1:接收一系列文档(原始数据)
2:每一个步骤对这些文档进行一系列运算
3:结果文档输出给下一个步骤
pipeline = [$stage1, $stage2, .... $stageN]
db.集合.aggregate(
pipeline,
{
options
}
)
eg:
db.user.aggregate(
[
{
$group:{
_id:"$name",
namecount:{$sum:1}
}
}
]
);
常见步骤
- $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档
- ** m a t c h ∗ ∗ : 用 于 过 滤 数 据 , 只 输 出 符 合 条 件 的 文 档 。 match**:用于过滤数据,只输出符合条件的文档。 match∗∗:用于过滤数据,只输出符合条件的文档。match使用MongoDB的标准查询操作
- $limit:用来限制MongoDB聚合管道返回的文档数
- $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档
- $group:将集合中的文档分组,可用于统计结果
- $sort:将输入文档排序后输出
- $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
- $geoNear:输出接近某一地理位置的有序文档。
练习
准备数据
var book1={
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
}
var book2={
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
}
var book3={
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
url: 'http://www.neo4j.com',
tags: ['neo4j', 'database', 'NoSQL'],
likes: 750
}
db.books.insert(book1);
db.books.insert(book2);
db.books.insert(book3);
聚合操作
db.books.aggregate([
{
$match:{ likes:{$gte:20} } # 过滤likes≥20
},
{
$group:{
_id:"$by_user", # 根据by_user列进行分组
sum:{$sum:"$likes"}, #分组后对数据进行求和
avg:{$avg:"$likes"},
min:{$min:"$likes"},
max:{$max:"$likes"},
push:{$push:"$likes"}, #分组分类,将分组结果放在一个集合内,集合列名push
addToSet:{$addToSet:"$likes"},
first:{$first:"$likes"},
last:{$last:"$likes"}
}
},
{
$project:{
_id:1,
sum:1,
avg:1
}
}
])
执行前两个 m a t c h 、 match 、 match、group 操作,结果如下:
执行整个语句的结果:
7、文档设计
范式
反范式
使用冗余字段方式替换关联映射
使用数组方式替换一对多
多对多关系设计:
8、设置用户
以下操作必须在cmd命令行中操作,执行以下命令
//1.选中admin数据库
use admin
//2.往里面添加一个超级管理员账号
db.createUser({user:"root", pwd: "admin", roles:["root"]})
//user:账号 pwd:密码 roles:角色->root超级管理员
修改MongoDB的配置文件:安装目录/Server/bin/mongod.cfg
#约在29行位置,配置开启权限认证
security:
authorization: enabled
完成上面配置后重启服务器
在登录时就必须要输入密码,否则不能执行任何的命令
9、集成springboot
完成数据库的 crud 功能
启动服务
依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--spring boot data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
配置
# application.properties
# 配置数据库连接
#格式: mongodb://账号:密码@ip:端口/数据库?认证数据库
#spring.data.mongodb.uri=mongodb://root:admin@localhost/mongodemo?authSource=admin
spring.data.mongodb.uri=mongodb://localhost:27017/mongodemo
# 配置MongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=debug
domain
@AllArgsConstructor
@NoArgsConstructor
@Data
@Document("users") //设置文档所在的集合
public class User {
//文档的id建议使用 String 类型,一般使用ObjectId类型来封装,并且贴上@Id注解
// 自动映射为 _id 自动封装ObjectId
@Id
private String id;
private String name;
private Integer age;
//数组推荐使用 ArrayList
private List<String> hobby = new ArrayList<>();
}
实现
1、方式一 MongoRepository
里面的 save、update 操作是全栈更新
加入对于 id1 插入了一条数据,然后在对 id1 更新 空数据 new User() ,则数据就是空的
新数据覆盖旧的
-
为什么 UserRepository 接口可以直接调用 CRUD 方法?
UserRepository 继承了 MongoRepository接口,它又继承了一系列接口,接口中定义类 crud 方法
MongoRepository—PagingAndSortRepository—CrudRepository—Repository
-
为什么不需要实现类?
spring容器中创建了代理实现类
spring容器启动时,根据 data-mongodb依赖,将 Repository 导入,springboot通过自动配置原理,加载 Repository 接口整个继承体系,其中 UserRepository 继承了 Repository,所以框架会通过动态代理方式,创建其实现类,并交给 spring容器管理
//用户持久化接口,类似 Mapper 接口
/**
* 继承接口:MongoRepository
* 1、接口泛型1:操作对象 User
* 2、接口泛型2:操作对象主键类型 string
*/
@Repository
public interface UserRepository extends MongoRepository<User, String> {}
//数据库 CRUD 操作
public interface IUserService {
void save(User user);
void delete(String id);
void update(User user);
User get(String id);
List<User> list();
}
@Service
public class UserServiceImpl implements IUserService{
//使用容器注入代理实现类,直接调用里面的方法
@Autowired
private UserRepository userRepository;
@Override
public void save(User user) {
userRepository.save(user);
}
@Override
public void delete(String id) {
userRepository.deleteById(id);
}
@Override
public void update(User user) {
userRepository.save(user);
}
@Override
public User get(String id) {
//userRepository.findById(id) 返回值是 Optional
return userRepository.findById(id).get();
}
@Override
public List<User> list() {
return userRepository.findAll();
}
}
--------------------------------------------------------------
@SpringBootTest
public class UserTest {
@Autowired
private IUserService userService;
@Test
public void testSave(){
User user = new User();
user.setId(21L);
user.setName("dafei");
user.setAge(18);
userService.save(user);
}
@Test
public void testUpdate(){
//由于这里的更新是全栈更新,数据容易丢失,所以需要先查询、再替换、再更新
//User user = new User();
//user.setId("5de507fca0852c6c7ebc1eac");
//user.setId(21L);
//user.setName("dafei2222");
//user.setAge(18);
//1、查询
User user = userService.get("5de507fca0852c6c7ebc1eac")
//2、替换
user.setName("dafei2222");
user.setAge(18);
//3、更新
userService.update(user);
}
@Test
public void testDelete(){
userService.delete("5de507fca0852c6c7ebc1eac");
}
@Test
public void testGet(){
System.out.println(userService.get("5de507fca0852c6c7ebc1eac"));
}
@Test
public void testList(){
System.out.println(userService.list());
}
}
现在想通过 name 字段来查询,但是 UserRepository 接口实现类并没有此种方法, 只有 findById()
此时仅仅需要在接口内定义一个抽象方法,作为通过 name 字段查询的方法,此处是因为 JPA 的查询规范
操作原理:在spring动态代理时,会根据JPA查询规范解析自定义方法,拼接出MQL查询语句,实现查询操作
JPA 查询规范:
定义 JPA 约束的查询方法,方法格式要求:
前缀 + 属性名 + 操作符
前缀:findBy 、queryBy 开头
属性名: 表的某个列(实体对象属性),首字母大写
操作符:And、LessThan、After、ISNull
@Repository
public interface UserRepository extends MongoRepository<User, String> {
// 使用Spring Data命名规范做高级查询
List<User> findByName(String name);
}
参考下图:
2、方式二 MongodbTemplate
JPA查询方法简单,但对于多条件查询(高级查询),JPA 无法拼接,需要使用 MongodbTemplate
springdata 是spring团队为了同一数据库(关系型与非关系型)操作而提供一套访问数据库的API规范
data-mongodb 是这个规范下创建子项目
data-mongodb 访问数据库的api核心类 :MongodbTemplate 类
// spring封装操作模板,MongodbTemplate 类,直接使用即可
@Service
public class UserServiceImpl implements IUserService{
//直接使用,容器已经创建好了
@Autowired
private MongoTemplate template;
@Override
public void save(User user) {
template.save(user);
}
@Override
public void delete(String id) {
template.remove(id);
}
@Override
public void update(User user) {
template.save(user);
}
@Override
public User get(String id) {
return template.findById(id, User.class);
}
@Override
public List<User> list() {
return template.findAll(User.class);
}
}
条件查询
注意包名
import org.springframework.data.mongodb.core.query.Query;
@SpringBootTest
public class QueryTest {
//直接使用
@Autowired
private MongoTemplate mongoTemplate;
// 查询所有name=dafei的文档
// mql:db.users.find({name:"dafei"})
@Test
public void testQuery1() throws Exception {
// Query 查询对象:理解为MQL语句拼接对象
Query query = new Query();
// Criteria 条件封装对象
// 相当于 {"name": "dafei"},是限制条件
Criteria criteria = Criteria.where("name").is("dafei");
// 添加限制条件
query.addCriteria(criteria);
//执行查询语句:指定集合名与返回值类型
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);//在可能会出错和调试时使用System.err
}
}
逻辑运算查询
@SpringBootTest
public class QueryTest {
//直接使用
@Autowired
private MongoTemplate mongoTemplate;
// 查询所有name=dafei 或者 age<30的文档
// db.users.find({ $or:[ { name:"dafei" }, { age:{$lt:30} } ] })
@Test
public void testQuery3() throws Exception {
// 创建查询对象
Query query = new Query();
// 构建限制条件 { "$or": [{"name": "dafei"}, {"age": {"$lt": 30}}] }
Criteria criteria = new Criteria();
// and关系就用andOperator
criteria.orOperator(
Criteria.where("name").is("dafei"),
Criteria.where("age").lt(30)
);
// 添加限制条件
query.addCriteria(criteria);
//执行查询语句
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}
}
模糊运算
@SpringBootTest
public class QueryTest {
//直接使用
@Autowired
private MongoTemplate mongoTemplate;
/**
* 查询所有name含有fei并且30<=age<=32的文档
* db.users.find({
* "$and":[
* {"name": {"$regex":".*fei.*"} },
* {"age": {"$gte":30, "$lte":32} }
* ]
* })
*/
@Test
public void testQuery4() throws Exception {
// 创建查询对象
Query query = new Query();
// 构建限制条件
Criteria criteria = new Criteria().andOperator(
Criteria.where("name").regex(".*fei.*"),
Criteria.where("age").gte(30).lte(32)
);
// 添加限制条件
query.addCriteria(criteria);
//执行查询语句
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}
}
分页查询
两种方式:使用Sort、使用Pageable(推荐)
@SpringBootTest
public class QueryTest {
//直接使用
@Autowired
private MongoTemplate mongoTemplate;
//一般不这么用
// 分页查询文档,显示第2页,每页显示3个,按照id升序排列
// mql:db.users.find({}).sort({id:1}).skip(3).limit(3)
@Test
public void testQuery2() throws Exception {
// 创建查询对象:理解为MQL语句拼接对象
Query query = new Query();
//排序对象,参数1:排序规则;参数2:排序列名
Sort sort = Sort.by(Sort.Direction.ASC,"id")
// 设置排序规则
query.with(sort);
// 设置分页信息
query.skip(3).limit(3);
//执行查询语句
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}
----------------------------------------------------------------------------------
//推荐
@Test
public void testQuery2() throws Exception {
// 查询对象
Query query = new Query();
// 分页参数对象,类似 QueryObject
//参数1:当前页,偏移量,从0开始;参数2:每页显示条数;参数3:排序规则;参数4:排序列
//显示第2页,每页显示3个,按照id升序排列
Pageable pageable = PageRequest.of(1, 3, Sort.Direction.ASC, "id");
// 设置分页信息
query.with(pageable);
//执行查询语句
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}
}