最近在项目中使用MongoDb先简单介绍MongoDb
MongoDb是什么?
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
干什么?
这个简单,肯定是存储数据。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
怎么用?
mongoDb的基础操作命令
自行百度吧。。。
mongoDb菜鸟教程
项目中如何进行MongoDb的CRUD
可以使用两种方式MongoRepository方式和mongoTemplate方式
MongoRepository方式
- 这种方式只有查询和新增,而且只需要创建仓库继承接口MongoRepository,按照规则创建方法就行。
public interface LogRepository extends MongoRepository<Log>{
}
复制代码
看一下MongoRepository源码里有什么方法
@NoRepositoryBean
public interface MongoRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
<S extends T> List<S> save(Iterable<S> var1);
List<T> findAll();
List<T> findAll(Sort var1);
<S extends T> S insert(S var1);
<S extends T> List<S> insert(Iterable<S> var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
复制代码
- 所以save和findAll可以直接使用,Sort为排序。如果想根据条件查询的话可以按照规则输入方法名。当需要根据实体类中的属性查询时,MongoRepository提供的方法已经不能满足,我们需要在Repository仓库中定义方法,定义方法名的规则为:find + By + 属性名(首字母大写),例如:
/**
* 根据地址查询
*
* @param address
* @author zyc
* @return
*/
List<AlarmLog> findByAddress(String address);
/**
* 根据地址查询带分页
*
* @param address
* @author zyc
* @return
*/
Page<AlarmLog> findByAddress(String address, Pageable pageable);
复制代码
- 当需要根据实体类中的属性进行模糊查询时,我们也需要在PersonRepository仓库中定义方法,模糊查询定义方法名的规则为:find + By + 属性名(首字母大写) + Like
/**
* 根据地址查询带分页
*
* @param address
* @author zyc
* @return
*/
Page<AlarmLog> findByAddressLike(String address, Pageable pageable);
复制代码
- 查询指定的值在上面写注解。其中value是查询的条件,?0这个是占位符,对应着方法中参数中的第一个参数,如果对应的是第二个参数则为?1。fields是我们指定的返回字段,其中id是自动返回的,不用我们指定,json中{‘address’:1}的1代表true,也就是代表返回的意思。
@Query(value="{'address':?0}",fields="{'address':1}")
public Page<Person> findByAddressLike(String address,Pageable pageable);
复制代码
MongoTemplate方式
个人感觉这种方式更加全面一点,更好理解。
- pom引入mongoDB的依赖 pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
复制代码
- 配置文件配置连接 application.yml:
server:
port: 8080
spring:
data:
mongodb:
uri: mongodb://localhost:27017/test01
application:
name: myserver
复制代码
- 在需要向mongoDB CRUD时引入MongoTemplate
@Repository("userDao")
public class UserDaoImpl implements UserDao {
/**
* 由springboot自动注入,默认配置会产生mongoTemplate这个bean
*/
@Autowired
private MongoTemplate mongoTemplate;
@Override
public void insert(User user) {
//插入数据id自增(mongoDB中存在自己的_id,如果不自己定义自增,会默认返回mongoDB中的_id,我看网上说是不建议将_id自增的,因为高并发会影响性能。从这看的https://segmentfault.com/q/1010000006019599)
mongoTemplate.insert(user);
}
@Override
public void update(User user) {
//根据id修改 update user set name=user.getName(),age=user.getAge() where id =user.getId()
mongoTemplate.updateFirst(new Query(Criteria.where("id").is(user.getId())),
new Update().set("name", user.getName()).set("age", user.getAge()), User.class);
}
@Override
public List<User> findAll() {
//select * from A
return mongoTemplate.findAll(User.class);
}
@Override
public User findOne() {
//select * from A LIMIT 1
return mongoTemplate.findOne(new Query(), User.class);// 第一条
}
@Override
public User findById(Long id) {
//Select * from A where a.id=id;
return mongoTemplate.findById(id, User.class);// id查询
}
@Override
public List<User> find() {
//select name from A where name='10'
DBObject dbObject = new BasicDBObject();
dbObject.put("name", "10"); // 查询条件
BasicDBObject fieldsObject = new BasicDBObject();
// 指定返回的字段
fieldsObject.put("name", true);
return mongoTemplate.find(new BasicQuery(dbObject, fieldsObject), User.class);
}
@Override
public List<User> findOr() {
//select * from A where name='10' or age=13
return mongoTemplate.find(
new Query(new Criteria().orOperator(Criteria.where("name").is("10"),Criteria.where("age").is(11))),
User.class);
}
@Override
public List<User> findAnd(){
//select * from A where name='13' and id=1
return mongoTemplate.find(new Query(Criteria.where("name").is("13").and("id").is(1)), User.class);
}
@Override
public List<User> findOrAnd() {
//select * from A where a.id=1 and (age=9 or name =13)
return mongoTemplate.find(
new Query(Criteria.where("id").is(1).andOperator(new Criteria().orOperator(new Criteria().orOperator(Criteria.where("age").is(10),Criteria.where("name").is("13"))))),
User.class);
}
@Override
public List<User> findDayuXiaoyu(){
//select * from A where age>1 and age<10
//gte大于等于 lte小于等于
//ge大于 lt小于
//ne 不等于
Query ge_lt = new Query(Criteria.where("age").gt(1).lt(10));//大于等于
Query gte_lte = new Query(Criteria.where("age").gte(1).lte(10));//小于等于
Query ne = new Query(Criteria.where("age").ne(5));//不等于
return mongoTemplate.find(ne, User.class);
}
/**
* 模糊查询 regex
完全匹配 "^hzb$"
右匹配 "^.*hzb$"
左匹配 "^hzb.*$"
双开 "^.*hzb.*$"
*/
@Override
public List<User> findLike(){
Query query = new Query(Criteria.where("name").regex("^.*2$"));
return mongoTemplate.find(query, User.class);
}
(自己使用碰到的问题)
碰到模糊查询的问题发现并不是好用,碰到一些检索带特殊符号的不可用,解决方案:判断模糊查询条件里面是否有特殊符号,加上转义字符。
Pattern pattern = Pattern.compile(".*?" + escapeExprSpecialWord(alarmLogCondition.getMessage()) + ".*");
criteria = Criteria.where("message").regex(pattern);
query.addCriteria(criteria);
List<AlarmLog> alarmLogs = mongoOperations.find(query, AlarmLog.class);
private String escapeExprSpecialWord(String keyword) {
if (StringUtils.isNotBlank(keyword)) {
String[] fbsArr = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" };
for (String key : fbsArr) {
if (keyword.contains(key)) {
keyword = keyword.replace(key, "\\" + key);
}
}
}
return keyword;
}
@Override
public List<User> findInNot(){
//in 和not in
Query in = new Query(Criteria.where("age").in(5,6,7));
//in
Query notIn = new Query(Criteria.where("age").nin(5,6,7));
//not in
return mongoTemplate.find(notIn, User.class);
}
@Override
public List<User> findDie(){
Query findDie = new Query(Criteria.where("age").mod(9, 1));
//取模(取余) age % 5==1
return mongoTemplate.find(findDie, User.class);
}
@Override
public List<User> findAllMatching(){
Query findAllMatching = new Query(Criteria.where("list").all(1L,2L));
//all 数据 [name=10, age=10, id=4, list=[1, 2]]
//完全匹配返回数据
return mongoTemplate.find(findAllMatching, User.class);
}
@Override
public List<User> findSize(){
Query findSize = new Query(Criteria.where("list").size(2));
//size 数据 [name=10, age=10, id=4, list=[1, 2]] 匹配数组内的元素数量的 list.size =2 返回
return mongoTemplate.find(findSize, User.class);
}
@Override
public List<User> findExists(){
Query findExists = new Query(Criteria.where("list").exists(false));
//exists 判断字段是否为空 等同于sql的 list is null 和 list is not null
return mongoTemplate.find(findExists, User.class);
}
@Override
public List<User> findType(){
Query findType = new Query(Criteria.where("list").type(4));
//匹配数据类型
//Double 1 “double”
//String 2 “string”
//Object 3 “object”
//Array 4 “array”
//Binary data 5 “binData”
//Undefined 6 “undefined” Deprecated.
//ObjectId 7 “objectId”
//Boolean 8 “bool”
//Date 9 “date”
//Null 10 “null”
//Regular Expression 11 “regex”
//DBPointer 12 “dbPointer” Deprecated.
//JavaScript 13 “javascript”
//Symbol 14 “symbol” Deprecated.
//JavaScript (with scope) 15 “javascriptWithScope”
//32-bit integer 16 “int”
//Timestamp 17 “timestamp”
//64-bit integer 18 “long”
//Decimal128 19 “decimal” New in version 3.4.
//Min key -1 “minKey”
//Max key 127 “maxKey”
return mongoTemplate.find(findType, User.class);
}
@Override
public List<User> findNot() {
Query findDie = new Query(Criteria.where("age").not().mod(9, 1));
//not取反
return mongoTemplate.find(findDie, User.class);
}
@Override
public long count() {
Query count = new Query(Criteria.where("age").not().mod(9, 1));
//总条数
return mongoTemplate.count(count, User.class);
}
@Override
public List<?> distinct(){
return mongoTemplate.getCollection("User").distinct("name");
}
@Override
public List<?> sort(){
Query sort = new Query();
//排序
return mongoTemplate.find(sort.with(new Sort(Direction.DESC, "id")), User.class);
}
@Override
public List<?> limit(){
Query limit = new Query();
//分页
//skip 设置起始数
//limit 设置查询条数
return mongoTemplate.find(limit.skip(1).limit(3), User.class);
}
}
复制代码
复杂分页查询 我的理解这里的query就是用来封装查询条件的 criteria.and().is() 在这里是构建了一个精准查询的条件,并且用 'and' 相连。
@Override
public Page<AlarmLog> findAlarmLog(LogCondition logCondition) {
log.info("进入查询告警接口,请求参数LogCondition={}",JSON.toJSONString(logCondition));
int currentPage = logCondition.getCurrentPage();
int pageSize = logCondition.getPageSize();
Date startTime = logCondition.getStartTime();
Query query = new Query();
//设置起始数
query.skip((currentPage - 1) * pageSize);
//设置查询条数
query.limit(pageSize);
Criteria criteria = new Criteria();
if (StringUtils.isNotEmpty(logCondition.getSystem())) {
criteria.and("system").is(logCondition.getSystem());
}
if (StringUtils.isNotEmpty(logCondition.getLevel())) {
criteria.and("level").is(logCondition.getLevel());
}
if (StringUtils.isNotEmpty(logCondition.getHostname())) {
criteria.and("hostname").is(logCondition.getHostname());
}
if (StringUtils.isNotEmpty(logCondition.getMessage())) {
criteria.and("message").is(logCondition.getMessage());
}
if (StringUtils.isNotEmpty(logCondition.getStatus())) {
criteria.and("status").is(logCondition.getStatus());
}
if (StringUtils.isNotEmpty(logCondition.getStartTime()) && StringUtils.isNotEmpty(logCondition.getEndTime())) {
criteria.and("createTime").gte(logCondition.getStartTime()).lte(logCondition.getEndTime());
} else if (StringUtils.isNotEmpty(logCondition.getStartTime())) {
criteria.and("createTime").gte(logCondition.getStartTime());
} else if (StringUtils.isNotEmpty(logCondition.getEndTime())) {
criteria.and("createTime").lte(logCondition.getEndTime());
}
query.addCriteria(criteria);
query.with(new Sort(Sort.Direction.DESC, "createTime"));
List<AlarmLog> alarmLogs = mongoOperations.find(query,AlarmLog.class);
log.info("查询结果alarmLogs={}", JSON.toJSONString(alarmLogs));
int count = (int) mongoOperations.count(query, AlarmLog.class);
Page<AlarmLog> logPage = new Page<>();
logPage.setRecords(alarmLogs);
logPage.setTotal(count);
logPage.setSize(logPage.setSize(count % pageSize == 0 ? count / pageSize : count / pageSize + 1););
复制代码
这里解释一下,上面是最近做的一个查询,是对多个字段可能有可能没有的分页查询,所以我只能用If做判断处理,里面有个查询条件是个开始时间和截止时间内容。对一个字段做两次
criteria.and("createTime").gte()
criteria.and("createTime").lte()
复制代码
会报错,所以使用了上面颇为丑陋的if else判断。写的时候查到了这种情况的处理方式,用到 andOperator 方法。试了一下可以。
//大于方法
Criteria gt = Criteria.where("createTime").gte("你的条件");
//小于方法
Criteria lt = Criteria.where("createTime").lte("你的条件");
/**
* new Criteria().andOperator(gt,lt) 把上面的大于和小于放在一起,注意上面两个key一定是一样的
* andOperator() 这个方法就是关键的方法了,把同key的多个条件组在一起
* 但是 andOperator() 有个很坑的地方就是,在一个query中只能出现一次
* 如果你有很固定很明确的入参,那还好,直接调用一次 andOperator()
* 如果是多个key需要多个条件,而且条件数量还是动态的,怕不是魔鬼吧...
*/
criteria.andOperator(gte, lte);
复制代码
按照惯例推荐一个软件:wox(解放你的桌面,和上次说的everything一起使用,爽到嗨。可以自己网上搜最新版)
链接:pan.baidu.com/s/1KDXwbQyk… 提取码:48b6