com.bugull.mongo.BuguDao.java Source code

Java tutorial

Introduction

Here is the source code for com.bugull.mongo.BuguDao.java

Source

/*
 * Copyright (c) www.bugull.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bugull.mongo;

import com.bugull.mongo.annotations.Default;
import com.bugull.mongo.annotations.EnsureIndex;
import com.bugull.mongo.annotations.Entity;
import com.bugull.mongo.annotations.Id;
import com.bugull.mongo.annotations.IdType;
import com.bugull.mongo.annotations.SplitType;
import com.bugull.mongo.cache.FieldsCache;
import com.bugull.mongo.exception.DBConnectionException;
import com.bugull.mongo.exception.IdException;
import com.bugull.mongo.lucene.backend.EntityChangedListener;
import com.bugull.mongo.lucene.backend.IndexChecker;
import com.bugull.mongo.misc.CascadeChecker;
import com.bugull.mongo.misc.DBIndex;
import com.bugull.mongo.misc.EntityRemovedListener;
import com.bugull.mongo.utils.IdUtil;
import com.bugull.mongo.utils.MapperUtil;
import com.bugull.mongo.utils.Operator;
import com.bugull.mongo.utils.ReferenceUtil;
import com.bugull.mongo.utils.StringUtil;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.log4j.Logger;

/**
 * The basic Dao class.
 * 
 * @author Frank Wen(xbwen@hotmail.com)
 */
public class BuguDao<T> {

    private final static Logger logger = Logger.getLogger(BuguDao.class);

    protected DBCollection coll;
    protected Class<T> clazz;
    protected DBObject keys; //non-lazy fields
    protected WriteConcern concern;
    protected EntityChangedListener luceneListener;
    protected EntityRemovedListener cascadeListener;

    public BuguDao(Class<T> clazz) {
        this.clazz = clazz;
        DB db = null;
        try {
            db = BuguConnection.getInstance().getDB();
        } catch (DBConnectionException ex) {
            logger.error(ex.getMessage(), ex);
        }
        //The default write concern is ACKNOWLEDGED, set in MongoClientOptions.
        concern = db.getWriteConcern();
        //init none-split collection
        Entity entity = clazz.getAnnotation(Entity.class);
        SplitType split = entity.split();
        if (split == SplitType.NONE) {
            String name = MapperUtil.getEntityName(clazz);
            initCollection(name);
        }
        //for keys
        keys = MapperUtil.getKeyFields(clazz);
        //for lucene
        if (IndexChecker.needListener(clazz)) {
            luceneListener = new EntityChangedListener(clazz);
        }
        //for cascade delete
        if (CascadeChecker.needListener(clazz)) {
            cascadeListener = new EntityRemovedListener(clazz);
        }
    }

    private void initCollection(String name) {
        DB db = null;
        try {
            db = BuguConnection.getInstance().getDB();
        } catch (DBConnectionException ex) {
            logger.error(ex.getMessage(), ex);
        }
        Entity entity = clazz.getAnnotation(Entity.class);
        //if capped
        if (entity.capped() && !db.collectionExists(name)) {
            DBObject options = new BasicDBObject("capped", true);
            long capSize = entity.capSize();
            if (capSize != Default.CAP_SIZE) {
                options.put("size", capSize);
            }
            long capMax = entity.capMax();
            if (capMax != Default.CAP_MAX) {
                options.put("max", capMax);
            }
            coll = db.createCollection(name, options);
        } else {
            coll = db.getCollection(name);
        }
        //for @EnsureIndex
        EnsureIndex ei = clazz.getAnnotation(EnsureIndex.class);
        if (ei != null) {
            List<DBIndex> list = MapperUtil.getDBIndex(ei.value());
            for (DBIndex dbi : list) {
                coll.createIndex(dbi.getKeys(), dbi.getOptions());
            }
        }
    }

    /**
     * If collection is splitted by date, you have to set the date to check which collection is in use.
     * @param date 
     */
    public void setSplitSuffix(Date date) {
        Entity entity = clazz.getAnnotation(Entity.class);
        SplitType split = entity.split();
        SimpleDateFormat sdf = null;
        switch (split) {
        case DAILY:
            sdf = new SimpleDateFormat("yyyy-MM-dd");
            break;
        case MONTHLY:
            sdf = new SimpleDateFormat("yyyy-MM");
            break;
        case YEARLY:
            sdf = new SimpleDateFormat("yyyy");
            break;
        default:
            break;
        }
        if (sdf != null) {
            String ext = sdf.format(date);
            String name = MapperUtil.getEntityName(clazz);
            initCollection(name + "-" + ext);
        }
    }

