`

MongoDB程序开发之ORM工具Morphia

 
阅读更多

上次的Blog中写到要写一篇关于mongodb的ORM工具的文章。上个月程序基本写完,这回把Blog补上:)

本文主要介绍mongodb的ORM工具morphia的使用。

首先介绍抽象类AbstractMongoDAO,里面包含有Morphia的初始化代码,因此继承了BasicDAO。AbstractMongoDAO.java:

public class AbstractMongoDAO<T, K> extends BasicDAO<T, K> {

    protected static final Morphia morphia = createMorphia();

    protected static final Mongo mongoInstance = createMongoInstance();

    protected static final Datastore morphiaDs = createDataStore();

    protected AbstractMongoDAO(Class<T> entityClass, Datastore ds) {
        super(entityClass, ds);
    }

    private static Morphia createMorphia() {
        return new Morphia();
    }

    private static Datastore createDataStore() {
        morphia.mapPackage("mongodb.orm.model", true);//映射model的包路径
        if (DatabaseProject.DB_CONFIG.containsKey("mongodb.username")) {
            return morphia.createDatastore(mongoInstance, DatabaseProject.DB_CONFIG.getString("mongodb.db")
                    , DatabaseProject.DB_CONFIG.getString("mongodb.username"), DatabaseProject.DB_CONFIG.getString("mongodb.password").toCharArray());
        }
        Datastore ds = morphia.createDatastore(mongoInstance, DatabaseProject.DB_CONFIG.getString("mongodb.db"));
        ds.ensureIndexes();
        return ds;
    }

    protected AbstractMongoDAO(Class<T> entityClass, Mongo mongo, Morphia morphia, String dbName) {
        super(entityClass, mongo, morphia, dbName);
    }

    private static Mongo createMongoInstance() {
        MongoOptions mo = new MongoOptions();
        mo.socketKeepAlive=true;
        mo.autoConnectRetry = true;
        mo.maxAutoConnectRetryTime=10;
        mo.connectionsPerHost = 40;
        mo.connectTimeout = 20 * 1000;
        mo.socketTimeout = 60 * 1000;
        try {
            if (DatabaseProject.DB_CONFIG.containsKey("mongodb.ips")) {
                return new Mongo(getServerAddrsFromConf("mongodb"),mo);
            }
            return new Mongo(new ServerAddress(DatabaseProject.DB_CONFIG.getString("mongodb.ip"), DatabaseProject.DB_CONFIG.getInt("mongodb.port")),mo);
        } catch (Throwable e) {
            DatabaseProject.LOGGER.error("Failed to init mongodb", e);
            throw new ExceptionInInitializerError(e);
        }
    }

    @SuppressWarnings("unchecked")
    public static List<ServerAddress> getServerAddrsFromConf(String confKeyPre) throws NumberFormatException, UnknownHostException {
        ArrayList<ServerAddress> res = new ArrayList<ServerAddress>();
        List<Object> ips = DatabaseProject.DB_CONFIG.getList(confKeyPre + ".ips");
        List<Object> ports = DatabaseProject.DB_CONFIG.getList(confKeyPre + ".port");
        if (ports.size() < ips.size()) {
            int defPort = 0;
            if (ports.size() != 0) defPort = Integer.parseInt(ports.get(0).toString());
            for (int i = 0; i < ips.size(); ++i) {
                if (defPort == 0) res.add(new ServerAddress(ips.get(i).toString()));
                else res.add(new ServerAddress(ips.get(i).toString(), defPort));
            }
        } else {
            for (int i = 0; i < ips.size(); ++i) {
                res.add(new ServerAddress(ips.get(i).toString(),
                        Integer.parseInt(ports.get(i).toString())));
            }
        }
        return res;
    }

    public static Mongo getMongoInstance() {
        return mongoInstance;
    }

    public static DB getDB() {
        return morphiaDs.getDB();
    }

    public static Morphia getMorphia() {
        return morphia;
    }
    
    public Iterable<Key<T>> insert(Iterable<T> entries, WriteConcern wc) {
        return ds.insert(entries, wc);
    }

    public Iterable<Key<T>> insert(Iterable<T> entries) {
        return ds.insert(entries);
    }  
}

注意createMongoInstance方法(上一篇介绍mongo java driver的文章中也有提到),主要是实例化一个mongo实例。

接下来是createDataStore方法,该方法通过morphia对象及使用mongo实例创建一个Datastore对象。其中morphia.mapPackage("mongodb.orm.model", true);是将mongodb.orm.model包路径下的所有类都进行关系对象映射。

下面是Model类:

/**
 * @Entity
 * value代表生成的集合名称、不写默认为类名(此处设置为与类名相同)
 * noClassnameStored如果设置为false,文档中会生成一列className保存类名("className":"mongodb.orm.model.OrmColl")
 */
@Entity(value="OrmColl",noClassnameStored = true)
/**
 * @Indexes
 * 创建复合索引(用,分隔).
 * 此处设置userId升序,name降序.
 * unique:设置为唯一,无法插入重复值.
 * dropDups:当为某个字段创建唯一索引时,删除其他相同值的记录。只保留第一条记录.
 *          true-删除重复,false-不删除重复(当有重复值时唯一索引创建失败),默认为false.
 * @Indexes({@Index(...),@Index(...)})多个索引加{}并用,分隔.
 * Datastore的ensureIndexes调用时才会创建索引.
 */
@Indexes(@Index(value="userId,-name",unique=true,dropDups=false))
public class OrmColl{
	@Id 
	private ObjectId id;//Id
	
	private long userId;
	private String name;
	
	private int age;
	private Date createDate = new Date();
	@Property("male")//该属性存储到collection中元素名指定为male
	private boolean sex;
	
	/**
	 * concreteClass:为接口指名实现类,默认
	 * java.util.ArrayList for List 
	 * java.util.HashSet for Set 
	 * java.util.HashMap for Map
	 * 此处为List指明实现类为java.util.Vector
	 */
	@Property(concreteClass=java.util.Vector.class)
	public List<String> list;//添加范型指名list类型,否则出现警告
	
	@Embedded(value="embedded_doc")
	private InnerDocument doc;//嵌入文档,默认为属性名.此处指定了元素名称.
	
	@Transient
	private int transientValue;//设置为@Transient不会被持久化
	@Serialized
	private int serializedValue;//序列化
	
	@Version
	Long version;//乐观锁
	
	@Reference
	private RefOrmColl refOrmColl;//自动生成名为refOrmColl的内嵌文档,包含"$ref"和"$id"属性,指明引用集合名及@Id属性值.
	
    @Reference
	private List<RefOrmColl> refList;//自动生成名为refList的数组,数组的每个元素都是内嵌文档,文档形式同refOrmColl.
	
	public OrmColl(){}
	
	@PrePersist 
	private void prePersist() {createDate = new Date();}

        //setter & getter
}

补充@Entity(concern = "SAFE")属性(粘贴自文档):

NONE: No exceptions are raised, even for network issues

NORMAL: Exceptions are raised for network issues, but not server errors

SAFE: Exceptions are raised for network issues, and server errors; waits on a server for the write operation

FSYNC_SAFE: Exceptions are raised for network issues, and server errors and the write operation waits for the server to flush the data to disk

REPLICAS_SAFE: Exceptions are raised for network issues, and server errors; waits for at least 2 servers for the write operation 

其中version属性不需要getter和setter方法,内嵌对象为InnerDocument。代码如下:

 

/**
 * 被@Embedded注解的类不允许有@Id
 */
@Embedded
public class InnerDocument {
	private String type;
	private long longValue;
	private Date createDate;
	
	public InnerDocument(){}
	
	@PrePersist 
	private void prePersist() {createDate = new Date();}
        
        //setter & getter...
}

引用对象为RefOrmColl.java,格式如下:

 

@Entity(value="RefOrmColl",noClassnameStored = true)
public class RefOrmColl {
	@Id
	private ObjectId id;//被关联的集合一定要包含@Id属性
	private long longValue;
	private String type;
	private Date date;
	
	@PrePersist 
	private void prePersist() {date = new Date();}
      
       //setter&getter...

}

在OrmColl的集合中自动生成RefOrmColl类型对象属性名的内嵌文档或数组(本例中是refOrmColl和refList),包含"$ref"和"$id"属性,以指明引用集合名及@Id属性值. 

 

DAO实现类:

OrmCollDao.java如下:

 

public class OrmCollDao extends AbstractMongoDAO<OrmColl,ObjectId>{//OrmColl主键是OjectId
	
	private static OrmCollDao childDaoInstance = createDAOInstance();
	
	protected static DefaultMorphiaMongoDAO<RefOrmColl> refDao = DefaultMorphiaMongoDAO.getInstance(RefOrmColl.class); 
	
	private static OrmCollDao createDAOInstance(){
		return new OrmCollDao(OrmColl.class,morphiaDs);
	}
	
	protected OrmCollDao(Class<OrmColl> entityClass,Datastore ds){
		super(entityClass,ds);
	}
	
	protected OrmCollDao(Class<OrmColl> entityClass, Mongo mongo, Morphia morphia, String dbName){
		super(entityClass,mongo,morphia,dbName);
	}
	
	public static OrmCollDao getInstance(){
		return childDaoInstance;
	}
	
	/**
	 * 数据初始化
	 */
	public static void insertRefOrmColl(){
		List list = new ArrayList();
		for(int i=0;i<100;i++){
			RefOrmColl roc = new RefOrmColl();
			roc.setLongValue(i);
			roc.setType("type"+i);
			list.add(roc);
		}
		//insert可以批量插入list,save只能单个插入,但save可以更新.
		refDao.insert(list);
	}
	
	/**
	 * 数据初始化
	 */
	public static void insertOrmColl(){
		//List<OrmColl> ormList = new ArrayList<OrmColl>();
		//查询一个关联集合的List
		Query<RefOrmColl> listQuery = refDao.createQuery().field("longValue").lessThan(10);
		List<RefOrmColl> refList = refDao.find(listQuery).asList();
		for(int i=0;i<100;i++){
			OrmColl oc = new OrmColl();
			oc.setAge(1+i);
			oc.setName("name"+i);
			oc.setUserId(10000+i);
			oc.setSex(true);
			oc.setTransientValue(12345);
			oc.setSerializedValue(1234567);
			InnerDocument doc = new InnerDocument();//内嵌文档
			doc.setType("type_str");
			doc.setLongValue(1986L);
			oc.setDoc(doc);
			Query<RefOrmColl> query = refDao.createQuery().field("longValue").equal(i);
			RefOrmColl refColl = refDao.findOne(query);//查询关联集合的文档
			if(refColl!=null){
				//设置关联
				oc.setRefOrmColl(refColl);
				oc.setRefList(refList);
			}
			OrmCollDao.getInstance().save(oc);//使用save方法会自动生成@version属性
			//ormList.add(oc);
		}
		//OrmCollDao.getInstance().insert(ormList);//使用批量插入,文档中不会自动生成@version属性.
	}
	
	/**
	 * 根据id加载对象
	 */
	public static void get(String _id){
		OrmCollDao dao = OrmCollDao.getInstance();
		OrmColl obj = dao.get(new ObjectId(_id));
		displayObj(obj);
	}
	
	/**
	 * filter查询方式(直接使用 > < >= <= = exists等表达式)
	 */
	public static void findByFilter(){
		OrmCollDao dao = OrmCollDao.getInstance();
		//filter之间以and连接
		Query query = dao.createQuery().filter("age >",1);
		query.filter("userId <=", 10010);
		query.filter("male =", true);
		query.filter("embedded_doc.type =", "type_str");
		query.filter("refList exists ", true);
		List<OrmColl> list = dao.find(query).asList();
		System.out.println("list.size:"+list.size());
		for(int i=0;i<list.size();i++){
			displayObj(list.get(i));
		}
	}
	
	/**
	 * Fluent查询方式(使用morphia封装的方法greaterThan、lessThanOrEq、equal、exists)
	 */
	public static void findByFluent(){
		OrmCollDao dao = OrmCollDao.getInstance();
		Query query = dao.createQuery();
		//不同条件之间为and
		query.field("age").greaterThan(1);
		query.field("userId").lessThanOrEq(10010);
		query.field("male").equal(true);
		query.field("embedded_doc.type").equal("type_str");
		query.field("refList").exists();//doesNotExist
		List<OrmColl> list = dao.find(query).asList();
		System.out.println("list.size:"+list.size());
		for(int i=0;i<list.size();i++){
			displayObj(list.get(i));
		}
	}
	
	/**
	 * Fluent查询方式(根据id查询文档)
	 */
	public static void findById(String _id){
		OrmCollDao dao = OrmCollDao.getInstance();
		Query query = dao.createQuery();
		query.field(Mapper.ID_KEY).equal(new ObjectId(_id));//Mapper.ID_KEY == "_id"
		OrmColl obj = dao.findOne(query);
		displayObj(obj);
	}
	
	/**
	 * Fluent查询方式 ,OR条件查询
	 */
	public static void orQuery(){
		OrmCollDao dao = OrmCollDao.getInstance();
		Query<OrmColl> query = dao.createQuery();
		query.or(
			query.criteria("userId").equal(10010),
			query.criteria("age").greaterThan(1)
		);
		List<OrmColl> list = dao.find(query).asList();
		System.out.println("list.size:"+list.size());
		for(int i=0;i<list.size();i++){
			displayObj(list.get(i));
		}
	}
	
	/**
	 * Count求和
	 */
	public static void getCount(){
		OrmCollDao dao = OrmCollDao.getInstance();
		Query query = dao.createQuery().field("userId").greaterThanOrEq(10095);
		long count = dao.count(query);
		System.out.println(count);
	}
	
	/**
	 * 更新操作
	 */
	public static void updateEntity(String _id){
		OrmCollDao dao = OrmCollDao.getInstance();
		UpdateOperations<OrmColl> ops = dao.createUpdateOperations().set("createDate",new Date()).set("serializedValue",12345);
	    UpdateResults<OrmColl> ur = dao.update(dao.createQuery().field(Mapper.ID_KEY).equal(new ObjectId(_id)),ops);
	    System.out.println(ur.getInsertedCount());
	}
	
	/**
	 * 对象转换并打印对象属性
	 */
	public static void displayObj(OrmColl obj){
		Morphia morphia = getMorphia();
		DBObject object = morphia.toDBObject(obj);//将对象转换成DBObject.
		System.out.println(object.toString());
	}
	
	/**
	 * Mapper and EntityCache
	 */
	public static void mapper(String _id){
		OrmCollDao dao = OrmCollDao.getInstance();
		OrmColl oc = dao.get(new ObjectId(_id));
		
		Mapper mapper = morphia.getMapper();
		
		if(mapper!=null && morphia.isMapped(OrmColl.class)){
			System.out.println("getCollectionName:"+mapper.getCollectionName(oc));
		    System.out.println("Id:"+mapper.getId(oc));
		    System.out.println("Key:"+mapper.getKey(oc));
		    System.out.println("keyToRef:"+mapper.keyToRef(mapper.getKey(oc)));
		    System.out.println("Options:"+mapper.getOptions());
		}
		
		EntityCache cache = mapper.createEntityCache();
		cache.putEntity(mapper.getKey(oc),oc);
		
		System.out.println("cache obj 1: "+cache.getEntity(mapper.getKey(oc)).getName());
		System.out.println("cache obj 2: "+cache.getEntity(mapper.getKey(oc)).getName());
		System.out.println("cache obj exists: "+cache.exists(mapper.getKey(oc)));
		
		EntityCacheStatistics ecs = cache.stats();
		System.out.println("EntityCacheStatistics1: "+ecs.toString());//cache统计1: 1 entities,3 hits
		
		cache.putProxy(mapper.getKey(oc),oc);
		System.out.println("cache proxy: "+cache.getProxy(mapper.getKey(oc)).getName());
		
		ecs = cache.stats();
		System.out.println("EntityCacheStatistics2: "+ecs.toString());//cache统计2: 2 entities,4 hits
		
		//所有已经映射的class
		for (MappedClass mc : mapper.getMappedClasses()) {
            System.out.println("getMappedClasses: " + mc);
        }
		for (EntityInterceptor ei : mapper.getInterceptors()) {
            System.out.println("EntityInterceptor: " + ei);
        }
		
	}
}

以上是针对OrmColl的集合建立的DAO,由于使用了泛型,因此find、update等操作都针对OrmColl,所以如果你需要在DAO中同时对其它集合进行操作的话,可以使用以下通用的DAO,DefaultMorphiaMongoDAO.java:

 

public class DefaultMorphiaMongoDAO<T> extends AbstractMongoDAO<T, ObjectId> {
	private static final Map<String,DefaultMorphiaMongoDAO> _instanceMap = new ConcurrentHashMap<String,DefaultMorphiaMongoDAO>();
	
	protected DefaultMorphiaMongoDAO(Class<T> entityClass, Datastore ds) {
		super(entityClass,ds);
		this.ensureIndexes();
	}
	
	public static DB getDB(){
		return morphiaDs.getDB();
	}
	
	public static <T> DefaultMorphiaMongoDAO<T> getInstance(Class<T> clazz){
		DefaultMorphiaMongoDAO inst = _instanceMap.get(clazz.getSimpleName());
		if (inst==null) {
			inst = new DefaultMorphiaMongoDAO<T>(clazz,morphiaDs);
			_instanceMap.put(clazz.getSimpleName(),inst);
		}
		return inst;
	}

}

 通过泛型,你就可以用该DAO对任何Collection进行操作了,(注意,此处代码,<T, ObjectId>可以支持不同的Key类型,不一定是ObjectId)。

 

至此,Morphia的学习算是告一段落了。

其实在遇到很多问题的时候要多看文档,下面列出一些Morphia的参考资料,希望对读者有所帮助:

1.morphia官方用户手册: http://code.google.com/p/morphia/wiki/GettingStarted

2.morphia在线API文档地址:http://morphia.googlecode.com/svn/site/morphia/apidocs/index.html 

 

分享到:
评论
2 楼 shensy 2013-10-29  
kingzhe123 写道
DatabaseProject
代码里的这个是啥对象方法呀?或者是哪个包提供的呀,我的morphia里没有的哇


在我blog里面搜索DatabaseProject即可找到。
1 楼 kingzhe123 2013-10-18  
DatabaseProject
代码里的这个是啥对象方法呀?或者是哪个包提供的呀,我的morphia里没有的哇

相关推荐

Global site tag (gtag.js) - Google Analytics