1.簡(jiǎn)介
Spring Data for MongoDB 作為 Spring 大家族中的一員,為MongoDB定制了類似于關(guān)系型數(shù)據(jù)庫的ORM框架。
與hibernate mybatis 等ORM框架類似,都需要一個(gè)pojo的bean。所不同的是,關(guān)系型數(shù)據(jù)庫對(duì)應(yīng)的是table,而此處對(duì)應(yīng)到MongoDB中的collection。
由于 MongoDB 本身并沒有事務(wù)支持,所以spring 也無法維護(hù)事務(wù)。但是在mongoDB的操作手冊(cè)提供了這樣一個(gè)方式來維護(hù)多文檔操作的事務(wù)。
原文是這樣說的:
Because only single-document operations are atomic with MongoDB, two-phase commits can
only offer transaction-like semantics. It is possible for applications to return intermediate data at
intermediate points during the two-phase commit or rollback.
只有單文檔操作是原子性的,兩階段提交可以提供這樣一個(gè)類似事務(wù)的模式。這樣來為應(yīng)用程序可以返回一個(gè)再兩階段提交或者回滾的中間點(diǎn)。直接略去我的翻譯,大致的意思,就是MongoDB僅僅支持單文檔的原子性操作,但是提供了一個(gè)兩階段方式來維護(hù)事務(wù)。也就是說在多個(gè)文檔操作時(shí),MongoDB提供了一個(gè)兩階段提交模式來維護(hù)事務(wù)。
具體的操作方式,具體參考一下
https://docs./manual/tutorial/perform-two-phase-commits/
對(duì)于這個(gè)MongoDB事務(wù),個(gè)人的觀點(diǎn)是不建議使用事務(wù),因?yàn)樵跀?shù)據(jù)庫選型的時(shí)候,如果希望事務(wù)比較強(qiáng),那么建議選用關(guān)系型數(shù)據(jù)庫來存儲(chǔ)數(shù)據(jù)。對(duì)于MongoDB的應(yīng)用,一般是用來查詢,或者是存儲(chǔ)日志等對(duì)事務(wù)要求比較弱的應(yīng)用場(chǎng)景。
2.spring data MongoDB 整合
環(huán)境:
spring.data.mongodb.1.7.2.RELEASE.jar
mongo-java-driver-2.13.3.jar
配置連接池:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www./schema/beans"
xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns:context="http://www./schema/context"
xmlns:mongo="http://www./schema/data/mongo"
xsi:schemaLocation="http://www./schema/beans
http://www./schema/beans/spring-beans-4.1.xsd
http://www./schema/context
http://www./schema/context/spring-context-4.1.xsd
http://www./schema/data/mongo
http://www./schema/data/mongo/spring-mongo-1.0.xsd">
<!-- 配置mongoTemplate -->
<mongo:mongo host="${mongo.host}" port="${mongo.port}"></mongo:mongo>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongo" />
<constructor-arg name="databaseName" value="${mongo.dbname}" />
</bean>
</beans>
MongoTemplate 是spring data MongoDB 中的的操作類,繼承了MongoOperations 與ApplicationContextAware ,這個(gè)MongoOperations里面封裝了基礎(chǔ)CRUD操作,有興趣直接閱讀以下源碼很快能看懂。
基礎(chǔ)的操作接口
package edu.yingding.core.mongo;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import edu.yingding.core.entity.PagerDTO;
//mongo操作接口
public interface IBaseMongo {
public List selectPage(Map<String, Object> param, Class entityClass, PagerDTO pagerDto) throws Exception;
public List selectPage(String queryStr, Class entityClass, PagerDTO pagerDto) throws Exception;
List find( Query query,Class entityClass);
public List selectPage(Query query,Class entityClass,PagerDTO pagerDto)throws Exception;
/**
* 分組取數(shù)據(jù)
* @param collectionName mongo表名
* @param fieldName
* @param param
* @return
* @throws Exception
*/
public List distinct(String collectionName, String fieldName, Map<String, Object> param) throws Exception;
/**
* 得到一個(gè)實(shí)體
* @param param
* @param entityClass
* @return
* @throws Exception
*/
public Object findOne(Map<String, Object> param, Class entityClass) throws Exception;
/**
* 保存結(jié)果集
* @param entity
* @return
* @throws Exception
*/
public boolean insert(Object entity) throws Exception;
/**
* 按id更新數(shù)據(jù)
* @param id
* @param param
* @return
* @throws Exception
*/
public boolean updateById(String id, Map<String, Object> param, Class entityClass) throws Exception;
List selectPage(String queryStr, Criteria criteria, Class entityClass, PagerDTO pagerDto) throws Exception;
}
package edu.yingding.core.mongo;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
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.Service;
import com.mongodb.WriteResult;
import edu.yingding.core.entity.PagerDTO;
import net.sf.json.JSONObject;
/**
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description:
* @Date: 15:53 2017/1/4
* @Version 1.0.0
*/
@Service
public class BaseMongoImpl implements IBaseMongo {
@Autowired
protected MongoTemplate mongoTemplate;
/**
* @param param 查詢參數(shù)【map類型】
* @param entityClass 反射類
* @param pagerDto 分頁實(shí)體
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 分頁方法【支持map類型參數(shù)】
* @Date: 15:52 2017/1/4
* @Version 1.0.0
*/
@Override
public List selectPage(Map<String, Object> param, Class entityClass, PagerDTO pagerDto) throws Exception {
Query query = parseParams(param);
long total = mongoTemplate.count(query, entityClass);
List results = null;
if (total > 0) {
pagerDto.init(total);
query.skip((pagerDto.getPageNum() - 1) * pagerDto.getPageSize());
query.limit(pagerDto.getPageSize());
if (StringUtils.isNotEmpty(pagerDto.getOrderBy())) {
JSONObject orderObj = JSONObject.fromObject(pagerDto.getOrderBy());
if (orderObj != null) {
Iterator it = orderObj.keys();
while (it.hasNext()) {
String key = (String) it.next();
int value = orderObj.getInt(key);
if (value == 1) {
query.with(new Sort(Direction.ASC, key));
} else {
query.with(new Sort(Direction.DESC, key));
}
}
}
}
System.out.println(query.toString());
results = mongoTemplate.find(query, entityClass);
pagerDto.setResult(results);
} else {
pagerDto.setResult(Collections.emptyList());
}
return results;
}
/**
* @param queryStr 查詢字符串
* @param entityClass 反射類
* @param pagerDto 分頁實(shí)體
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 分頁方法 【支持查詢字符串】
* @Date: 15:54 2017/1/4
* @Version 1.0.0
*/
@Override
public List selectPage(String queryStr, Class entityClass, PagerDTO pagerDto) throws Exception {
if (queryStr == null) {
queryStr = "";
}
BasicQuery query = new BasicQuery(queryStr);
long total = mongoTemplate.count(query, entityClass);
List results = null;
if (total > 0) {
pagerDto.init(total);
query.skip((pagerDto.getPageNum() - 1) * pagerDto.getPageSize());
query.limit(pagerDto.getPageSize());
if (StringUtils.isNotEmpty(pagerDto.getOrderBy())) {
JSONObject orderObj = JSONObject.fromObject(pagerDto.getOrderBy());
if (orderObj != null) {
Iterator it = orderObj.keys();
while (it.hasNext()) {
String key = (String) it.next();
int value = orderObj.getInt(key);
if (value == 1) {
query.with(new Sort(Direction.ASC, key));
} else {
query.with(new Sort(Direction.DESC, key));
}
}
}
}
results = mongoTemplate.find(query, entityClass);
pagerDto.setResult(results);
} else {
pagerDto.setResult(Collections.emptyList());
}
return results;
}
/**
* @param queryStr
* @param criteria
* @param entityClass
* @param pagerDto
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 分頁方法 【支持查詢字符串與Criteria對(duì)象】
* @Param:
* @Date: 15:55 2017/1/4
* @Version 1.0.0
*/
@Override
public List selectPage(String queryStr, Criteria criteria, Class entityClass, PagerDTO pagerDto) throws Exception {
if (queryStr == null) {
queryStr = "";
}
BasicQuery query = new BasicQuery(queryStr);
query.addCriteria(criteria);
long total = mongoTemplate.count(query, entityClass);
List results = null;
if (total > 0) {
pagerDto.init(total);
query.skip((pagerDto.getPageNum() - 1) * pagerDto.getPageSize());
query.limit(pagerDto.getPageSize());
if (StringUtils.isNotEmpty(pagerDto.getOrderBy())) {
JSONObject orderObj = JSONObject.fromObject(pagerDto.getOrderBy());
if (orderObj != null) {
Iterator it = orderObj.keys();
while (it.hasNext()) {
String key = (String) it.next();
int value = orderObj.getInt(key);
if (value == 1) {
query.with(new Sort(Direction.ASC, key));
} else {
query.with(new Sort(Direction.DESC, key));
}
}
}
}
results = mongoTemplate.find(query, entityClass);
pagerDto.setResult(results);
} else {
pagerDto.setResult(Collections.emptyList());
}
return results;
}
/**
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 查詢實(shí)體list
* @Param: query 查詢對(duì)象
* @Param: entityClass 反射類
* @Date: 15:57 2017/1/4
* @Version 1.0.0
*/
@Override
public List find(Query query, Class entityClass) {
return mongoTemplate.find(query, entityClass);
}
/**
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 查詢分頁 【支持query對(duì)象】
* @Param: * @param query 查詢對(duì)象
* @Param: * @param entityClass 反射類
* @Param: * @param pagerDto 分頁實(shí)體
* @Date: 15:58 2017/1/4
* @Version 1.0.0
*/
@Override
public List selectPage(Query query, Class entityClass, PagerDTO pagerDto) throws Exception {
List results = null;
if (query != null) {
long count = mongoTemplate.count(query, entityClass);
if (count > 0) {
pagerDto.init(count);
query.skip((pagerDto.getPageNum() - 1) * pagerDto.getPageSize());
query.limit(pagerDto.getPageSize());
if (StringUtils.isNotEmpty(pagerDto.getOrderBy())) {
JSONObject orderObj = JSONObject.fromObject(pagerDto.getOrderBy());
if (orderObj != null) {
Iterator it = orderObj.keys();
while (it.hasNext()) {
String key = (String) it.next();
int value = orderObj.getInt(key);
if (value == 1) {
query.with(new Sort(Direction.ASC, key));
} else {
query.with(new Sort(Direction.DESC, key));
}
}
}
}
System.out.println(query.toString());
results = mongoTemplate.find(query, entityClass);
pagerDto.setResult(results);
} else {
pagerDto.setResult(Collections.emptyList());
}
} else {
pagerDto.setResult(Collections.EMPTY_LIST);
}
return results;
}
/**
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 查詢?nèi)ブ貙?shí)體集合
* @Param: * @param collectionName 集合名稱
* @Param: * @param fieldName 去重字段名
* @Param: * @param param 查詢條件
* @Date: 15:59 2017/1/4
* @Version 1.0.0
*/
@Override
public List distinct(String collectionName, String fieldName, Map<String, Object> param) throws Exception {
if (StringUtils.isEmpty(collectionName) || StringUtils.isEmpty(fieldName)) {
return Collections.emptyList();
}
Query query = parseParams(param);
return mongoTemplate.getCollection(collectionName).distinct(fieldName, query.getQueryObject());
}
/**
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 格式化查詢條件
* @Param: * @param param 查詢條件
* @Date: 16:00 2017/1/4
* @Version 1.0.0
*/
private Query parseParams(Map<String, Object> param) {
Query query = new Query();
if (param != null && !param.isEmpty()) {
Iterator<Map.Entry<String, Object>> it = param.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
String key = entry.getKey();
if (StringUtils.isEmpty(key)) {
continue;
}
if (key.endsWith("Ignore")) {
continue;
}
if (key.startsWith("$")) {
} else if (key.endsWith("Contains")) {
query.addCriteria(Criteria.where(key.replaceFirst("Contains", "")).regex((String) entry.getValue()));
} else {
query.addCriteria(Criteria.where(key).is(entry.getValue()));
}
}
}
return query;
}
@Override
public Object findOne(Map<String, Object> param, Class entityClass) throws Exception {
Query query = parseParams(param);
return mongoTemplate.findOne(query, entityClass);
}
@Override
public boolean insert(Object entity) throws Exception {
try {
mongoTemplate.insert(entity);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
public boolean updateById(String id, Map<String, Object> param, Class entityClass) throws Exception {
Query query = new Query();
query.addCriteria(Criteria.where("id").is(id));
Update update = parseUpdate(param);
WriteResult result = mongoTemplate.updateFirst(query, update, entityClass);
return result.getN() > 0 ? true : false;
}
private Update parseUpdate(Map<String, Object> param) {
Update update = new Update();
if (param != null && !param.isEmpty()) {
Iterator<Map.Entry<String, Object>> it = param.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
String key = entry.getKey();
if (StringUtils.isEmpty(key)) {
continue;
}
if (key.endsWith("Ignore")) {
continue;
}
if (key.startsWith("$")) {
} else {
update.set(key, entry.getValue());
}
}
}
return update;
}
}
分頁實(shí)體
package edu.yingding.core.entity;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
/**
* @Author:chenfanglin 【chenfanglincfl@163.com】
* @Description: 分頁實(shí)體
* @Param: * @param null
* @Date: 16:01 2017/1/4
* @Version 1.0.0
*/
public class PagerDTO {
//當(dāng)前第幾頁
private int pageNum=1;
//總共多少頁
private long pageCount;
//每頁顯示幾條數(shù)據(jù)
private int pageSize = 10;
//總共多少條
private long total;
/* 排序方式 */
private String orderBy;
/* 查詢字符串 */
private String queryStr;
/* 表單防止重復(fù)提交碼 */
private String formToken;
/* 必須有參數(shù)才能做分頁查詢 */
private boolean argsCanSearch;
/* 如果為真,并且pageCount > 0 ,則在數(shù)據(jù)庫中不進(jìn)行查詢 */
private boolean userPageCount;
//分頁返回的數(shù)據(jù)
private Object result;
public boolean isUserPageCount() {
return userPageCount;
}
public void setUserPageCount(boolean userPageCount) {
this.userPageCount = userPageCount;
}
public boolean isArgsCanSearch() {
return argsCanSearch;
}
public void setArgsCanSearch(boolean argsCanSearch) {
this.argsCanSearch = argsCanSearch;
}
public String getFormToken() {
return formToken;
}
public void setFormToken(String formToken) {
this.formToken = formToken;
}
public void init(long total) {
this.setTotal(total);
boolean flag = (total%this.getPageSize() == 0) ? false : true;
long iPageSize = flag ? (total/this.getPageSize()+1) : (total/this.getPageSize());
if(this.getPageNum() > iPageSize) {
this.setPageNum(1);
}
this.setPageCount(iPageSize);
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public long getPageCount() {
return pageCount;
}
public void setPageCount(long pageCount) {
this.pageCount = pageCount;
}
public int getPageSize() {
return pageSize;
}
public void setPerPage(int pageSize) {
if(pageSize > 100) {
this.pageSize = 100;
} else {
this.pageSize = pageSize;
}
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public String getOrderBy() {
return orderBy;
}
public void setOrderBy(String orderBy) {
this.orderBy = orderBy;
}
public String getQueryStr() {
return queryStr;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
public String getEncodeQueryStr() {
if(queryStr != null && !"".equals(queryStr)) {
try {
return URLEncoder.encode(queryStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return "";
}
public String getDecodeQueryStr() {
if(queryStr != null && !"".equals(queryStr)) {
try {
return URLDecoder.decode(queryStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return "";
}
public void setQueryStr(String queryStr) {
this.queryStr = queryStr;
}
public String getCacheString() {
StringBuffer cacheKey = new StringBuffer();
cacheKey.append(this.getPageNum()).append("#");
cacheKey.append(this.getPageSize()).append("#");
if(StringUtils.isNotEmpty(this.getOrderBy())) {
cacheKey.append(this.getOrderBy()).append("#");
}
List<String> args = new ArrayList<String>();
setCacheList(args);
if(args != null && !args.isEmpty()) {
for(String arg : args) {
if(StringUtils.isNotEmpty(arg)) {
cacheKey.append(arg).append("#");
}
}
}
return cacheKey.toString();
}
/**
* 設(shè)置緩存KEY
* @param cacheKeys
*/
public void setCacheList(List<String> args) {
}
}
總結(jié)
總的來說,Spring Data MongoDB 整體架構(gòu)方式還是類似于hibernate mybatis,只是相應(yīng)的會(huì)有一些概念的變動(dòng)。就操作性來說,對(duì)于開發(fā)人員只是熟悉相關(guān)API以及相關(guān)的概念,觸類旁通。使用來說還是很方便的。
擴(kuò)展:
諸如類似Spring Data MongoDB ORM 框架還有諸如 morphia
https://github.com/mongodb/morphia。
|