首先介绍抽象类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)。