springboot 整合 mongodb
环境: spring boot 2.x ; mongodb v4.2.0 单机
目录
一、pom二、配置
1. Mongodb 数据库配置
2.事务 配置
三、mongoDB 常用类型说明
四、MongoDB 常用操作
1. 查询全部
2. 指定条件进行查询,以及And条件
3. 使用OR 来进行查询
4. 比较查询
5. IN 查询
6. Count
7. 分页
8. 模糊查询
9. 不等于
10. 排序
11. 不包含 not in
12. 编辑
五、JAVA 整合mongodb 常用操作
Ⅰ、使用 JPA 去操作
新增
条数查询
查询
删除
分页
ExampleMatcher说明
Ⅱ、使用 template 去操作
多条件查询
返回特定的字段
区间查询
模糊查询
修改数据
Ⅲ、template-分页
六、Filters的一些操作
正文
一、pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
二、配置
1. Mongodb 数据库配置
# mongodb 数据库
spring.data.mongodb.database=test1
# mongodb 服务器地址
spring.data.mongodb.uri=mongodb://192.168.137.128
# Redis 服务器连接端口
spring.data.mongodb.port=27017
2.事务 配置(注意:单节点的mongodb无法开启事务)
@Bean
MongoTransactionManager transactionManager(MongoDbFactory mongoDbFactory){
return new MongoTransactionManager(mongoDbFactory);
}
开启事务
@SpringBootApplication
@EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
三、mongoDB 常用类型说明
类型 | 描述 |
String | 字符串类型 |
Integer | 整数类型 |
Bollean | 布尔值 |
Double | 双精度浮点值 |
Arrays | 数组或列表 |
Timestamp | 时间搓 |
Object | 内嵌文档 |
Date | 日期时间 |
Object ID | 对象id |
Binary Data | 二进制数据 |
Code | 代码类型,已知支持javaScript |
Regular | 正则类型 |
四、MongoDB 常用操作
getCollection(tableName); – 获得表的Collection
1. 查询全部
db.collection.find();
e.g.:
db.getCollection('dept_info').find({}); --查询全部数据 db.getCollection('dept_info').find({},{name:1,sex:1}); -- 只返回表的 name和sex 字段
2. 指定条件进行查询,以及And条件
db.collection.find(field:search,…)
e.g.:
db.getCollection('dept_info').find({"name":"王重阳"}); --查询name为王重阳的 db.getCollection('dept_info').find({"name":"王重阳","sex":"男"}); --查询name为王重阳,sex为男的
3. 使用OR 来进行查询
db.collection.find({"$or":[{field:search},…]})
e.g.:
db.getCollection('dept_info').find("$or":[{"name":"王重阳"},{"sex":"男"}]); -- 查询name为王重阳或者sex 为男的
4. 比较查询
db.collection.find({field:{"$gt":1000}});
– gte(>=)、lte(<=)
e.g.:
db.getCollection('dept_info').find({"salary":{"$gte":1000}}); -- 查询 salary >=1000 的 db.getCollection('dept_info').find({"salary":{"$gte":1000},"salary":{"$lte":65000}}); -- 查询 1000 <= salary <= 65000
5. IN 查询
db.collection.find({field:{"$in":[v1,v2,…]}});
e.g.:
db.getCollection('dept_info').find({"name":{"$in":["春香","夏香"]}}); -- 查询 name 为 春香或者 夏香的 db.getCollection('dept_info').find({"name":{"$in":["春香","夏香"]},"sex":"女"}); -- 查询 name为春香或者 夏香的 sex为女的
6. Count
db.collection.count(Bson);
e.g.:
db.getCollection('dept_info').count(); --查询 dept_info 的条数 db.getCollection('dept_info').count({"name":{"$in":["春香","夏香"]},"sex":"女"}); -- 查询 name为 name 为 春香或者 夏香的的条数
7. 分页
db.collection.find(Bson),skip(起始条数).limit(终止条数)
e.g.:
db.getCollection('dept_info').find({"salary":{"$gte":100},"salary":{"$lte":500}}).skip(pageSize*pageNum-1).limit(pageNum); --查询salary 介于[100,500) 的 条数pageSize-(pageNum-1) 查询 pageNum;
8. 模糊查询
db.collection.find({field:/.search./});
.* search – %search 前模糊查询
search.* – search% 后模糊查询
.search. – %search% 前后模糊查询
e.g.:
db.getCollection('dept_info').find({"mobile":/136.*/}); --查询手机号 136开头的
9. 不等于
db.collection.find(field:{"$ne":search});
e.g.:
db.getCollection("dept_info").find({"name":{"$ne":"张无忌"}});--查询 name 不包括张无忌的
10. 排序
db.collection.find(Bson).sort({“field”:1});
sort({filed:1}); – 按照 field 升序
sort({filed:-1}); --按照 field 降序
e.g.:
db.getCollection("dept_info").find().sort({"salary":-1}); --按照salary 降序查询
11. 不包含 not in
db.collection.find({field:{"$nin":[v1,v2,…]}});
e.g.:
db.getCollection('dept_info').find({"name":{"$nin":["春香","夏香"]}}); -- 查询 name 不为 春香或者 夏香的
12. 编辑
db.collection.update(selectBson,UpdateBson);
selectBson: 查询条件bson 例如:{“name”:“张三”},以张三作为查询条件
UpdateBson: 数据修改方,注意如果不加 $set 则是文档替换 加上才是指定字段修改
e.g.:
db.getCollection('dept_info').update({"_id":"1572570469058571910510215403"},{"name" : "丘处机"}); //修改id 为 1572570469058571910510215403 的文档为 {"name" : "丘处机"} ---> 改前 { "_id" : "1572570469058571910510215403", "name" : "丘处机", "sex" : "男", "mobile" : "13892111116", "salary" : 888888, "createTimeLong" : "1572570469533", "_class" : "com.study.www.entity.Dept" } ----> 改后 { "_id" : "1572570469058571910510215403", "name" : "丘处机" } db.getCollection('dept_info').update({"_id":"1572570469058571910510215403"},{ "$set" : {"mobile" : "13892111116", "salary" : 6666666} }); ---> 改前 { "_id" : "1572570469058571910510215403", "name" : "丘处机", "sex" : "男", "mobile" : "13892111116", "salary" : 888888, "createTimeLong" : "1572570469533", "_class" : "com.study.www.entity.Dept" } ----> 改后 { "_id" : "1572570469058571910510215403", "name" : "丘处机", "sex" : "男", "mobile" : "13892111116", "salary" : 6666666.0, "createTimeLong" : "1572570469533", "_class" : "com.study.www.entity.Dept" }
五、JAVA 整合mongodb 常用操作
Ⅰ、使用 JPA 去操作
dao继承 MongoRepository<T,String>
e.g.:
@Repository public interface DeptRepository extends MongoRepository<Dept,String> { }
新增
repository.save(dept);
条数查询
repository.count(Example);
e.g.:
Dept dept = new Dept(); dept.setName(name); ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.hashCode()); Example<Dept> example = Example.of(dept, matcher); return deptRepository.count(example);
查询
repository.deptRepository.(Example);
或
repository.findById(String id);
e.g.:
deptRepository.findById(id).orElseGet(() -> null); --根据id去查询 Dept dept = new Dept(); dept.setName(name); ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.hashCode()); Example<Dept> example = Example.of(dept, matcher); return deptRepository.findById(example);
删除
repository.deleteById(String id);
分页
Page pageResult= Repository.findAll(example, pageRequest);
PageRequest pageRequest = PageRequest.of(startInt, endInt, Sort.Direction.DESC, "id"); Dept dept = new Dept(); dept.setMobile(mobile); ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("mobile", ExampleMatcher.GenericPropertyMatchers.startsWith()); Example<Dept> example = Example.of(dept, exampleMatcher); return deptRepository.findAll(example, pageRequest);
ExampleMatcher 说明
ExampleMatcher可以构成Example来进行查询条件的制作;如下
ExampleMatcher.GenericPropertyMatchers.hashCode() -- 等同于 sql 中的 == ,mongo中的{field,v}
ExampleMatcher.GenericPropertyMatchers.startWith() -- 等同于 sql中的 search%, mongo中的{field,/v.*/}
ExampleMatcher.GenericPropertyMatchers.endWith() -- 等同于 sql中的 %search, mongo中的{field,/.*v/}
Ⅱ、使用 template 去操作
多条件查询
//根据 mobile id createTimeLong 去查询 等同于SQL where mobile = X OR id = X OR createTimeLong = X
//转为mongo: db.collection.find({"$or":[{"mobile":X},{"id":X},{"createTimeLong":X}]})
Criteria criteriaMobile = Criteria.where("mobile").is(search);
Criteria criteriaSearch = Criteria.where("id").is(search);
Criteria criteriaTimeLong = Criteria.where("createTimeLong").is(search);
Criteria criteria = new Criteria().orOperator(criteriaSearch, criteriaMobile,criteriaTimeLong);
Query query = new Query().addCriteria(criteria);
// 转为 BSON 等同于
Bson mobileQueryBson = Filters.eq("mobile", search);
Bson idQueryBson = Filters.eq("id", search);
Bson createTimeLongQueryBson = Filters.eq("createTimeLong", search);
Bson queryBson = Filters.or(mobileQueryBson, idQueryBson, createTimeLongQueryBson);
MongoCollection<Document> collection = mongoTemplate.getCollection(
Objects.requireNonNull(TableUtils.getTableName(Dept.class)));
FindIterable<Document> documents = collection.find(queryBson);
return mongoTemplate.findOne(query, Dept.class);
返回特定的字段
// 根据 name 做全模糊查询,回显 name,_id,sex 等同于SQL: select _id,name,sex from table where name like '%search%'
//转为mongodb: db.collection.find({"name":/.*search.*/})
Bson queryName = Filters.regex("name", ".*" + name + ".*");
//回显的字段
Document document = new Document();
document.put("name",1);
document.put("_id",1);
document.put("sex",1);
return pageHelper.pageQuery(queryName, Dept.class, pageNum, pageSize,selectDocument);
区间查询
// 根据 salary 做区间查询等同于 [starSalary,endSalary] 等同于 SQL: select * from table where salary >= 'startSalary' AND salary <= 'endSalary'
//转为 mongodb: db.collection.find({"salary":{"$gte":startSalary},"salary":{"$lte":endSalary}})
MongoCollection<Document> collection = mongoTemplate.getCollection(
Objects.requireNonNull(TableUtils.getTableName(Dept.class))
);
// >= startSalary
Bson startSalaryBson = Filters.gte("salary", startSalary.doubleValue()*1000);
// <= endSalary
Bson endSalaryBson = Filters.lte("salary", endSalary.doubleValue()*1000);
Bson queryBson = Filters.and(startSalaryBson, endSalaryBson);
FindIterable<Document> documents = collection.find(queryBson);
return BeanUtils.copyList(documents,Dept.class);
模糊查询
//根据 名称 OR 电话 OR 性别 做模糊查询,三者的区别 名称前后模糊,电话后模糊,性别全匹配 等同于SQL: SELECT * FROM table where name like "%search%" OR mobile like "search%" OR sex = "search"
//转为 mongodb bson: db.collection.find({"$or":[{"name":/.*136.*/},{"mobile": /136.*/},{"sex":"136"}]})
Bson queryBson = new BsonDocument();
// 前后模糊 等同于 like %search%
Bson likeName = Filters.regex("name", ".*" + search + ".*");
// 后模糊 等同于 like search%
Bson likeMobile = Filters.regex("mobile", search + ".*");
// 等同于 ==
Bson eqSex = Filters.eq("sex", search);
// 使用 or 将三者拼接
queryBson = Filters.or(likeMobile, likeName, eqSex);
return pageHelper.pageQuery(queryBson, Dept.class, pageNum, pageSize);
删除数据
//根据名称去删除数据,等同于SQL: delete from table where name = "search"
//转为 mongod bson: db.getCollection('dept_info').remove({name:"name"})
Bson queryBson = Filters.eq("name", name);
MongoCollection<Document> collection = mongoTemplate.getCollection(Objects.requireNonNull(TableUtils.getTableName(Dept.class)));
collection.deleteMany(queryBson);
修改数据
//根据id去修改其他信息 注意需要加上 $set
//等同于SQL: update table set ... where id = $search$
//转为mongo bson: db.getCollection('dept_info').update({"_id":"157257046"},{"$set" : {....}})
MongoCollection<Document> collection = mongoTemplate.getCollection(Objects.requireNonNull(TableUtils.getTableName(Dept.class)));
Bson mongoIdSearch = Filters.eq("_id", dept.getId());
dept.setId(null);
HashMap<String, Object> changeMap = new HashMap<>(1);
changeMap.put("$set",dept);
BsonDocument update = BsonDocument.parse(JSONObject.toJSONString(changeMap));
collection.updateMany(mongoIdSearch,update);
Ⅲ、template-分页
mongodb中使用bson分页是:db.collection.find(Bson).skip(起始条数).limit(终止条数),
其的做法是查询出终止条数然后再减去起始条数,例如: db.collection.find().skip(50000).limit(50010)。mongodb会先查出 50010 条然后再丢弃前 50000条。我们可以利用id进行 lastId < 来操作(需要根据id进行排序)
db.collection.find({"_id":{"$gte":1000}}),skip(起始条数).limit(终止条数);
待你用心观察你会发现查询条件都在find 里面,我们大可将其抽取成为一个分页工具类如下:
@Component
public class MongoPageHelper {
// 最小页码
public static final int FIRST_PAGE_NUM = 1;
/** mongodb自带得id */
public static final String MONGO_ID = "_id";
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private MongoConverter mongoConverter;
@Autowired
public MongoPageHelper(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
/**
* 分页查询,不考虑条件分页,直接使用skip-limit来分页.
*
*/
public <T> PageResult<T> pageQuery(Bson queryBson, Class<T> clazz,Integer pageNum, Integer pageSize) {
return pageQuery(queryBson, clazz,pageNum, pageSize, null,null);
}
public <T> PageResult<T> pageQuery(Bson queryBson, Class<T> clazz,Integer pageNum, Integer pageSize,String lastId) {
return pageQuery(queryBson, clazz,pageNum, pageSize, lastId,null);
}
public <T> PageResult<T> pageQuery(Bson queryBson, Class<T> clazz,Integer pageNum, Integer pageSize,Document selectDocument) {
return pageQuery(queryBson, clazz,pageNum, pageSize, null,selectDocument);
}
/**
* 分页查询.
*
* @param queryBson Bson,构造你自己的查询条件.
* @param clazz Mongo collection定义的 class,用来确定查询哪个集合.
* @param pageSize 分页的大小.
* @param pageNum 当前页.
* @param lastId 条件分页参数, 区别于skip-limit,采用find(_id>lastId).limit分页.
* 如果不跳页,像朋友圈,微博这样下拉刷新的分页需求,需要传递上一页的最后一条记录的ObjectId。 如果是null,则返回pageNum那一页.
* @param <T> collection定义的class类型.
* @param selectDocument 查询返回的字段组.
* @return PageResult,一个封装page信息的对象.
*/
public <T> PageResult<T> pageQuery(Bson queryBson, Class<T> clazz,Integer pageNum, Integer pageSize,String lastId,Document selectDocument) {
//分页逻辑
MongoCollection<Document> collection = mongoTemplate.getCollection(Objects.requireNonNull(TableUtils.getTableName(clazz)));
long total = collection.countDocuments(queryBson);
final PageResult<T> pageResult = new PageResult<>();
pageResult.setTotal(total);
pageResult.setPageSize(pageSize);
pageResult.setPageNum(pageNum);
//总页数
final Integer pages = (int) Math.ceil(total / (double) pageSize);
if (pageNum <= 0) {
pageNum = FIRST_PAGE_NUM;
}
// 如果查询页数大于总页数则为空
if (pageNum > pages){
pageResult.setList(Lists.newArrayList());
return pageResult;
}
FindIterable<Document> findIterable = null;
//默认根据id 排序
Bson mongoIdAscBson = Sorts.orderBy(Sorts.ascending(MONGO_ID));
if (StringUtils.isNotBlank(lastId)) {
if (pageNum != FIRST_PAGE_NUM) {
Bson lastIdBson = Filters.gt(MONGO_ID, new ObjectId(lastId));
queryBson =Filters.and(queryBson, lastIdBson);
}
findIterable = collection.find(queryBson).sort(mongoIdAscBson);
} else {
int skip = pageSize * (pageNum - 1);
findIterable = collection.find(queryBson).sort(mongoIdAscBson).skip(skip);
}
findIterable = findIterable.limit(pageSize);
if (!ObjectUtils.isEmpty(selectDocument)) {
findIterable.projection(selectDocument);
}
MongoCursor<Document> iterator = findIterable.iterator();
List<T> list = Lists.newArrayList();
while (iterator.hasNext() ){
Document next = iterator.next();
if (!ObjectUtils.isEmpty(next.get(MONGO_ID))) {
next.put("id",next.get(MONGO_ID).toString());
}
list.add(mongoConverter.read(clazz,next));
}
pageResult.setList(list);
return pageResult;
}
}
六、Filters的一些操作
方法 | 含义 |
Bson bson = Filters.regex(field, “." + search + ".”); | field 字段名, search 搜索条件前后拼 .* 代表%的意思 |
Bson bson = Filters.eq(field, search); | field 字段名,search 搜索条件等于SQL : where field = search |
Bson bson = Filters.or(Bson…) | Bson 上文的搜索条件,多个条件 OR 串联 |
Bson bson = Filters.gte(field, search); 同类的 gt,lt,lte | field 字段名,search 搜索条件等于SQL : where field <= search |
Bson bson = Filters.or(Bson…); | Bson 上文的搜索条件,多个条件 AND 串联 |
Bson bson = Sorts.orderBy(Sorts.ascending(field)); Sorts.orderBy(Sorts.descending(field)); | field 字段名, 排序 ASC |