package org.sothis.dal.mongo; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.bson.types.ObjectId; import org.sothis.core.util.Cursor; import org.sothis.core.util.Pager; import org.sothis.dal.AbstractJpaCompatibleDao; import org.sothis.dal.query.Chain; import org.sothis.dal.query.Cnd; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.Mongo; import com.mongodb.WriteConcern; import com.mongodb.WriteResult; /** * mongo db Dao的虚基类 * * @author velna * * @param <E> */ public abstract class AbstractMongoDao<E extends MongoEntity> extends AbstractJpaCompatibleDao<E, String> implements MongoDao<E> { public static final String ID = "_id"; private final String dbName; private final String collectionName; private final MongoQueryBuilder queryBuilder; private Mongo mongo; public AbstractMongoDao() { super(); String tableName = this.getTableName(); int i = tableName.indexOf('.'); if (i <= 0 || i == tableName.length() - 1) { throw new RuntimeException("invalid table name: " + tableName + " of entity class: " + this.getEntityClass()); } dbName = tableName.substring(0, i); this.collectionName = tableName.substring(i + 1); queryBuilder = new MongoQueryBuilder(getPropertyMap(), this.isIdGeneratedValue()); } /** * 得到集合 * * @return */ protected final DBCollection getDbCollection() { DBCollection dbCollection = mongo.getDB(dbName).getCollection(collectionName); dbCollection.setWriteConcern(WriteConcern.SAFE); return dbCollection; } public Mongo getMongo() { return mongo; } public void setMongo(Mongo mongo) { this.mongo = mongo; } @Override public List<E> find(Cnd cnd, Pager pager, Chain chain) { DBObject query = queryBuilder.cndToQuery(cnd); DBObject fields = queryBuilder.chainToFields(chain); DBObject sorts = queryBuilder.orderByToSorts(cnd); DBCursor cursor = this.getDbCollection().find(query, fields).sort(sorts); if (null != pager) { cursor.limit(pager.getPageSize()).skip(pager.getStartRow()).batchSize(pager.getPageSize()); } else { cursor.limit(Integer.MAX_VALUE).skip(0); } List<DBObject> dbObjects = cursor.toArray(); List<E> ret = new ArrayList<E>(dbObjects.size()); for (DBObject object : dbObjects) { ret.add(this.dbObjectToEntity(object)); } return ret; } @Override public Cursor<E> cursor(Cnd cnd, Chain chain) { DBObject query = queryBuilder.cndToQuery(cnd); DBObject fields = queryBuilder.chainToFields(chain); DBObject sorts = queryBuilder.orderByToSorts(cnd); DBCursor cursor = this.getDbCollection().find(query, fields).sort(sorts); return new MongoCursor(cursor); } @Override public int update(Cnd cnd, Chain chain) { DBObject query = queryBuilder.cndToQuery(cnd); DBObject update = queryBuilder.chainToUpdate(chain); WriteResult result = this.getDbCollection().update(query, update, false, true, WriteConcern.SAFE); return result.getN(); } @Override public int delete(Cnd cnd) { DBObject query = queryBuilder.cndToQuery(cnd); WriteResult result = this.getDbCollection().remove(query, WriteConcern.SAFE); return result.getN(); } @Override public E insert(E entity) { if (!this.isIdGeneratedValue() && entity.getId() == null) { throw new IllegalArgumentException("id can not be null since id column is not a generated value"); } DBObject object = this.entityToDBObject(entity); this.getDbCollection().insert(object, WriteConcern.SAFE); entity.setId(toEntityId(object.get(ID))); return entity; } @Override public List<E> insert(List<E> entityList) { List<DBObject> objects = new ArrayList<DBObject>(entityList.size()); for (E e : entityList) { objects.add(this.entityToDBObject(e)); } this.getDbCollection().insert(objects, WriteConcern.SAFE); for (int i = 0; i < objects.size(); i++) { entityList.get(i).setId(this.toEntityId(objects.get(i).get(ID))); } return entityList; } @Override public int count(Cnd cnd) { DBObject query = queryBuilder.cndToQuery(cnd); return (int) this.getDbCollection().count(query); } protected E dbObjectToEntity(DBObject dbObject) { if (null == dbObject) { return null; } E entity; try { entity = this.getEntityClass().newInstance(); Set<String> keySet = dbObject.keySet(); Map<String, PropertyInfo> fieldMap = this.getFieldMap(); for (String key : keySet) { PropertyInfo propertyInfo = fieldMap.get(key); if (null == propertyInfo) { throw new RuntimeException("un-mapped field '" + key + "' of entity class " + this.getEntityClass()); } PropertyDescriptor descriptor = propertyInfo.getPropertyDescriptor(); Method writeMethod = descriptor.getWriteMethod(); Object value = dbObject.get(key); if (value instanceof ObjectId) { value = ((ObjectId) value).toString(); } try { writeMethod.invoke(entity, value); } catch (IllegalArgumentException e) { throw new RuntimeException("Error set value to " + value + "[" + value.getClass() + "] of field " + descriptor.getName() + "[" + descriptor.getPropertyType() + "]", e); } } return entity; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } protected DBObject entityToDBObject(E entity) { if (null == entity) { return null; } DBObject dbObject = new BasicDBObject(); Map<String, PropertyInfo> propertyMap = this.getPropertyMap(); for (Map.Entry<String, PropertyInfo> entry : propertyMap.entrySet()) { PropertyInfo propertyInfo = entry.getValue(); if (propertyInfo.isTransient()) { continue; } Method readMethod = propertyInfo.getPropertyDescriptor().getReadMethod(); Object value; try { value = readMethod.invoke(entity, (Object[]) null); } catch (Exception e) { throw new RuntimeException(e); } if (propertyInfo.isID()) { value = toMongoId(value); } if (null != value) { dbObject.put(propertyInfo.getColumn().name(), value); } } return dbObject; } protected String toEntityId(Object id) { if (this.isIdGeneratedValue()) { ObjectId objectId = (ObjectId) id; return objectId.toString(); } else { return (String) id; } } @SuppressWarnings("unchecked") protected Object toMongoId(Object id) { if (null == id) { return null; } if (id instanceof List) { List<Object> idList = (List<Object>) id; List<Object> idObjectList = new ArrayList<Object>(idList.size()); for (Object _id : idList) { idObjectList.add(toMongoId(_id)); } return idObjectList; } if (!(id instanceof String)) { throw new IllegalArgumentException("id must be instanceof " + String.class.getName() + ", but was " + id.getClass().getName() + " of class " + this.getEntityClass().getName()); } if (this.isIdGeneratedValue()) { return new ObjectId((String) id); } else { return id; } } private class MongoCursor implements Cursor<E> { private final DBCursor cursor; public MongoCursor(DBCursor cursor) { this.cursor = cursor; } @Override public Iterator<E> iterator() { return new EntityMapIterator(cursor.iterator()); } @Override public int count() { return cursor.count(); } @Override public Cursor<E> batchSize(int batchSize) { cursor.batchSize(batchSize); return this; } @Override public Cursor<E> limit(int limit) { cursor.limit(limit); return this; } @Override public Cursor<E> skip(int skip) { cursor.skip(skip); return this; } } private class EntityMapIterator implements Iterator<E> { private final Iterator<DBObject> i; public EntityMapIterator(Iterator<DBObject> i) { this.i = i; } @Override public boolean hasNext() { return i.hasNext(); } @Override public E next() { DBObject next = i.next(); return dbObjectToEntity(next); } @Override public void remove() { i.remove(); } } }