    /**
     * If collection is splitted by string, you have to set the string value to check which collection is in use.
     * @param s 
     */
    public void setSplitSuffix(String s) {
        Entity entity = clazz.getAnnotation(Entity.class);
        SplitType split = entity.split();
        if (split == SplitType.STRING) {
            String name = MapperUtil.getEntityName(clazz);
            initCollection(name + "-" + s);
        }
    }

    /**
     * The default write concern is ACKNOWLEDGED, you can change it.
     * @param concern 
     */
    public void setWriteConcern(WriteConcern concern) {
        this.concern = concern;
    }

    /**
     * Insert an entity to mongoDB.
     * @param t
     * @return 
     */
    public WriteResult insert(T t) {
        DBObject dbo = MapperUtil.toDBObject(t);
        WriteResult wr = coll.insert(dbo, concern);
        String id = dbo.get(Operator.ID).toString();
        BuguEntity ent = (BuguEntity) t;
        ent.setId(id);
        if (luceneListener != null) {
            luceneListener.entityInsert(ent);
        }
        return wr;
    }

    /**
     * Batch insert.
     * @param list 
     * @return 
     */
    public WriteResult insert(List<T> list) {
        List<DBObject> dboList = new ArrayList<DBObject>();
        for (T t : list) {
            dboList.add(MapperUtil.toDBObject(t));
        }
        WriteResult wr = coll.insert(dboList, concern);
        int len = dboList.size();
        for (int i = 0; i < len; i++) {
            String id = dboList.get(i).get(Operator.ID).toString();
            BuguEntity ent = (BuguEntity) (list.get(i));
            ent.setId(id);
        }
        if (luceneListener != null) {
            for (T t : list) {
                luceneListener.entityInsert((BuguEntity) t);
            }
        }
        return wr;
    }

    /**
     * Save an entity to mongoDB. 
     * If no id in it, then insert the entity.
     * Else, check the id type, to confirm do save or insert.
     * @param t 
     * @return 
     */
    public WriteResult save(T t) {
        WriteResult wr = null;
        BuguEntity ent = (BuguEntity) t;
        if (StringUtil.isEmpty(ent.getId())) {
            wr = insert(t);
        } else {
            Field idField = null;
            try {
                idField = FieldsCache.getInstance().getIdField(clazz);
            } catch (IdException ex) {
                logger.error(ex.getMessage(), ex);
            }
            Id idAnnotation = idField.getAnnotation(Id.class);
            if (idAnnotation.type() == IdType.USER_DEFINE) {
                if (this.exists(Operator.ID, ent.getId())) {
                    wr = doSave(ent);
                } else {
                    wr = insert(t);
                }
            } else {
                wr = doSave(ent);
            }
        }
        return wr;
    }

    private WriteResult doSave(BuguEntity ent) {
        WriteResult wr = coll.save(MapperUtil.toDBObject(ent), concern);
        if (luceneListener != null) {
            luceneListener.entityUpdate(ent);
        }
        return wr;
    }

    /**
     * Drop the collection. 
     * It will automatically drop all indexes from this collection.
     */
    public void drop() {
        if (luceneListener != null || cascadeListener != null) {
            List<T> list = findAll();
            for (T t : list) {
                remove(t);
            }
        }
        coll.drop();
        coll.dropIndexes();
    }

    /**
     * Remove an entity.
     * @param t 
     * @return 
     */
    public WriteResult remove(T t) {
        BuguEntity ent = (BuguEntity) t;
        return remove(ent.getId());
    }

    /**
     * Remove an entity by id.
     * @param id 
     * @return 
     */
    public WriteResult remove(String id) {
        if (cascadeListener != null) {
            BuguEntity entity = (BuguEntity) findOne(id);
            cascadeListener.entityRemove(entity);
        }
        if (luceneListener != null) {
            luceneListener.entityRemove(id);
        }
        DBObject dbo = new BasicDBObject(Operator.ID, IdUtil.toDbId(clazz, id));
        return coll.remove(dbo, concern);
    }

    /**
     * Batch remove by id.
     * @param idList
     * @return 
     */
    public WriteResult remove(List<String> idList) {
        int len = idList.size();
        Object[] arr = new Object[len];
        for (int i = 0; i < len; i++) {
            arr[i] = IdUtil.toDbId(clazz, idList.get(i));
        }
        DBObject in = new BasicDBObject(Operator.IN, arr);
        return removeMulti(new BasicDBObject(Operator.ID, in));
    }

    /**
     * Remove by condition.
     * @param key the condition field
     * @param value the condition value
     * @return 
     */
    public WriteResult remove(String key, Object value) {
        value = checkSpecialValue(key, value);
        return removeMulti(new BasicDBObject(key, value));
    }

    /**
     * Remove by query condition.
     * @param query 
     * @return 
     */
    public WriteResult remove(BuguQuery query) {
        return removeMulti(query.getCondition());
    }

