1. 假装这是个前言
在前面的博客中我写了MongoDB Shell基本指令操作,而在开发中,我们通常是用代码去操作数据库的,对于java来说有许多的方式去操作数据库,一个是用Java原生的JDBC接口,要去下载MongoDB的jdbc驱动包,但我们开发往往不会使用原生的jdbc,原生的jdbc每一次操作都得自己建立起连接,想要细细了解MongoDB的jdbc驱动的可以点这个MongoDB Driver官方帮助文档进行学习,所以我们这一篇博客,写的是用springboot去整合MongoDB,springboot里面自带了一个MongoDB的依赖,叫spring-data-mongo
帮助文档:https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#reference
API帮助文档:https://docs.spring.io/spring-data/mongodb/docs/current/api/
2. 演示
2.1 导入依赖
导入spring-data-mongodb依赖
<!--MongoDb依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.6.2</version>
</dependency>
目录结构如下
2.2 编写配置文件
编写配置文件application.yaml
spring:
data:
mongodb:
host: localhost
port: 27017
database: test
# 我这里的数据库没有设用户名和密码,所以密码和用户名就没指定了
# 也可以用下面这种方式写,效果等同于上面的host port database分开写的效果
# uri: mongodb://localhost:27017/person
server:
port: 8888
2.3 编写映射实体类
PersonInfo.java
package edu.jsu.mongodemo.pojo.dto;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.io.Serializable;
import java.util.List;
/**
* @author Mo
* @createTime 2022/1/20 15:41
* @description
*/
@Data
@Document(collection = "person")
public class PersonInfo implements Serializable {
// 标记主键
@Id
private String id;
// 标记字段
@Field
private String name;
// 可以映射数据库内字段名称
@Field(value = "score_list")
private List<SubjectScore> subjectScoreList;
}
@Id - 用于字段级别,标记这个字段是一个主键,默认生成的名称是“_id”
@Document - 用于类,以表示这个类需要映射到数据库,您也可以指定映射到数据库的集合名称
@DBRef - 用于字段,以表示它将使用com.mongodb.DBRef进行存储。
@Indexed - 用于字段,表示该字段需要如何创建索引
@CompoundIndex - 用于类,以声明复合索引
@GeoSpatialIndexed - 用于字段,进行地理位置索引
@TextIndexed - 用于字段,标记该字段要包含在文本索引中
@Language - 用于字段,以设置文本索引的语言覆盖属性。
@Transient - 默认情况下,所有私有字段都映射到文档,此注解将会去除此字段的映射
@PersistenceConstructor - 标记一个给定的构造函数,即使是一个protected修饰的,在从数据库实例化对象时使用。构造函数参数通过名称映射到检索的DBObject中的键值。
@Value - 这个注解是Spring框架的一部分。在映射框架内,它可以应用于构造函数参数。这允许您使用Spring表达式语言语句来转换在数据库中检索的键值,然后再用它来构造一个域对象。为了引用给定文档的属性,必须使用以下表达式:@Value("#root.myProperty"),root要指向给定文档的根。
@Field - 用于字段,并描述字段的名称,因为它将在MongoDB BSON文档中表示,允许名称与该类的字段名不同。
@Version - 用于字段锁定,保存操作时检查修改。初始值是0,每次更新时自动触发。??
SubjectScore.java
package edu.jsu.mongodemo.pojo.dto;
import org.springframework.data.mongodb.core.mapping.Field;
/**
* @author Mo
* @createTime 2022/1/23 15:43
* @description
*/
@Data
public class SubjectScore {
@Field("subject_name")
private String subjectName;
private Double score;
}
2.4 CRUD编写
2.4.1 Bson文档格式
/* 1 */
{
"_id" : ObjectId("61ed0eafc2da593110662f7e"),
"name" : "mo",
"scoreList" : [
{
"subjectName" : "语文",
"score" : 83
},
{
"subjectName" : "数学",
"score" : 150
},
{
"subjectName" : "英语",
"score" : 666
}
],
"_class" : "edu.jsu.mongodemo.pojo.dto.PersonInfo"
}
/* 2 */
{
"_id" : ObjectId("61ed5c816d978a6bfe8b50a3"),
"name" : "C4ptainMo",
"scoreList" : [
{
"subjectName" : "语文",
"score" : 104
},
{
"subjectName" : "数学",
"score" : 114
},
{
"subjectName" : "英语",
"score" : 118
}
],
"_class" : "edu.jsu.mongodemo.pojo.dto.PersonInfo"
}
/* 3 */
{
"_id" : ObjectId("61ed5cb36d978a6bfe8b50a4"),
"name" : "ProphetMo",
"scoreList" : [
{
"subjectName" : "语文",
"score" : 104
},
{
"subjectName" : "数学",
"score" : 99
},
{
"subjectName" : "英语",
"score" : 134
}
],
"_class" : "edu.jsu.mongodemo.pojo.dto.PersonInfo"
}
2.4.2 Controller层
package edu.jsu.mongodemo.controller;
import edu.jsu.mongodemo.pojo.dto.PersonInfo;
import edu.jsu.mongodemo.pojo.vo.CommonResult;
import edu.jsu.mongodemo.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author Mo
* @createTime 2022/1/20 23:23
* @description
*/
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private TestService testService;
@PostMapping("/add")
public CommonResult addPerson(@RequestBody PersonInfo personInfo) {
return testService.addPerson(personInfo);
}
@PostMapping("/delete")
public CommonResult deletePerson(@RequestParam("Id")String Id) {
return testService.deletePerson(Id);
}
@PostMapping("/updateScore")
public CommonResult updatePersonScore(@RequestParam("Id")String Id, @RequestParam("score")Integer score) {
return testService.updatePersonScore(Id, score);
}
@GetMapping("/queryAllPerson")
public CommonResult queryAllPerson() {
return testService.queryPersonList();
}
@PostMapping("/queryById")
public CommonResult queryPersonById(@RequestParam("Id")String Id) {
return testService.queryPersonById(Id);
}
}
2.4.2 Service层
因为是演示,我就直接把实现写在Service里面了
package edu.jsu.mongodemo.service;
import edu.jsu.mongodemo.dao.TestDao;
import edu.jsu.mongodemo.pojo.dto.PersonInfo;
import edu.jsu.mongodemo.pojo.vo.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author Mo
* @createTime 2022/1/21 0:51
* @descripton
*/
@Service
public class TestService {
@Autowired
private TestDao testDao;
public CommonResult addPerson(PersonInfo personInfo) {
testDao.addPerson(personInfo);
return new CommonResult(200, "添加成功");
}
public CommonResult deletePerson(String Id) {
testDao.deletePerson(Id);
return new CommonResult(200, "删除成功");
}
public CommonResult updatePersonScore(String Id, Integer score) {
testDao.updatePersonScore(Id, score);
return new CommonResult(200, "修改成功");
}
public CommonResult queryPersonById(String Id) {
return new CommonResult(200, "查询成功", testDao.findPersonById(Id));
}
public CommonResult queryPersonList() {
return new CommonResult(200, "查询成功", testDao.queryPersonList());
}
}
2.4.3 dao层编写
package edu.jsu.mongodemo.dao;
import edu.jsu.mongodemo.pojo.dto.PersonInfo;
import java.util.List;
/**
* @author Mo
* @createTime 2022/1/22 16:07
* @description 使用mongodbTemplate进行数据操作,适合复杂的数据操作,推荐使用
*/
public interface TestDao {
void addPerson(PersonInfo personInfo);
void deletePerson(String id);
void updatePersonScore(String id, Integer score, String subjectName);
List<PersonInfo> queryPersonList();
PersonInfo findPersonById(String id);
}
dao层实现层
package edu.jsu.mongodemo.dao.daoImpl;
import com.mongodb.client.result.UpdateResult;
import edu.jsu.mongodemo.dao.TestDao;
import edu.jsu.mongodemo.pojo.dto.PersonInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
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 java.io.Serializable;
import java.util.List;
/**
* @author Mo
* @createTime 2022/1/22 16:07
* @description
*/
@Repository
public class TestDaoImpl implements TestDao {
@Autowired
private MongoTemplate mongoTemplate;
private static final Log log = LogFactory.getLog(TestDaoImpl.class);
@Override
public void addPerson(PersonInfo personInfo) {
// 有两种mongoTemplate提供的CRUD方法,
// 一套需要指明操作的集合,一套不需要指明,
// 根据传入映射对象的注释来判断,
// 例如PersonInfo类,如果没有@Document注释那么默认为类名作为集合名插入
// mongoTemplate.insert(personInfo);
Serializable person1 = mongoTemplate.insert(personInfo, "person");
// 输出序列化对象
log.info(person1);
}
@Override
public void deletePerson(String id) {
Serializable andRemove = mongoTemplate.findAndRemove(Query.query(
Criteria.where("id").is(id)
), PersonInfo.class, "person");
log.info(andRemove);
}
@Override
public void updatePersonScore(String id, Integer score, String subjectName) {
Query query = new Query(Criteria.where("id").is(id)
// 在查询里面写出子查询条件
.and("scoreList.subjectName").is(subjectName));
// 这个update方法相当于一个静态工厂,会生成一个Update对象,并执行set方法,上面的那个Criteria.where()同理
Update update = Update.update("scoreList.$.score", score);
// 写法1
PersonInfo oldPersonInfo = mongoTemplate.update(PersonInfo.class)
// 匹配符合query的对象
.matching(query)
// 修改操作
.apply(update)
// 执行
.findAndModifyValue();
// 输出执行修改前的数据
log.info("old PersonInfo: " + oldPersonInfo);
PersonInfo newPersonInfo = mongoTemplate.query(PersonInfo.class)
.matching(query)
.oneValue();
// 输出修改后的数据
log.info("new PersonInfo: " + newPersonInfo);
PersonInfo personInfo = mongoTemplate.update(PersonInfo.class)
.matching(query)
.apply(Update.update("scoreList.$.score", 100))
// 用这个可以添加修改时的选项,这里是返回修改后的值,区别于上面那个修改
.withOptions(FindAndModifyOptions.options().returnNew(true))
.findAndModifyValue();
log.info("optional update result: " + personInfo);
// 写法2
UpdateResult updateResult = mongoTemplate.updateFirst(query, Update.update("scoreList.$.score", 666), PersonInfo.class);
log.info("update result: " + updateResult);
}
@Override
public List<PersonInfo> queryPersonList() {
List<PersonInfo> personInfoList = mongoTemplate.findAll(PersonInfo.class, "person");
log.info("queried PersonInfoList: " + personInfoList);
return personInfoList;
}
@Override
public PersonInfo findPersonById(String id) {
PersonInfo one = mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), PersonInfo.class, "person");
log.info(one);
return one;
}
}
期间遇到了许多问题,比如说集合里面的名字命名规范最好不要有下划线“_”,不然无法识别会报错,再然后就是文档的子嵌套查询,查询方式可以看我写的更新操作,我一直在想那个update的filterArray()方法咋用,在StackOverflow上也找了半天,下次等有时间去好好看看官方文档,不过最后还是解决了这个问题,在Query里面就限定好要查询的内容就行,被这么个问题卡这么久,血压都升上来了。
至此,spring-data-mongodb已经入门上手了,还有个分页查询我没写着里面来,等下次有时间再更新下分页查询的写法,其实也很简单就是query().skip(/ 跳过的数据数 /).limit(/每一页的数据量/)这样就实现了分页查询哈哈哈哈哈,下次详细说说,溜了