MongoDB简介:
mongodb属于nosql非关系型数据库中的“文档型数据库”。
非关系型数据库还有键值存储数据库(如redis)、列存储数据库(ru:Neo4j)和图形数据库(Cassandra、Hbase)等
mongodb中基本的概念是数据库、集合和文档(对应mysql的数据库,表和行数据),通常以json格式显示,以bson格式(二进制的json)存储
优势:
1、性能好,因为把一部分数据存储在内存中。
2、扩展性好。
3、高可用。支持故障转移
4、json格式,非常适合存储和查询(不像mysql需要修改“表”结构)
劣势:
1、查询方式比较特别,学习成本高
2、没有事务机制,只支持单文档事务。
3、锁只能提供到集合级别,不能提供到文档级别
4、不支持连表查询
使用场景:
可以把一些经常添加或更新的、表结构多变的,不需要复杂查询的,事务要求不高的,数据量较大的表,
从mysql迁移到mongodb中(因为数据量太大查询会很慢),例如:日志,报表、通知、传感器采集的数据等
开始整合:
1、引入pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
2、配置文件添加:
#mongodb
spring.data.mongodb.uri=mongodb://localhost:27017/sumengnan
3、创建实体类用@Document注解
package com.sumengnan.test.domain;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document(collection="mongo")
public class Mongo {
@Id
private Long id;
private String size;
private String color;
private Double price;
//getter,setter……
}
4、在Controller注入MongoTemplate就可以使用了,简单保存和查询如下:
//保存(插入或更新)
Mongo mongo = new Mongo();
mongo.setColor("红色"+i);
mongo.setPrice(123.32d);
mongo.setSize("12");
mongoTemplate.save(mongo,"mongo"); //第二个参数为collname
//查询
Query query = new Query();
query.addCriteria(Criteria.where("color").is("红色"));
List<Mongo> mongo = mongoTemplate.find(query, Mongo.class, "mongo");//第二个参数为collname
System.out.println(mongo);
还有其他查询方法:
1、Criteria.where("user").is("sumengnan").and("age").is(24)//and多条件查询
2、Criteria.where("user").is("sumengnan").orOperator(Criteria.where("age").is(24), Criteria.where("sex").exists(true))//or多条件查询
3、Criteria.where("age").in(Arrays.asList(11, 21, 33))//in查询
4、Criteria.where("age").gt(24)//gt数值比较,其他的还有gte, gt, lt, let
5、Criteria.where("user").regex("^sumengnan")//regex正则
6、mongoTemplate.count()//count查询总数
7、Aggregation.newAggregation(Aggregation.group("user").count().as("userCount"))//group分组查询
8、Criteria.where("user").is("sumengnan")).with(Sort.by("age")//Sort排序
9、Criteria.where("user").is("sumengnan")).with(Sort.by("age")).limit(2)//limit取出多少数据(分页)
10、Criteria.where("user").is("sumengnan")).with(Sort.by("age")).skip(2)//skip跳过多少数据(分页)
5、完毕
问题: mongodb主键没法自动增长?
解决办法:
原理:建一个sequence表(集合)保存序列值,每次插入数据时,先更新序列值(+1),返回当前序列值,设置给要插入数据的id上即可。
步骤:
1、建一个sequence表(集合)
package com.sumengnan.test.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document(collection = "sequence")
public class Sequence {
@Id
private String id;// 主键
private String collName;// 集合名称,为了区分不同集合的序列值
private Long seqId;// 序列值
//getter,setter……
}
2、建一个AutoKey注解
package com.sumengnan.test.domain;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*自动增长id注解类
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoKey {
}
3、添加一个类继承AbstractMongoEventListener,重写onBeforeConvert方法
package com.sumengnan.config;
import com.sumengnan.test.domain.AutoIncKey;
import com.sumengnan.test.domain.Mongo;
import com.sumengnan.test.domain.Sequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
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.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
/**
* 监听添加操作,并将自增id插入
*/
@Component
public class SaveEventListener<T> extends AbstractMongoEventListener<Object> {
private static final Logger logger = LoggerFactory.getLogger(SaveEventListener.class);
@Autowired
private MongoTemplate mongo;
/**
*
* @param event
*/
@Override
public void onBeforeConvert(BeforeConvertEvent<Object> event) {
T source = (T) event.getSource();
if (source != null) {
ReflectionUtils.doWithFields(source.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
// 如果字段添加了我们自定义的AutoIncKey注解
if (field.isAnnotationPresent(AutoIncKey.class)) {
// 设置自增ID
try {
// 判断当前对象的id是否未空,不为空时不自动生成id
Field idField = source.getClass().getDeclaredField(field.getName());//反射获取当前注解的字段,一般是ObjectId
idField.setAccessible(true);//设置允许访问private私有属性
if (idField.get(source)==null){//获取id的值
field.set(source, getNextId(source.getClass().getSimpleName()));
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
});
}
}
private Long getNextId(String collName) {
Query query = new Query(Criteria.where("collName").is(collName));
Update update = new Update();
update.inc("seqId", 1);//把Sequence序列值增加1
FindAndModifyOptions options = new FindAndModifyOptions();
options.upsert(true);
options.returnNew(true);
Sequence seq = mongo.findAndModify(query, update, options, Sequence.class);//原子操作,更新Sequence序列值,并返回改值
return seq.getSeqId();
}
}
4、把AutoKey注解在id要自动增长的实体类上
package com.sumengnan.test.domain;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document(collection="mongo")
public class Mongo {
@AutoKey
@Id
private Long id;
private String size;
private String color;
private Double price;
//getter,setter……
}
5、当使用mongoTemplate.save()方法时,没有id则自动赋值id为自动增长,否则为更新