    private WriteResult removeMulti(DBObject condition) {
        DBCursor cursor = coll.find(condition, keys);
        List<T> list = MapperUtil.toList(clazz, cursor);
        if (cascadeListener != null) {
            for (T t : list) {
                cascadeListener.entityRemove((BuguEntity) t);
            }
        }
        if (luceneListener != null) {
            for (T t : list) {
                BuguEntity ent = (BuguEntity) t;
                luceneListener.entityRemove(ent.getId());
            }
        }
        return coll.remove(condition, concern);
    }

    private Object checkSpecialValue(String key, Object value) {
        Object result = value;
        if (value instanceof BuguEntity) {
            BuguEntity be = (BuguEntity) value;
            result = ReferenceUtil.toDbReference(clazz, key, be.getClass(), be.getId());
        } else if (!(value instanceof DBObject) && (FieldsCache.getInstance().isEmbedField(clazz, key)
                || FieldsCache.getInstance().isEmbedListField(clazz, key))) {
            result = MapperUtil.toDBObject(value);
        }
        return result;
    }

    /**
     * Check if any entity with id already exists
     * @param id the id value to check
     * @return 
     */
    public boolean exists(String id) {
        DBObject query = new BasicDBObject();
        query.put(Operator.ID, IdUtil.toDbId(clazz, id));
        return coll.findOne(query) != null;
    }

    /**
     * Check if any entity match the condition.
     * @param key the condition field
     * @param value the condition value
     * @return 
     */
    public boolean exists(String key, Object value) {
        value = checkSpecialValue(key, value);
        DBObject query = new BasicDBObject(key, value);
        return coll.findOne(query) != null;
    }

    /**
     * Find a single document by natural order
     * @return 
     */
    public T findOne() {
        DBObject result = coll.findOne();
        return MapperUtil.fromDBObject(clazz, result);
    }

    /**
     * Find a single document by id
     * @param id
     * @return 
     */
    public T findOne(String id) {
        DBObject dbo = new BasicDBObject();
        dbo.put(Operator.ID, IdUtil.toDbId(clazz, id));
        DBObject result = coll.findOne(dbo);
        return MapperUtil.fromDBObject(clazz, result);
    }

    /**
     * Find a single document by key-value
     * @param key
     * @param value
     * @return 
     */
    public T findOne(String key, Object value) {
        value = checkSpecialValue(key, value);
        DBObject query = new BasicDBObject(key, value);
        DBObject dbo = coll.findOne(query);
        return MapperUtil.fromDBObject(clazz, dbo);
    }

    /**
     * Find all document by natural order
     * @return 
     */
    public List<T> findAll() {
        DBCursor cursor = coll.find(new BasicDBObject(), keys);
        return MapperUtil.toList(clazz, cursor);
    }

    /**
     * Find all document by order
     * @param orderBy
     * @return 
     */
    public List<T> findAll(String orderBy) {
        DBObject dbo = MapperUtil.getSort(orderBy);
        DBCursor cursor = coll.find(new BasicDBObject(), keys).sort(dbo);
        return MapperUtil.toList(clazz, cursor);
    }

    /**
     * Find all document, and return one page
     * @param pageNum
     * @param pageSize
     * @return 
     */
    public List<T> findAll(int pageNum, int pageSize) {
        DBCursor cursor = coll.find(new BasicDBObject(), keys).skip((pageNum - 1) * pageSize).limit(pageSize);
        return MapperUtil.toList(clazz, cursor);
    }

    /**
     * Find all document, and return one page
     * @param orderBy
     * @param pageNum
     * @param pageSize
     * @return 
     */
    public List<T> findAll(String orderBy, int pageNum, int pageSize) {
        DBObject dbo = MapperUtil.getSort(orderBy);
        DBCursor cursor = coll.find(new BasicDBObject(), keys).sort(dbo).skip((pageNum - 1) * pageSize)
                .limit(pageSize);
        return MapperUtil.toList(clazz, cursor);
    }

    public List distinct(String key) {
        return coll.distinct(key);
    }

    /**
     * Count all entity.
     * @return 
     */
    public long count() {
        return coll.count();
    }

    /**
     * Count by condition.
     * @param key the condition field
     * @param value the condition value
     * @return 
     */
    public long count(String key, Object value) {
        value = checkSpecialValue(key, value);
        return coll.count(new BasicDBObject(key, value));
    }

    /**
     * Get the DBCollection object, supplied by the mongodb java driver.
     * @return 
     */
    public DBCollection getCollection() {
        return coll;
    }

    /**
     * Create a query.
     * @return a new BuguQuery object
     */
    public BuguQuery<T> query() {
        return new BuguQuery<T>(coll, clazz, keys);
    }

    /**
     * Create a updater.
     * @return a new BuguUpdater object
     */
    public BuguUpdater<T> update() {
        return new BuguUpdater(coll, clazz, concern, luceneListener);
    }

}