目录
前言
带着问题学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。
以上系个人理解,如果存在错误,欢迎大家指正。原创不易,转载请注明出处!