mongodb作为文档型数据库,是最像关系型数据库的。
1,pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- 引入mongodb starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2,application.properties
# MongoDB
spring.data.mongodb.uri=mongodb://localhost:27017/test
#logging.level.com.example.demo=debug
logging.level.org.springframework.data.mongodb.core.MongoTemplate=debug
3,实体
import org.springframework.data.annotation.Id;
//指明集合名
@Document(collection="user")
public class User{
//指定id
@Id
private String id;
private String password;
private String name;
private String gender;
private Date createTime;
//get/set
}
@Document(collection="order")
public class Order {
@Id
private String id;
private String shopName;
//关联user集合
@DBRef
private User user;
//get/set
}
public class GroupResult {
private Integer count;
private String groupName;
//get/set
}
4,工具类
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* 发射可以说是java提供给程序员的作弊器了,
* 利用反射可以创建并调用bean的私有方法,
* 但这样就破坏了java的封装性,而且反射是 比较耗资源的
* 使用要谨慎
* @author 25395
*
*/
public class ReflectUtil {
/**
* 将实体中有值的属性放到map中
* @param object
* @return
*/
public static Map<String,Object> entryToMap(Object object){
Map<String,Object> map=new HashMap<>();
try {
Field[] fields = object.getClass().getDeclaredFields();
Object result=null;
for(Field field: fields) {
field.setAccessible(true);
result = field.get(object);
if(result!=null) {
map.put(field.getName(), result);
}
}
}catch (Exception e) {
e.printStackTrace();
}
return map;
}
}
5,工具方法抽取
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.aggregation.UnwindOperation;
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.util.StringUtils;
import com.example.demo.bean.GroupResult;
import com.example.demo.util.ReflectUtil;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
public class MongodbServiceImpl {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 带条件分组
* @param criteria
* @param filed
* @param collectionName
* @return
*/
protected List<GroupResult> groupBy(Criteria criteria, String filed,String collectionName) {
MatchOperation match = Aggregation.match(criteria);
GroupOperation groupOperation = Aggregation.group(filed).count()
.as("count");
ProjectionOperation project = Aggregation.project("count").and("_id")
.as("groupName");
Aggregation agg = Aggregation.newAggregation(match,groupOperation,project);
AggregationResults<GroupResult> results = mongoTemplate
.aggregate(agg, collectionName, GroupResult.class);
List<GroupResult> mappedResults = results.getMappedResults();
return mappedResults;
}
/**
* 根据传入的字段,对集合进行分组
* @param filed:待分组的字段
* @param collectionName:集合名
* @return:[{_id=女, count=2, groupName=女}]
*/
protected List<GroupResult> groupBy(String filed,String collectionName) {
GroupOperation groupOperation = Aggregation.group(filed).count()
.as("count");
ProjectionOperation project = Aggregation.project("count").and("_id")
.as("groupName");
Aggregation agg = Aggregation.newAggregation(groupOperation,project);
AggregationResults<GroupResult> results = mongoTemplate
.aggregate(agg, collectionName, GroupResult.class);
List<GroupResult> mappedResults = results.getMappedResults();
return mappedResults;
}
/**
* 内嵌集合分组查询
* @param filed 待分组字段,即要分组的字段 如:products.category
* @param collectionName 集合名
* @return
*/
protected List<GroupResult> innerGroupBy(String filed,String collectionName) {
String[] fileds = filed.split("\\.");
UnwindOperation unwind = Aggregation.unwind(fileds[0]);
GroupOperation groupOperation = Aggregation.group(filed).count()
.as("count");
ProjectionOperation project = Aggregation.project("count").and("_id")
.as("groupName");
Aggregation agg = Aggregation.newAggregation(unwind,groupOperation,project);
AggregationResults<GroupResult> results = mongoTemplate
.aggregate(agg, collectionName, GroupResult.class);
List<GroupResult> mappedResults = results.getMappedResults();
return mappedResults;
}
/**
* 根据传入实体,利用反射进行查询
* @param object
* @param page
* @return
*/
protected Page<?> findAll(Object object, Pageable page) {
Criteria criatira=new Criteria();
try {
Field[] fields = object.getClass().getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
Object val = field.get(object);
if(val != null) {
String key = field.getName();
criatira.and(key).is(val);
}
}
}catch (Exception e) {
e.printStackTrace();
}
return findAll(criatira, page,object.getClass());
}
/**
* 根据传入的查询条件,进行查询
* @param criatira
* @param page
* @param entityClass
* @return
*/
protected <K> Page<K> findAll(Criteria criatira, Pageable page, Class<K> entityClass) {
Query query = new Query(criatira);
long totalCount = this.mongoTemplate.count(query, entityClass);//查询数量
Page<K> pages;
List<K> list;
if (totalCount > 0) {
list = mongoTemplate.find(query.with(page), entityClass);
pages = new PageImpl<>(list, page, totalCount);
} else {
pages = new PageImpl<>(Collections.emptyList());
}
return pages;
}
/**
* 查询所有满足条件的记录,不分页
* @param criatira
* @param entityClass
* @return
*/
protected <K> List<K> findAll(Criteria criatira, Class<K> entityClass) {
Query query = new Query(criatira);
List<K> list = mongoTemplate.find(query, entityClass);
return list;
}
/**
* 根据id进行有选择地更新,如果entry属性值为空,则将该值设置为空(当只有一列时,会删除该列)
* @param id
* @param map
* @param entityClass
*/
protected long updateById(Object id, Map<String, Object> map, Class<?> entityClass) {
Update update = new Update();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (null == entry.getValue()) {
update.unset(entry.getKey());
} else {
update.set(entry.getKey(), entry.getValue());
}
}
UpdateResult updateMulti = mongoTemplate.updateMulti(new Query(Criteria.where("_id").is(id)), update, entityClass);
return updateMulti.getModifiedCount();
}
/**
* 指定更新對象的id 的名,根據傳入對象有選擇地更新
* @param object :更新的對象
* @param idName : id名
*/
protected long updateByEntry(Object object,String idName) {
Map<String, Object> map = ReflectUtil.entryToMap(object);
if(!StringUtils.hasText(idName)) {
idName="id";
}
Object idVal = map.get(idName);
map.remove(idName);
return updateById(idVal, map, object.getClass());
}
/**
* 根據傳入對象有選擇地更新(主键的key 默认为:id)
* @param object :更新的對象
*/
protected long updateByEntry(Object object) {
return updateByEntry(object,null);
}
/**
* 根据传入的ids 对指定集合进行删除
* @param ids
* @param entityClass
* @param idName
* @return 删除的条数
*/
protected long remove(List<Object> ids,Class<?> entityClass,String idName) {
if(!StringUtils.hasText(idName)) {
idName="id";
}
Criteria criatira=Criteria.where(idName).in(ids);
Query query = new Query(criatira);
DeleteResult result = mongoTemplate.remove(query, entityClass);
return result.getDeletedCount();
}
/**
* 根据传入的ids 对指定集合进行删除,注解默认为:id
* @param ids
* @param entityClass
* @return 删除的条数
*/
protected long remove(List<Object> ids,Class<?> entityClass) {
return remove(ids, entityClass, null);
}
/**
* 保存单个实体
* @param objectToSave
*/
protected void insert(Object objectToSave) {
mongoTemplate.insert(objectToSave);
}
/**
* 保存实体集合
* @param objectsToSave
*/
protected void insertAll(Collection<? extends Object> objectsToSave) {
mongoTemplate.insertAll(objectsToSave);
}
}
6,测试类
import java.util.List;
import java.util.Map;
import org.springframework.data.domain.Page;
import com.example.demo.bean.User;
public interface UserService {
void update(User user);
Page<User> query(User user,Page<User> page) ;
List<Map> groupBy(String filed,String collectionName);
void remove(List<Object> ids);
}
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import com.example.demo.bean.User;
import com.example.demo.service.UserService;
@Service
public class UserServiceImpl extends MongodbServiceImpl implements UserService{
@Override
public void update(User user) {
super.updateByEntry(user);
}
@Override
public Page<User> query(User user,Page<User> page) {
// Page<?> pages = super.findAll(user, PageRequest.of(0, 2,Sort.by(Direction.DESC,"createTime","gender")));
List<Order> orders=Arrays.asList(new Order(Direction.DESC,"createTime"),new Order(Direction.ASC,"gender"));
Page<?> pages = super.findAll(user, PageRequest.of(0, 2,Sort.by(orders)));
return (Page<User>) pages;
}
@Override
public List<Map> groupBy(String filed,String collectionName){
return super.groupBy(filed, collectionName);
}
@Override
public void remove(List<Object> ids) {
super.remove(ids, User.class);
}
}
7,管道方法
①,unwind的用法
②,其余管道方法作用
8,mongodb的坑
切记,
①,mongodb不支持事务;
---这个真没办法了呀,能想到的办件就是手动恢复了,将有更新的原始记录保存起来,如果发生异常就是将原来的数据设置回去。
②,如果常对mongdb的一行的集合进行修改,切记用外键关联的方式,否则会造成锁表很严重
更多注意事项,请参考:
https://www.cnblogs.com/l1pe1/p/7871859.html
9,亲测mongodb事务支持情况
一旦异常抛出,下面方法就不会执行,也没有回滚
@Transactional
@Override
public void transact() {
User user=new User();
user.setId("5b07fd0d75337d081c64c5e8");
user.setName("红米1");
super.updateByEntry(user);
int i=9/0;
System.out.println("====================");
user=new User();
user.setId("5b08bbef75337d0a58748083");
user.setName("刘亦菲");
super.updateByEntry(user);
}
@Transactional
@Override
public void repositoryTransact() {
User user=new User();
user.setName("红米NOTE4X");
userRepsoitory.save(user);
int i=9/0;
System.out.println("====================");
user=new User();
user.setName("冬马");
userRepsoitory.save(user);
}
10,批量更新
db.employee.find().forEach(
function(item){
db.employee.update({_id:item._id},{$set:{email:'ai@qq.com'}})
}
)
11,查询指定字段
BasicDBObject fieldsObject=new BasicDBObject();
fieldsObject.put("id","1");
String queryJson="{\"_id\":\"5b36f44be664da37b4fafe9f\"}";
String fieldJson="{\"_id\":\"1\",\"orderNo\":\"1\"}";
BasicQuery query = new BasicQuery(queryJson,fieldJson);
query.addCriteria(Criteria.where("id").in(orderRecordMap.keySet()));
List<B2bOrder> orderList = mongoTemplate.find(query, B2bOrder.class);
12,java代码方式配置mongo
①,SpringBoot 没有提供 mongoxxxCustomizer,但springdata提供了一个AbstractMongoConfiguration
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import com.mongodb.MongoClient;
@Configuration
public class MongoConfig extends AbstractMongoConfiguration{
@Override
public MongoClient mongoClient() {
return new MongoClient("127.0.0.1",27017);
}
@Override
protected String getDatabaseName() {
return "test";
}
}