Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.teasoft.beex.mongodb.MongodbSqlLib Maven / Gradle / Ivy
/*
* Copyright 2016-2023 the original author.All rights reserved.
* Kingstar([email protected] )
* The license,see the LICENSE file.
*/
package org.teasoft.beex.mongodb;
import static com.mongodb.client.model.Accumulators.avg;
import static com.mongodb.client.model.Accumulators.max;
import static com.mongodb.client.model.Accumulators.min;
import static com.mongodb.client.model.Accumulators.sum;
import static com.mongodb.client.model.Aggregates.group;
import static com.mongodb.client.model.Projections.excludeId;
import static com.mongodb.client.model.Projections.fields;
import static com.mongodb.client.model.Projections.include;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.teasoft.bee.mongodb.MongoSqlStruct;
import org.teasoft.bee.mongodb.MongodbBeeSql;
import org.teasoft.bee.osql.Cache;
import org.teasoft.bee.osql.Condition;
import org.teasoft.bee.osql.FunctionType;
import org.teasoft.bee.osql.IncludeType;
import org.teasoft.bee.osql.ObjSQLException;
import org.teasoft.bee.osql.OrderType;
import org.teasoft.bee.osql.SuidType;
import org.teasoft.bee.osql.exception.BeeIllegalBusinessException;
import org.teasoft.beex.mongodb.ds.SingleMongodbFactory;
import org.teasoft.honey.database.DatabaseClientConnection;
import org.teasoft.honey.osql.core.AbstractBase;
import org.teasoft.honey.osql.core.BeeFactory;
import org.teasoft.honey.osql.core.BeeFactoryHelper;
import org.teasoft.honey.osql.core.ConditionImpl;
import org.teasoft.honey.osql.core.ConditionImpl.FunExpress;
import org.teasoft.honey.osql.core.ExceptionHelper;
import org.teasoft.honey.osql.core.HoneyConfig;
import org.teasoft.honey.osql.core.HoneyContext;
import org.teasoft.honey.osql.core.HoneyUtil;
import org.teasoft.honey.osql.core.JsonResultWrap;
import org.teasoft.honey.osql.core.Logger;
import org.teasoft.honey.osql.core.NameTranslateHandle;
import org.teasoft.honey.osql.core.StringConst;
import org.teasoft.honey.osql.mongodb.MongoConditionHelper;
import org.teasoft.honey.osql.name.NameUtil;
import org.teasoft.honey.sharding.ShardingReg;
import org.teasoft.honey.sharding.ShardingUtil;
import org.teasoft.honey.sharding.engine.mongodb.MongodbShardingDdlEngine;
import org.teasoft.honey.sharding.engine.mongodb.MongodbShardingSelectEngine;
import org.teasoft.honey.sharding.engine.mongodb.MongodbShardingSelectFunEngine;
import org.teasoft.honey.sharding.engine.mongodb.MongodbShardingSelectJsonEngine;
import org.teasoft.honey.sharding.engine.mongodb.MongodbShardingSelectListStringArrayEngine;
import org.teasoft.honey.util.ObjectUtils;
import org.teasoft.honey.util.StringUtils;
import com.mongodb.MongoTimeoutException;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.InsertManyResult;
import com.mongodb.client.result.UpdateResult;
/**
* @author Jade
* @author Kingstar
* @since 2.0
*/
public class MongodbSqlLib extends AbstractBase implements MongodbBeeSql, Serializable {
private static final long serialVersionUID = 1596710362261L;
private static final String IDKEY = "_id";
private static String _toTableName(Object entity) {
return NameTranslateHandle.toTableName(NameUtil.getClassFullName(entity));
}
private DatabaseClientConnection getConn() {
if (!HoneyConfig.getHoneyConfig().multiDS_enable) {
return null;
} else {
DatabaseClientConnection db = HoneyContext.getDatabaseConnection();
return db;
}
}
private MongoDatabase getMongoDatabase(DatabaseClientConnection conn) {
if(conn==null) {
return SingleMongodbFactory.getMongoDb(); //单个数据源时,
}
return (MongoDatabase) conn.getDbConnection();
}
private boolean isShardingMain() {//有分片(多个)
return HoneyContext.getSqlIndexLocal() == null && ShardingUtil.hadSharding(); //前提要是HoneyContext.hadSharding()
}
@Override
public List select(T entity) {
return select(entity, null);
}
@SuppressWarnings("unchecked")
private Class toClassT(T entity) {
return (Class)entity.getClass();
}
@Override
public int update(T entity) {
String tableName = _toTableName(entity);
Document doc = null;
DatabaseClientConnection conn = null;
int num = 0;
String sql = "";
try {
String pkName = HoneyUtil.getPkFieldName(entity);
if ("".equals(pkName)) pkName = "id";
String pks[] = pkName.split(",");
if (pks.length < 1) throw new ObjSQLException(
"ObjSQLException: in the update(T entity) or update(T entity,IncludeType includeType), the id field is missing !");
Map map = ParaConvertUtil.toMap(entity);
Document filter = new Document();
String column = "";
for (int i = 0; i < pks.length; i++) {
column = pks[i];
if ("id".equalsIgnoreCase(column)) {// 替换id为_id
column = IDKEY;
}
filter.append(column, map.get(column));
map.remove(column);
}
doc = new Document(map);
Document updateDocument = new Document("$set", doc);
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, filter, null, null,
null, null, false, entity.getClass(), updateDocument);
sql = struct.getSql();
HoneyContext.addInContextForCache(sql, struct.getTableName());
logSQLForMain(" Mongodb::update: "+sql);
conn = getConn();
UpdateResult rs = getMongoDatabase(conn).getCollection(tableName).updateMany(filter,
updateDocument);
return num = (int) rs.getModifiedCount();
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
logAffectRow(num);
clearInCache(sql, "int", SuidType.MODIFY, num);
close(conn);
}
}
@Override
public int insert(T entity) {
String tableName = _toTableName(entity);
Document doc = null;
DatabaseClientConnection conn =null;
String sql="";
int num = 0;
try {
Map map = ParaConvertUtil.toMap(entity);
doc = new Document(map);
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, null, null, null,
null, null, false,entity.getClass(),doc); //insert 放在updateSet
sql=struct.getSql();
logSQLForMain(" Mongodb::insert: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
conn = getConn();
getMongoDatabase(conn).getCollection(tableName).insertOne(doc);
num=1; //有异常不会执行到这
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
boolean notCatch=HoneyConfig.getHoneyConfig().notCatchModifyDuplicateException;
if (!notCatch && isConstraint(e)) { //内部捕获并且是重复异常,则由Bee框架处理
boolean notShow=HoneyConfig.getHoneyConfig().notShowModifyDuplicateException;
if(! notShow) Logger.warn(e.getMessage());
return num;
}
throw ExceptionHelper.convert(e);
} finally {
logAffectRow(num);
clearInCache(sql, "int", SuidType.MODIFY, num);
close(conn);
}
return num;
}
@Override
public int delete(T entity) {
return delete(entity, null);
}
@Override
public List select(T entity, Condition condition) {
if (condition != null && Boolean.TRUE.equals(condition.hasGroupBy())) {
return selectWithGroupBy(entity, condition);
} else {
if (entity == null) return Collections.emptyList();
MongoSqlStruct struct = parseMongoSqlStruct(entity, condition, "List");
Class entityClass = toClassT(entity);
return select(struct, entityClass);
}
}
@Override
public List selectById(Class entityClass, Object id) {
String tableName = _toTableNameByClass(entityClass);
Object[] obj = processId(entityClass, id);
Document one = (Document) obj[0];
Bson moreFilter = (Bson) obj[1];
Bson filter = null;
if (moreFilter != null)
filter = moreFilter;
else
filter = one;
MongoSqlStruct struct = new MongoSqlStruct("List", tableName, filter, null, null,
null, null, true,entityClass);
return select(struct, entityClass);
}
@Override
public List selectOrderBy(T entity, String orderFields, OrderType[] orderTypes) {
String tableName = _toTableName(entity);
Bson filter = toDocument(entity);
Bson sortBson = ParaConvertUtil.toSortBson(orderFields.split(","), orderTypes);
Class entityClass = toClassT(entity);
MongoSqlStruct struct = new MongoSqlStruct("List", tableName, filter, sortBson, null,
null, null, true,entityClass);
return select(struct, entityClass);
}
//用于判断单源和分片的, selectById也可以用
@Override
public List select(MongoSqlStruct struct, Class entityClass) {
if (!ShardingUtil.hadSharding()) {
return _select(struct, entityClass); // 不用分片走的分支
} else {
if (HoneyContext.getSqlIndexLocal() == null) { //分片,主线程
List tabNameList = HoneyContext.getListLocal(StringConst.TabNameListLocal);
struct.setTableName(struct.getTableName().replace(StringConst.ShardingTableIndexStr, tabNameList==null?"":tabNameList.toString()));
List list =_select(struct, entityClass); //检测缓存的
if (list != null) {// 若缓存是null,就无法区分了,所以没有数据,最好是返回空List,而不是null
logDsTab();
return list;
}
ShardingReg.regShadingPage("", "", struct.getStart(), struct.getSize());
List rsList = new MongodbShardingSelectEngine().asynProcess(entityClass, this, struct);
addInCache(struct.getSql(), rsList, rsList.size());
logSelectRows(rsList.size());
return rsList;
} else { // 子线程执行
return _select(struct, entityClass);
}
}
}
// 单表查,一次只涉及一张表
@SuppressWarnings("unchecked")
private List _select(MongoSqlStruct struct, Class entityClass) {
String sql = struct.getSql();
logSQLForMain(" Mongodb::select: " + sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
// boolean isReg //不需要添加returnType判断,因MongoSqlStruct已有returnType
// initRoute(SuidType.SELECT, entityClass, sql);
Object cacheObj = getCache().get(sql);
if (cacheObj != null) {
clearContext(sql);
List list = (List) cacheObj;
logSelectRows(list.size());
return list;
}
if (isShardingMain()) return null; // sharding时,主线程没有缓存就返回.
List rsList = null;
try {
FindIterable docIterable = findIterableDocument(struct);
rsList = TransformResult.toListEntity(docIterable, entityClass);
addInCache(sql, rsList, rsList.size());
logSelectRows(rsList.size());
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
}
return rsList;
}
@Override
public String selectJson(T entity, Condition condition) {
if (entity == null) return null;
MongoSqlStruct struct = parseMongoSqlStruct(entity, condition, "StringJson");
Class entityClass = toClassT(entity);
return selectJson(struct, entityClass);
}
@Override
public String selectJson(MongoSqlStruct struct, Class entityClass) {
String sql = struct.getSql();
if (!ShardingUtil.hadSharding()) { // 无分片
return _selectJson(struct, entityClass);
} else { // 有分片
if (HoneyContext.getSqlIndexLocal() == null) { // 有分片的主线程
String cacheValue = _selectJson(struct, entityClass); // 检测缓存的
if (cacheValue != null) {
logDsTab();
return cacheValue;
}
ShardingReg.regShadingPage("", "", struct.getStart(), struct.getSize());
JsonResultWrap wrap = new MongodbShardingSelectJsonEngine().asynProcess(entityClass, this, struct); // 应该还要传suid类型
logSelectRows(wrap.getRowCount());
String json = wrap.getResultJson();
addInCache(sql, json, -1); // 没有作最大结果集判断
return json;
} else { // 子线程执行
return _selectJson(struct, entityClass);
}
}
}
public String _selectJson(MongoSqlStruct struct, Class entityClass) {
String sql = struct.getSql();
logSQLForMain(" Mongodb::selectJson: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
initRoute(SuidType.SELECT, entityClass, sql);
Object cacheObj = getCache().get(sql); // 这里的sql还没带有值
if (cacheObj != null) {
clearContext(sql);
return (String) cacheObj;
}
if (isShardingMain()) return null; // sharding时,主线程没有缓存就返回.
String json = "";
try {
FindIterable docIterable = findIterableDocument(struct);
JsonResultWrap wrap = TransformResult.toJson(docIterable.iterator(), entityClass);
json = wrap.getResultJson();
logSelectRows(wrap.getRowCount());
addInCache(sql, json, -1); // 没有作最大结果集判断
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
}
return json;
}
@Override
public List selectString(T entity, Condition condition) {
if (entity == null) return Collections.emptyList();
MongoSqlStruct struct = parseMongoSqlStruct(entity, condition, "List");
Class entityClass = toClassT(entity);
return selectString(struct, entityClass);
}
@Override
public List selectString(MongoSqlStruct struct, Class entityClass) {
String sql = struct.getSql();
if (!ShardingUtil.hadSharding()) {
return _selectString(struct, entityClass); // 不用分片走的分支
} else {
if (HoneyContext.getSqlIndexLocal() == null) {
List list = _selectString(struct, entityClass); // 检测缓存的
if (list != null) {
logDsTab();
return list;
}
ShardingReg.regShadingPage("", "", struct.getStart(), struct.getSize());
List rsList = new MongodbShardingSelectListStringArrayEngine().asynProcess(entityClass, this, struct);
addInCache(sql, rsList, rsList.size());
return rsList;
} else { // 子线程执行
return _selectString(struct, entityClass);
}
}
}
@SuppressWarnings("unchecked")
private List _selectString(MongoSqlStruct struct, Class entityClass) {
String sql = struct.getSql();
logSQLForMain(" Mongodb::selectString: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
initRoute(SuidType.SELECT, entityClass, sql);
Object cacheObj = getCache().get(sql); // 这里的sql还没带有值
if (cacheObj != null) {
clearContext(sql);
List list = (List) cacheObj;
logSelectRows(list.size());
return list;
}
if (isShardingMain()) return null; // sharding时,主线程没有缓存就返回.
List list = null;
try {
FindIterable docIterable = findIterableDocument(struct);
list = TransformResult.toListString(docIterable.iterator(), struct.getSelectFields());
logSelectRows(list.size());
addInCache(sql, list, list.size());
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
}
return list;
}
private MongoSqlStruct parseMongoSqlStruct(T entity, Condition condition, String returnType) {
if (condition != null) condition.setSuidType(SuidType.SELECT);
String tableName = _toTableName(entity);
Document filter = toDocument(entity, condition);
Bson sortBson = ParaConvertUtil.toSortBson(condition);
Integer size = null;
Integer start = null;
String[] selectFields = null;
boolean hasId = false;
ConditionImpl conditionImpl = (ConditionImpl) condition;
if (condition != null) {
size = conditionImpl.getSize();
start = conditionImpl.getStart();
selectFields = conditionImpl.getSelectField();
if (selectFields != null) {
if (selectFields.length == 1) selectFields = selectFields[0].split(",");
for (int i = 0; i < selectFields.length; i++) {
if ("id".equalsIgnoreCase(selectFields[i])) {
selectFields[i] = IDKEY;
hasId = true;
break;
}
}
}
}
return new MongoSqlStruct(returnType, tableName, filter, sortBson, start, size, selectFields, hasId,entity.getClass());
}
private FindIterable findIterableDocument(MongoSqlStruct struct) {
String tableName = struct.getTableName();
Bson filter = (Bson)struct.getFilter();
Bson sortBson = (Bson)struct.getSortBson();
Integer size = struct.getSize();
Integer start = struct.getStart();
String[] selectFields = struct.getSelectFields();
boolean hasId = struct.isHasId();
DatabaseClientConnection conn = getConn();
FindIterable docIterable = null;
try {
if (filter != null)
docIterable = getMongoDatabase(conn).getCollection(tableName).find(filter);
else
docIterable = getMongoDatabase(conn).getCollection(tableName).find();
if (sortBson != null) docIterable = docIterable.sort(sortBson); //And Filter{filters=[Document{{_id=1}}, Document{{userid=1}}]}
if (size != null && size > 0) {
if (start == null || start < 0) start = 0;
docIterable = docIterable.skip(start).limit(size);
}
if (selectFields != null) {
if (hasId)
docIterable = docIterable.projection(fields(include(selectFields)));
else
docIterable = docIterable
.projection(fields(include(selectFields), excludeId()));
}
} finally {
close(conn);
}
return docIterable;
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public int delete(T entity, Condition condition) {
String tableName = _toTableName(entity);
Document filter = toDocument(entity, condition);
int num = 0;
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, filter, null, null,
null, null, false,entity.getClass());
String sql=struct.getSql();
logSQLForMain(" Mongodb::delete: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
DatabaseClientConnection conn = getConn();
try {
DeleteResult rs = null;
if (filter != null) {
rs = getMongoDatabase(conn).getCollection(tableName).deleteMany(filter);
}else {
boolean notDeleteWholeRecords = HoneyConfig.getHoneyConfig().notDeleteWholeRecords;
if (notDeleteWholeRecords) {
throw new BeeIllegalBusinessException("BeeIllegalBusinessException: It is not allowed delete whole documents(records) in one collection(table).If need, you can change the config in bee.osql.notDeleteWholeRecords !");
}
rs = getMongoDatabase(conn).getCollection(tableName).deleteMany(new Document(new HashMap()));
}
if (rs != null)
num=(int) rs.getDeletedCount();
logAffectRow(num);
return num;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
clearInCache(sql, "int", SuidType.MODIFY, num); // has clearContext(sql)
close(conn);
}
}
@Override
public int update(T oldEntity, T newEntity) {
String tableName = _toTableName(oldEntity);
try {
Map oldMap = ParaConvertUtil.toMap(oldEntity);
Map newMap = ParaConvertUtil.toMap(newEntity);
return update(oldMap, newMap, tableName, null);
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
}
}
@Override
public int update(T entity, Condition condition, String... setFields) {
String tableName = _toTableName(entity);
Map reMap[] = toMapForUpdateSet(entity, condition, setFields);
return update(reMap[0], reMap[1], tableName, condition);
}
@Override
public int updateBy(T entity, Condition condition, String... whereFields) {
String tableName = _toTableName(entity);
Map reMap[] = toMapForUpdateBy(entity, condition, whereFields);
return update(reMap[0], reMap[1], tableName, condition);
}
private int update(Map filterMap, Map newMap, String tableName,Condition condition) {
Document oldDoc = null;
Document newDoc = null;
DatabaseClientConnection conn = null;
String sql="";
int num=0;
try {
boolean notUpdateWholeRecords = HoneyConfig.getHoneyConfig().notUpdateWholeRecords;
if (notUpdateWholeRecords && ObjectUtils.isEmpty(filterMap)) {
throw new BeeIllegalBusinessException(
"BeeIllegalBusinessException: It is not allowed update whole documents(records) in one collection(table). If need, you can change the config in bee.osql.notUpdateWholeRecords !");
}
if (filterMap == null) filterMap = new HashMap<>();
oldDoc = new Document(filterMap); // filter
List updateBsonList=new ArrayList<>();
if(newMap!=null && newMap.size()>0) {
newDoc = new Document(newMap);
updateBsonList.add(new Document("$set", newDoc));
}
List updateSetBsonList=MongoConditionUpdateSetHelper.processConditionForUpdateSet(condition);
if(updateSetBsonList!=null) updateBsonList.addAll(updateSetBsonList);
Bson updateSet=Updates.combine(updateBsonList);
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, oldDoc, null, null,
null, null, false, null, updateSet); // this method no entityClass
sql = struct.getSql();
HoneyContext.addInContextForCache(sql, struct.getTableName());
logSQLForMain(" Mongodb::update: "+sql);
conn = getConn();
// UpdateResult rs = getMongoDatabase(conn).getCollection(tableName).updateMany(oldDoc, updateBsonList.get(0)); //ok
UpdateResult rs = getMongoDatabase(conn).getCollection(tableName).updateMany(oldDoc, updateSet);
Logger.debug("Update raw json: "+rs.toString());
num=(int) rs.getModifiedCount();
logAffectRow(num);
return num;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
clearInCache(sql, "int", SuidType.MODIFY, num);
close(conn);
}
}
private Map[] toMapForUpdateBy(T entity, Condition condition, String... specialFields) {
return toMapForUpdate(entity, condition, true, specialFields);
}
private Map[] toMapForUpdateSet(T entity, Condition condition, String... specialFields) {
return toMapForUpdate(entity, condition, false, specialFields);
}
private String[] adjustVariableString(String... fieldList) {
if (fieldList == null) return new String[] { "" };
String fields[];
if (fieldList.length == 1) { // 变长参数,只有一个时,才允许用逗号隔开
fields = fieldList[0].split(",");
} else {
fields = fieldList;
}
return fields;
}
// 没指定为whereFields的字段,作为set部分(默认只处理非空,非null的字段)
// condition中op,between,notBetween方法设置的字段,不受includeType的值影响
@SuppressWarnings("unchecked")
private Map[] toMapForUpdate(T entity, Condition condition,
boolean isFilterField, String... specialFields) {
Map reMap[] = new Map[2];
try {
if (condition != null) condition.setSuidType(SuidType.UPDATE);
Map entityMap = ParaConvertUtil.toMap(entity, getIncludeType(condition));
Map filterMapFromC = MongoConditionHelper.processCondition(condition);
// Map setMapFromC = MongoConditionHelper.processCondition(condition); // 只获取set的部分 condition set的部分,在下一方法才获取
// String fields[] = specialFields.split(",");
String fields[] = adjustVariableString(specialFields);
Map specialMap = new LinkedHashMap();
for (int i = 0; i < fields.length; i++) {
fields[i] = _toColumnName(fields[i], entity.getClass());
if ("id".equalsIgnoreCase(fields[i])) {// 替换id为_id fixed bug v2.0.2.14
fields[i] = "_id";
}
// entityMap分为两部分, 先找出特殊的部分
if (entityMap.containsKey(fields[i])) { //将entity的字段转到filter,作为过滤
specialMap.put(fields[i], entityMap.get(fields[i]));
entityMap.remove(fields[i]);
}
// Condition.set(arg1,arg2) 另外设置的字段,不受updateFields的约束;因此updateFields只指定在entity里的字段即可.
//mongodb没必要像下一句说的那么麻烦.
//// 一个字段既在指定的updateFields,也用在了Condition.set(arg1,arg2)等方法设置,entity里相应的字段会按规则转化到where部分.(V1.9.8)
}
Map filterMap;
Map setMap;
if (isFilterField) {
filterMap = specialMap;
setMap = entityMap;
} else {
filterMap = entityMap;
setMap = specialMap;
}
//这个方法,filterMapFromC 只从Condition提取过滤条件
if (ObjectUtils.isNotEmpty(filterMapFromC)) filterMap.putAll(filterMapFromC);
reMap[0] = filterMap;
reMap[1] = setMap;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
}
return reMap;
}
@Override
public int insert(T entity[], int batchSize, String excludeFields) {
String tableName = _toTableName(entity[0]);
int len = entity.length;
List list = null;
int count = 0;
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, null, null, null,
null, null, false,entity.getClass()," (Artificial sql) Just define for cache: insert batch "); //insert 放在updateSet
String sql=struct.getSql();
HoneyContext.addInContextForCache(sql, struct.getTableName());
logSQLForMain(" Mongodb::insert: "+sql);
DatabaseClientConnection conn = getConn();
try {
for (int i = 1; i <= len; i++) { // i 1..len
Document doc = toDocumentExcludeSome(entity[i - 1], excludeFields);
if (i == 1) list = new ArrayList<>();
list.add(doc);
if (i % batchSize == 0 || i == len) {
InsertManyResult irs = getMongoDatabase(conn).getCollection(tableName)
.insertMany(list);
// System.out.println(irs.getInsertedIds());
count += irs.getInsertedIds().size();
// MongoUtils.getCollection(tableName).bulkWrite(list);
if (i != len) list = new ArrayList<>();
}
}
logAffectRow(count);
return count;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
logAffectRow(count);
if (isConstraint(e)) {
Logger.warn(e.getMessage());
return count;
}
throw ExceptionHelper.convert(e);
} finally {
clearInCache(sql, "int", SuidType.MODIFY, count);
close(conn);
}
}
private Document toDocument(T entity) {
Document doc = null;
try {
Map map = ParaConvertUtil.toMap(entity);
if (ObjectUtils.isNotEmpty(map)) doc = new Document(map);
} catch (Exception e) {
throw ExceptionHelper.convert(e);
}
return doc;
}
private Document toDocumentExcludeSome(T entity, String excludeFields) {
Document doc = null;
try {
Map map = ParaConvertUtil.toMapExcludeSome(entity, excludeFields);
doc = new Document(map);
} catch (Exception e) {
throw ExceptionHelper.convert(e);
}
return doc;
}
@SuppressWarnings("rawtypes")
private Object[] processId(Class clazz, Object id) {
Object obj[] = new Object[2];
Document one = new Document();
Bson moreFilter = null;
if (id instanceof String) {
String ids[] = ((String) id).split(",");
String idType = getIdType(clazz, getPkName(clazz));
if (ids.length > 1) {
Document idFilters[] = new Document[ids.length];
int k = 0;
for (String idValue : ids) {
if ("String".equals(idType) && MongodbUtil.isMongodbId(idValue))
idFilters[k++] = new Document(IDKEY, new ObjectId(idValue)); // 改为in 也可以
else
idFilters[k++] = new Document(IDKEY, tranIdObject(idType, idValue)); // 改为in 也可以
}
moreFilter = Filters.or(idFilters);
} else {
if ("String".equals(idType) && MongodbUtil.isMongodbId(ids[0]))
one.put(IDKEY, new ObjectId(ids[0]));
else
one.put(IDKEY, tranIdObject(idType, ids[0]));
}
} else {
one.put(IDKEY, id);
}
obj[0] = one;
obj[1] = moreFilter;
return obj;
}
// @Override
// public List selectById(Class entityClass, Object id) {
//
// String tableName = _toTableNameByClass(entityClass);
//
// Object[] obj = processId(entityClass, id);
// Document one = (Document) obj[0];
// Bson moreFilter = (Bson) obj[1];
//
//
//// DatabaseClientConnection conn = getConn();
//// try {
//// FindIterable docIterable = null;
//// if (moreFilter != null)
//// docIterable = getMongoDatabase(conn).getCollection(tableName).find(moreFilter);
//// else
//// docIterable = getMongoDatabase(conn).getCollection(tableName).find(one);
////
//// return TransformResult.toListEntity(docIterable, entityClass);
//// } finally {
//// close(conn);
//// }
// }
@SuppressWarnings("rawtypes")
private String getIdType(Class clazz, String pkName) {
Field field = null;
String type = null;
try {
field = clazz.getDeclaredField(pkName);
type = field.getType().getSimpleName();
} catch (Exception e) {
// ignore
}
return type;
}
@SuppressWarnings("rawtypes")
private String getPkName(Class c) {
try {
c.getDeclaredField("id"); // V1.11 因主键可以不是默认id,多了此步检测
return "id";
} catch (NoSuchFieldException e) {
String pkName = HoneyUtil.getPkFieldNameByClass(c);
if ("".equals(pkName))
throw new ObjSQLException("No primary key in " + c.getName());
if (pkName.contains(",")) throw new ObjSQLException(
"method of selectById just need one primary key, but more than one primary key in "
+ c.getName());
return pkName;
}
}
private Object tranIdObject(String idType, String idValue) {
if (idType != null) {
if ("Long".equals(idType) || "long".equals(idType)) {
return Long.parseLong(idValue);
} else if ("Integer".equals(idType) || "int".equals(idType)) {
return Integer.parseInt(idValue);
} else if ("Short".equals(idType) || "short".equals(idType)) {
return Short.parseShort(idValue);
} else {
return idValue;
}
}
return idValue;
}
@Override
@SuppressWarnings("rawtypes")
public int deleteById(Class c, Object id) {
String tableName = _toTableNameByClass(c);
Object[] obj = processId(c, id);
Document one = (Document) obj[0];
Bson moreFilter = (Bson) obj[1];
Object filter;
if (moreFilter != null)
filter=moreFilter;
else
filter=one;
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, filter, null, null,
null, null, false,c);
String sql=struct.getSql();
logSQLForMain(" Mongodb::deleteById: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
int num=0;
DatabaseClientConnection conn = getConn();
try {
DeleteResult rs = null;
if (moreFilter != null)
rs = getMongoDatabase(conn).getCollection(tableName).deleteMany(moreFilter);
else
rs = getMongoDatabase(conn).getCollection(tableName).deleteOne(one);
num=(int) rs.getDeletedCount();
logAffectRow(num);
return num;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
clearInCache(sql, "int", SuidType.MODIFY, num);
close(conn);
}
}
@Override
public int count(T entity, Condition condition) {
String tableName = _toTableName(entity);
Document filter = toDocument(entity, condition);
Class entityClass = toClassT(entity);
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, filter, null, null, null,
null, false, entityClass);
String c = count(struct, entityClass);
return Integer.parseInt(c);
}
@Override
public String count(MongoSqlStruct struct, Class entityClass) {
if (!ShardingUtil.hadSharding()) {
return _count(struct, entityClass);
} else {
if (HoneyContext.getSqlIndexLocal() == null) {
String cacheValue = _count(struct, entityClass); // 检测缓存的
if (cacheValue != null) {
logDsTab();
return cacheValue;
}
String fun = "";
fun = new MongodbShardingSelectFunEngine().asynProcess(entityClass, this,
struct);
String sql = struct.getSql();
addInCache(sql, fun, 1);
return fun;
} else { // 子线程执行
return _count(struct, entityClass);
}
}
}
public String _count(MongoSqlStruct struct, Class entityClass) {
String sql=struct.getSql();
logSQLForMain(" Mongodb::count: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
Object cacheObj = getCache().get(sql);
if (cacheObj != null) {
clearContext(sql);
return (String) cacheObj;
}
if (isShardingMain()) return null; // sharding时,主线程没有缓存就返回.
String tableName=struct.getTableName();
Document filter=(Document)struct.getFilter();
DatabaseClientConnection conn = getConn();
try {
int c;
if (filter != null)
c = (int) getMongoDatabase(conn).getCollection(tableName)
.countDocuments(filter);
else
c = (int) getMongoDatabase(conn).getCollection(tableName).countDocuments();
addInCache(sql, c + "", 1);
logAffectRow(c);
return c+"";
} finally {
close(conn);
}
}
@Override
public long insertAndReturnId(T entity, IncludeType includeType) {
String tableName = _toTableName(entity);
Condition condition = null;
if (includeType != null) condition = BeeFactoryHelper.getCondition().setIncludeType(includeType);
Document doc = toDocument(entity, condition);
int num = 0;
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, null, null, null, null,
null, false, entity.getClass(), doc);
String sql = struct.getSql();
logSQLForMain(" Mongodb::insertAndReturnId: "+sql);
HoneyContext.addInContextForCache(sql, struct.getTableName());
DatabaseClientConnection conn = getConn();
try {
BsonValue bv = getMongoDatabase(conn).getCollection(tableName).insertOne(doc)
.getInsertedId();
long r=0;
if (bv != null) {
r = bv.asInt64().longValue();
if (r > 0) num = 1;
}
return r;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
boolean notCatch=HoneyConfig.getHoneyConfig().notCatchModifyDuplicateException;
if (!notCatch && isConstraint(e)) { //内部捕获并且是重复异常,则由Bee框架处理
boolean notShow=HoneyConfig.getHoneyConfig().notShowModifyDuplicateException;
if(! notShow) Logger.warn(e.getMessage());
return num;
}
Logger.warn("Confirm that the returned value is numeric type!");
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
logAffectRow(num);
clearInCache(sql, "int", SuidType.MODIFY, num);
close(conn);
}
}
/**
* SQL function: max,min,avg,sum,count. 如果统计的结果集为空,除了count返回0,其它都返回空字符.
*/
@Override
public String selectWithFun(T entity, FunctionType functionType, String fieldForFun,
Condition condition) {
if(entity==null) return null;
if (FunctionType.COUNT == functionType) {
return count(entity, condition)+"";
}
// last pipeline stage can not be null 不能在这拆分
String tableName = _toTableName(entity);
Document filter = toDocument(entity, condition);
Bson funBson = null;
if ("id".equalsIgnoreCase(fieldForFun)) fieldForFun = IDKEY;
// if (filter != null) listBson.add(Aggregates.match(filter)); // 过滤条件,要放在match里
if (FunctionType.MAX == functionType) {
// fun=Arrays.asList(Aggregates.match(filter), group(null, max("_fun", "$"+fieldForFun)) );
funBson = group(null, max("_fun", "$" + fieldForFun));
} else if (FunctionType.MIN == functionType) {
funBson = group(null, min("_fun", "$" + fieldForFun));
} else if (FunctionType.AVG == functionType) {
funBson = group(null, avg("_fun", "$" + fieldForFun));
} else if (FunctionType.SUM == functionType) {
funBson = group(null, sum("_fun", "$" + fieldForFun)); // 统计的值为null时, sum: 0
}
Class entityClass = toClassT(entity);
MongoSqlStruct struct = new MongoSqlStruct("int", tableName, filter, null, null,
null, null, false,entityClass,funBson);
return selectWithFun(struct, entityClass);
}
@Override
public String selectWithFun(MongoSqlStruct struct, Class entityClass) {
if (!ShardingUtil.hadSharding()) {
return _selectWithFun(struct, entityClass);
} else {
if (HoneyContext.getSqlIndexLocal() == null) {
String cacheValue=_selectWithFun(struct, entityClass); //检测缓存的
if(cacheValue!=null) {
logDsTab();
return cacheValue;
}
String fun = "";
String funType = HoneyContext.getSysCommStrLocal(StringConst.FunType);
if (FunctionType.AVG.getName().equalsIgnoreCase(funType)) {
Logger.warn("AVG do not process here!");
} else {
fun = new MongodbShardingSelectFunEngine().asynProcess(entityClass, this, struct);
}
String sql = struct.getSql();
addInCache(sql, fun, 1);
return fun;
} else { // 子线程执行
return _selectWithFun(struct, entityClass);
}
}
}
public String _selectWithFun(MongoSqlStruct struct, Class entityClass) {
Document filter = (Document) struct.getFilter();
String tableName = struct.getTableName();
String sql = struct.getSql();
logSQLForMain(" Mongodb::selectWithFun: "+sql);
HoneyContext.addInContextForCache(sql, tableName);
Object cacheObj = getCache().get(sql);
if (cacheObj != null) {
clearContext(sql);
return (String) cacheObj;
}
if (isShardingMain()) return null; // sharding时,主线程没有缓存就返回.
DatabaseClientConnection conn = getConn();
try {
MongoCollection collection = getMongoDatabase(conn)
.getCollection(tableName);
List listBson = new ArrayList<>();
Bson funBson = (Bson) struct.getUpdateSet();
if (filter != null) listBson.add(Aggregates.match(filter)); // 过滤条件,要放在match里
// if (FunctionType.MAX == functionType) {
//// fun=Arrays.asList(Aggregates.match(filter), group(null, max("_fun", "$"+fieldForFun)) );
// funBson = group(null, max("_fun", "$" + fieldForFun));
// } else if (FunctionType.MIN == functionType) {
// funBson = group(null, min("_fun", "$" + fieldForFun));
// } else if (FunctionType.AVG == functionType) {
// funBson = group(null, avg("_fun", "$" + fieldForFun));
// } else if (FunctionType.SUM == functionType) {
// funBson = group(null, sum("_fun", "$" + fieldForFun)); // 统计的值为null时, sum: 0
// }
listBson.add(funBson);
Document rs = collection.aggregate(listBson).first();
String fun = "";
if (rs != null) {
Logger.debug("selectWithFun raw json: "+rs.toJson());
// Logger.debug(rs.get("_fun")+"");
//
// Map jsonMap = null;
// try {
// ObjectMapper objectMapper = new ObjectMapper();
// jsonMap = objectMapper.readValue(rs.toJson(),
// new TypeReference>() {
// });
// } catch (Exception e) {
// Logger.debug(e.getMessage(), e);
// }
// if (jsonMap != null && jsonMap.get("_fun") != null)
// fun = jsonMap.get("_fun").toString();
// else
// fun = "";
Object json = rs.get("_fun");
if (json != null)
fun = json.toString();
else
fun = "";
}
addInCache(sql, fun, 1);
return fun;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
close(conn);
}
}
//table,where:doc.toJson(), group: orderyBy: skip: limit: selectFields:
private List selectWithGroupBy(T entity,Condition condition) {
String tableName = _toTableName(entity);
Document filter = toDocument(entity, condition); // 加过滤条件.
DatabaseClientConnection conn = getConn();
try {
MongoCollection collection = getMongoDatabase(conn).getCollection(tableName);
List groupNameslist = condition.getGroupByFields();
int size = groupNameslist == null ? -1 : groupNameslist.size();
StringBuffer groupColumn = new StringBuffer(" '_id' : {");
// { '$group' : { '_id' : {'id':'$id'},'_count' : { '$sum' : 1 }}}
String fieldName="";
for (int i = 0; groupNameslist != null && i < size; i++) {
if (i != 0) groupColumn.append(" , ");
fieldName=groupNameslist.get(i);
if("id".equalsIgnoreCase(fieldName)) fieldName="_id";
groupColumn.append("'");
groupColumn.append(fieldName);
groupColumn.append("':'$");
groupColumn.append(fieldName);
groupColumn.append("'");
}
groupColumn.append("}");
StringBuffer groupSearch = new StringBuffer("{ '$group' : {");
groupSearch.append(groupColumn);
String[] selectFunBsonFieldArray = selectFunBsonField(condition);
for (int i = 0; selectFunBsonFieldArray != null
&& i < selectFunBsonFieldArray.length; i++) {
groupSearch.append(",");
groupSearch.append(selectFunBsonFieldArray[i]);
}
groupSearch.append("}}");
List listBson = new ArrayList<>();
if (filter != null) listBson.add(Aggregates.match(filter)); // 过滤条件,要放在match里
listBson.add(BsonDocument.parse(groupSearch.toString()));
AggregateIterable iterable= collection.aggregate(listBson);
//////// test start
// System.out.println("--------------------start--");
Class entityClass = toClassT(entity);
List list = new ArrayList<>();
MongoCursor it=iterable.iterator();
// String json="";
while(it.hasNext()) {
Document doc=it.next();
// {"_id": {"id": null}, "id": 10299, "max_total": {"$numberDecimal": "298893.12"}, "min_total": {"$numberDecimal": "70.7"}}
// System.out.println(doc.toJson());
//
// Object id=doc.get("id");
// Object max_total=doc.get("max_total");
// Object min_total=doc.get("min_total");
//
// System.out.println(id);
// System.out.println(max_total);
// System.out.println(min_total);
//直接按字段名获取.
// {"_id": {"abc": "test bee ", "name": "mongodb21"}, "_count": 2}
// Object name=doc.get("name");
// System.out.println(name); //不行
// Object _count=doc.get("_count");
// System.out.println(_count);
//
// Document g=(Document)doc.get("_id"); //多个排序字段.
// System.out.println(g.get("name"));
// System.out.println(g.get("abc"));
Map groupNameMap=null;
if(size>0) {
Document groupNameDoc=(Document)doc.get("_id");
groupNameMap=TransformResult.doc2Map(groupNameDoc);
}
Map rsMap=TransformResult.doc2Map(doc);
rsMap.remove("_id");
if(size>0) rsMap.putAll(groupNameMap); //要是排序字段有_id,会在groupNameMap保留
Logger.debug("selectWithGroupBy doc2Map raw json: "+rsMap.toString());
try {
list.add(TransformResult.toEntity(rsMap, entityClass));
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
}
/*// System.out.println(it.next().toJson());
json=it.next().toJson();
System.out.println(json);
Map jsonMap = null;
try {
ObjectMapper objectMapper = new ObjectMapper();
jsonMap = objectMapper.readValue(json,new TypeReference>(){});
if (jsonMap != null && jsonMap.get("_id") != null) {
}
} catch (Exception e) {
Logger.debug(e.getMessage(), e);
}*/
}
//////// test end
return list;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
close(conn);
}
}
@SuppressWarnings("rawtypes")
private String _toTableNameByClass(Class c) {
return NameTranslateHandle.toTableName(c.getName());
}
private String[] selectFunBsonField(Condition condition) {
String[] bsonFieldArray = null;
ConditionImpl conditionImpl = (ConditionImpl) condition;
List funExpList = conditionImpl.getFunExpList();
for (int i = 0; funExpList != null && i < funExpList.size(); i++) {
if (i == 0) bsonFieldArray = new String[funExpList.size()];
String functionType = funExpList.get(i).getFunctionType();
String alias = funExpList.get(i).getAlias();
String fieldForFun = funExpList.get(i).getField();
if(StringUtils.isBlank(alias)) alias=fieldForFun;
if ("id".equalsIgnoreCase(fieldForFun)) fieldForFun = IDKEY;
if (FunctionType.MAX.getName().equals(functionType)) {
bsonFieldArray[i] ="'XfieldX1' : { '$max' : '$XfieldX2' }".replace("XfieldX1", alias).replace("XfieldX2", fieldForFun);
} else if (FunctionType.MIN.getName().equals(functionType)) {
bsonFieldArray[i] ="'XfieldX1' : { '$min' : '$XfieldX2' }".replace("XfieldX1", alias).replace("XfieldX2", fieldForFun);
} else if (FunctionType.AVG.getName().equals(functionType)) {
bsonFieldArray[i] ="'XfieldX1' : { '$avg' : '$XfieldX2' }".replace("XfieldX1", alias).replace("XfieldX2", fieldForFun);
} else if (FunctionType.SUM.getName().equals(functionType)) {
bsonFieldArray[i] ="'XfieldX1' : { '$sum' : '$XfieldX2' }".replace("XfieldX1", alias).replace("XfieldX2", fieldForFun);
} else if (FunctionType.COUNT.getName().equals(functionType)) {
bsonFieldArray[i] ="'_countX1' : { '$sum' : 1 }".replace("_countX1", alias);
}
}
if(bsonFieldArray==null && Boolean.TRUE.equals(conditionImpl.hasGroupBy())) {
bsonFieldArray=new String[1];
bsonFieldArray[0] = "'_count' : { '$sum' : 1 }";
}
return bsonFieldArray;
}
private Cache cache;
public Cache getCache() {
if (cache == null) {
cache = BeeFactory.getHoneyFactory().getCache();
}
return cache;
}
public void setCache(Cache cache) {
this.cache = cache;
}
private void close(DatabaseClientConnection conn) {
try {
if (conn != null) conn.close();
} catch (IOException e) {
Logger.error(e.getMessage(), e);
}
}
private int getIncludeType(Condition condition) {
if (condition == null) return -1;
return condition.getIncludeType() == null ? -1 : condition.getIncludeType().getValue();
}
private Document toDocument(T entity, Condition condition) {
Document doc = null;
try {
Map map = ParaConvertUtil.toMap(entity, getIncludeType(condition));
if (condition == null) {
if (ObjectUtils.isNotEmpty(map))
return new Document(map);
else
return null;
}
Map map2 = MongoConditionHelper.processCondition(condition);
if (ObjectUtils.isNotEmpty(map) && ObjectUtils.isNotEmpty(map2))
map.putAll(map2); // map的值,会被map2中有同样key的值覆盖.
else if (ObjectUtils.isEmpty(map)) map = map2;
if (ObjectUtils.isNotEmpty(map)) doc = new Document(map);
} catch (Exception e) {
throw ExceptionHelper.convert(e);
}
return doc;
}
@Override
public boolean createTable(Class entityClass, boolean isDropExistTable) {
if (!ShardingUtil.hadSharding()) {
return _createTable(entityClass, isDropExistTable); // 不用分片走的分支
} else {
if (HoneyContext.getSqlIndexLocal() == null) { // 分片,主线程
boolean f = new MongodbShardingDdlEngine().asynProcess(entityClass, this,
isDropExistTable);
return f;
} else { // 子线程执行
return _createTable(entityClass, isDropExistTable);
}
}
}
private boolean _createTable(Class entityClass, boolean isDropExistTable) {
String tableName = _toTableNameByClass(entityClass);
String baseTableName = tableName.replace(StringConst.ShardingTableIndexStr, "");
DatabaseClientConnection conn = null;
boolean f = false;
try {
conn = getConn();
MongoDatabase mdb = getMongoDatabase(conn);
if (isDropExistTable) {
logSQLForMain(" Mongodb::drop collection(table): " + baseTableName);
mdb.getCollection(baseTableName).drop();
}
logSQLForMain(" Mongodb::create collection(table): " + baseTableName);
mdb.createCollection(baseTableName);
f = true;
} catch (Exception e) {
if (e instanceof MongoTimeoutException) Logger.warn(Timeout_MSG);
throw ExceptionHelper.convert(e);
} finally {
close(conn);
}
return f;
}
private static final String Timeout_MSG = "Can not connect the Mongodb server. Maybe you did not start the Mongodb server!";
}