隨筆 - 22, 文章 - 0, 評論 - 1, 引用 - 0
          數據加載中……

          mongodb的morphia框架學習筆記

          快速開始:
          @Entity
          public class Hotel {
               @Id private ObjectId id;
              private String name;
              private int stars;
               @Embedded
              private Address address;
          }
          @Embedded
          public class Address {
              private String street;
              private String city;
              private String postCode;
              private String country;
          }
          main方法:
          Mongo mongo=new Mongo();
          Morphia morphia = new Morphia();
          morphia.map(Hotel.class).map(Address.class);
          Datastore ds = morphia.createDatastore(mongo, "my_database");
          //保存
          ds.save(hotelToSave);
          //查詢四星級酒店
          //List<Hotel> fourStarHotels = ds.find(Hotel.class, "stars >=", 4).asList();
          List<Hotel> fourStarHotels = ds.find(Hotel.class).field("stars").greaterThanOrEq(4).asList();
          -------------------------------------------------
          Datastore創建后index和capped collections的初始化:
          ds.ensureIndexes(); ///creates all defined with @Indexed
          ds.ensureCaps(); //creates all collections for @Entity(cap=@CappedAt(...))
          這兩個方法應該在你把實體注冊到morphia之后(morphia.map(Hotel.class).map(Address.class))來調用,它默認會同步的創建index和capped collections,這可以在每次系統啟動時確保索引存在(包括第一次運行的時候)。
          查詢一條記錄:
          MyEntity e = ds.find(MyEntity.class).get(); //get the first one by type
          MyEntity e = ds.find(MyEntity.class).field("name").equal("someName").get(); //get the first one where name = "someName"
          -------------------------------------------------
          Datastore接口介紹:
          -------------------------
          get方法:其實就是find方法的快捷方式,根據ID返回一個實體,或者返回null。
          Hotel hotel = ds.get(Hotel.class, hotelId);
          -------------------------
          find方法:會返回一個Query對象,支持迭代器和QueryResults接口。
          //use in a loop
          for(Hotel hotel : ds.find(Hotel.class, "stars >", 3))
             print(hotel);
          //get back as a list
          List<Hotel> hotels = ds.find(Hotel.class, "stars >", 3).asList();
          //sort the results
          List<Hotel> hotels = ds.find(Hotel.class, "stars >", 3).sort("-stars").asList();
          //get the first matching hotel, by querying with a limit(1)
          Hotel gsHotel = ds.find(Hotel.class, "name", "Grand Sierra").get();
          //same as
          Hotel gsHotel = ds.find(Hotel.class, "name =", "Grand Sierra").get();
          可用的操作符列表: ["=", "==","!=", "<>", ">", "<", ">=", "<=", "in", "nin", "all", "size", "exists"]
          -------------------------
          save方法:
          Hotel hotel = new Hotel();
          ds.save(hotel);
          //@Id field is filled in for you (after the save), if you didn't set it.
          ObjectId id = hotel.getId();
          -------------------------
          delete方法:根據一個ID或者查詢對象來刪除
          ds.delete(Hotel.class, "Grand Sierra Resort");
          //use a query
          ds.delete(ds.createQuery(Hotel.class).filter("pendingDelete", true));
          -------------------------
          FindAndDelete方法:刪除并返回記錄的原子操作
          Hotel grandSierra = ds.findAndDelete(ds.get(Hotel.class, "Grand Sierra Resort"));
          -------------------------
          Update方法:更新部分信息時比整體save一次要高效的多,例如更新用戶的最后登錄時間:
          @Entity
          class User
          {
             @Id private ObjectId id;
             private long lastLogin;
             //... other members
             private Query<User> queryToFindMe()
             {
                return datastore.createQuery(User.class).field(Mapper.ID_KEY).equal(id);
             }
             public void loggedIn()
             {
                long now = System.currentTimeMillis();
                UpdateOperations<User> ops = datastore.createUpdateOperations(User.class).set("lastLogin", now);
                ds.update(queryToFindMe(), ops);
                lastLogin = now;
             }
          }
          有關update方法的更多高級操作:
          http://code.google.com/p/morphia/wiki/Updating
          -------------------------------------------------
          Query接口介紹:
          可以添加查詢條件,排序,限定返回結果條數和位置。實現了QueryResults接口。
          -------------------------
          Filter方法:
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12);
          第一個參數是屬性名和比較符,第二個參數是比較值。
          多個fileter直接的關系是“與(and)”
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).filter("foo <", 30);
          可用操作符列表:
          ["=", "==","!=", "<>", ">", "<", ">=", "<=", "in", "nin", "all", "size", "exists"]
          -------------------------
          Fluent接口:
          與Fileter接口差不多,英文呢方法名,更符合使用習慣:
          Query q = ds.createQuery(MyEntity.class).field("foo").equal(1);
          可用方法列表:
          [exists,doesNotExist,greaterThan,greaterThanOrEq,lessThan,lessThanOrEq,equal,notEqual,hasThisOne,hasAllOf,hasAnyOf,hasNoneOf,hasThisElement,sizeEq]
          使用Fluent接口,可以使用or條件:
          Query<Person> q = ad.createQuery(Person.class);
          q.or(
                  q.criteria("firstName").equal("scott"),
                  q.criteria("lastName").equal("scott")
          );
          -------------------------
          Geo-spatial:mongoDB的地理位置操作
          -------------------------
          Fields方法:
          類似與mongo本身的query,可以用“點號”指定屬性。
          Query q = ds.createQuery(Person.class).field("addresses.city").equal("San Francisco");
          //or with filter, or with this helper method
          Query q = ds.find(Person.class, "addresses.city", "San Francisco");
          -------------------------
          Validation:如果查詢時屬性名找不到,則會拋出異常。如果用到“點號”指定屬性,則表達式每一部分都必須匹配類的結構圖。
          如果服務器可以強制轉換數據,則數據類型的錯誤會被作為警告記錄下來。
          上述校驗特性可以在任何一個查詢或查詢的一部分處被關閉:
          Query q = ds.createQuery(MyEntity.class).disableValidation();
          //or it can be disabled for just one filter
          Query q = ds.createQuery(MyEntity.class).disableValidation().filter("someOldField", value).enableValidation().filter("realField", otherVal);
          -------------------------
          Sort方法:排序
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).order("dateAdded");
          ... // desc order
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).order("-dateAdded");
          ... // asc dateAdded, desc foo
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).order("dateAdded, -foo");
          -------------------------
          Limit方法:
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).limit(100);
          -------------------------
          offset(skip)方法:要求服務器跳過一部分記錄,這么做效率不如使用一些屬性的range filter,比如pagination。
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).offset(1000);
          -------------------------
          只返回指定的屬性:這么做會導致不完整的對象,小心使用。
          MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo").get();
          val = e.getFoo(); // 僅返回這個字段
          MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(false, "foo").get();
          val = e.getFoo(); // 僅不返回這個字段
          指定字段名的參數可以是字符串list或者數組:
          MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo", "bar").get();
          -------------------------
          取數據:
          使用QueryResults接口的方法即可取到數據,這些方法不會影響Query對象本身(返回的是副本),所以可以反復調用。
          get()     使用limit(1),返回一條記錄
          asList()     返回所有記錄,大數據量時可能較慢
          fetch()     明確要求返回一個可迭代的對象
          asKeyList()     返回記錄的Key<T>,只從server取id字段
          fetchEmptyEntities()     與fetch類似,但只取id字段
          Query q = ds.createQuery(MyEntity.class).filter("foo >", 12);
          MyEntity e = q.get();
          e = q.sort("foo").get();
          for (MyEntity e : q)  print(e);
          List<MyEntity> entities = q.asList();
          -------------------------------------------------
          生命周期方法注解:
          @PrePersist - 在save之前調用,可以返回一個DBObject以替代一個空值
          @PostPersist - 在save之后調用
          @PreLoad - 在將數據映射到POJO之前調用,DBObject作為參數傳入,你可以手動修改其中的值
          @PostLoad - 在將數據映射到POJO之后調用
          測試類例子:
          http://code.google.com/p/morphia/source/browse/trunk/morphia/src/test/java/com/google/code/morphia/TestDatastore.java
          //這個例子保存前將最后登錄時間更新為當前時間
          class BankAccount {
            @Id String id;
            Date lastUpdated = new Date();
            @PrePersist void prePersist() {lastUpdated = new Date();}
          }
          下面的這個例子中,通過EntityListerners注解,將生命周期事件的實現放到外部類當中:
          @EntityListeners(BackAccountWatcher.class)
          class BankAccount {
            @Id String id;
            Date lastUpdated = new Date();
          }
          class BankAccountWatcher{
            @PrePersist void prePersist(BankAccount act) {act.lastUpdated = new Date();}
          }
          注意:沒有delete操作相關的生命周期事件。
          -------------------------------------------------
          類注解:
          -------------------------
          @Entity
          標記一個要被映射的類。此注解為可選,一般寫上沒壞處。
          @Entity("collectionName")
          指定對應的collection的名稱,這種情況POJO里必須有無參構造方法
          @Entity(noClassnameStored = true)
          注解不要存儲類名。
          默認是存儲類名的,因為可以把不同的類(特別是相互繼承的類)存入同一個collection,例如:
          @Entity("animals") abstract class Animal { String name; }
          @Entity("animals") Cat extends Animal { ... }
          @Entity("animals") Dog extends Animal { ... }
          @Entity(cap = @CappedAt(...)) 指定為Capped
          -------------------------
          @Indexes 索引注解可以標記在類上,這樣就可以建立多列索引
          @Entity
          @Indexes( @Index("user, -date") )
          public class ChangeLog{
          Date date;
          String user;
          Record changedRecord;
          }
          注:"date"前面的負號表示日期降序排列,便于查找最近的用戶
          還可以添加多個多列索引:
          @Indexes({
             @Index("user, -cs"),
             @Index("changedRecord, -cs")})
          注:也可以(但不建議)在這里定義單列索引,但最好還是注解在具體的列上
          -------------------------------------------------
          字段注解:
          -------------------------
          @Id 標記字段為數據庫主鍵
          POJO里的對應字段可以是任意支持持久化的類型,例如int,uuid,Object
          使用ObjectId可以實現自動生成,否則在每次存儲前需要指定此值
          -------------------------
          @Indexed  注解索引
          每次調用datastore.ensureIndexes()時,這些索引就會被應用。
          注:如果在一個已經存在數據和索引的系統上,調用datastore.ensureIndexes()方法,將不會產生任何操作,所以可以放心的調用。
          @Indexed(value=IndexDirection.ASC, name="upc", unique=true, dropDups=true)
          value:索引方向,默認是IndexDirection.ASC,還可以是IndexDirection.DESC和IndexDirection.BOTH
          name:索引名,默認是mongoDB自動生成
          unique:是否唯一索引,默認為flase
          dropDups:通知唯一索引靜默刪除已有的重復元素,只保留第一個,默認為false
          -------------------------
          @Embedded 注解需要嵌入的對象(形成一個對象樹來讀取/寫入)
          被嵌入的對象將嵌套在父對象里,被存入同一個collection中,所以被嵌入的對象里不允許出現@Id注解。
          也可以指定被嵌入對象在mongoDB里的屬性名:
          @Embedded("blog_comments")
          -------------------------
          @Property POJO屬性注解
          在POJO中,java原生類型和基本類型默認是不需要注解即可完成讀取和寫入的(除非注解了@Transient)。
          mongoDB只支持四種數據類型:Integer,Long,Double,String
          morphia會自動映射java基本數據類型和String,這些類型的數組,以及List,Set,Map到mongoDB中。
          另外,以下對象也會被自動轉換和讀取/寫入:
          enum 轉為String存儲
          java.util.Date 轉為毫秒數存儲
          java.util.Locale 轉為String存儲
          com.mongodb.DBRef
          com.mongodb.ObjectId
          注:morphia默認使用POJO屬性名作為collection里的字段名,這個行為可以被覆蓋:
          @Property("my_integer")
          private int myInt;
          -------------------------
          @Reference 引用注解,標記某一個字段存在另一個collection中。
          該注解有三種屬性:
          lazy:懶漢模式,被調用時才從數據庫獲取此字段
          ignoreMissing:讀取引用失敗時不產生異常
          concreteClass:產生的實例的類類型
          例子:
          @Entity public class BlogEntry { @Id private ObjectId id; @Reference private Author author;}
          @Entity public class Author { @Id private ObjectId id;}
          注:被引用的對象必須存在于mongoDB中,然后才能存儲引用其的對象。
          注:morphia默認使用POJO里引用的屬性名作為collection里的字段名,這個行為可以被覆蓋:
          @Reference("blog_authors")
          private List<Author> authors;
          -------------------------
          集合類相關的屬性注解:
          morphia支持Collection(List,Set,Map):
          private Set<String> tags;
          private Map<String,Translation> translations;
          @Reference private List<Article> relatedArticles;
          默認情況下,morphia讀取數據創建實例時會使用以下實現類:
          java.util.ArrayList
          java.util.HashSet
          java.util.HashMap
          這個行為可以被覆蓋:
          @Property(concreteClass = java.util.TreeSet.class)
          private Set<String> tags;
          @Embedded(concreteClass = java.util.TreeMap.class)
          private Map<String,Translation> translations;
          @Reference(concreteClass = java.util.Vector.class)
          private List<Article> relatedArticles;
          -------------------------
          @Transient 標記字段被忽略,包括讀取/寫入
          @Serialized 字段被轉為二進制后儲存
          @NotSaved 字段可以被讀取,但在寫入時忽略
          @AlsoLoad 字段可以被任何支持的名字所讀取
          @Version 版本號標記,自動實現樂觀鎖,標記后修改操作時可能會拋出ConcurrentModificationException
          -------------------------------------------------
          Morphia的擴展:
          -------------------------
          校驗擴展ValidationExtension:
          符合JSR303,可以直接調用Hibernate validation。
          對JSR303 API的輕量級的包裝:new ValidationExtension(morphia);
          import org.hibernate.validator.constraints.Email;
          @Entity
          public class Userlike {
                  @Id ObjectId id;
                  @Email String email;
          }
          -------------------------
          日志重定向擴展:SLF4JExtension
          將morphia的運行日志重定向到SLF4J中,引入morphia-logging-slf4j-0.99.jar,在系統啟動時執行一次:
          MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class);
          -------------------------------------------------
          手動映射對象到DBObjects(以便傳遞給底層的driver):
          -------------------------
          創建Morphia實例(建議只創建一次,然后重用它):
          Morphia morphia = new Morphia();
          morphia.map(BlogEntry.class).map(Author.class);
          每一個這樣手動映射的類都會被檢查,如果失敗會拋出MappingException。
          也可以讓morphia自動掃描某個包,并自動映射:
          morphia.mapPackage("my.package.with.only.mongo.entities");
          -------------------------
          高級使用:從morphia里直接調用底層的java driver存儲數據(手動):
          Morphia morphia = ...;
          Mongo mongo = ...;
          DB db = mongo.getDB("BlogSite");
          //這是注解過的POJO
          BlogEntry blogEntry = ...;
          //讓morphia將POJO轉為java driver需要的DBObject
          DBObject blogEntryDbObj = morphia.toDBObject(blogEntry);
          //用java driver將其寫入mongodb
          db.getCollection("BlogEntries").save(blogEntryDbObj);
          -------------------------
          高級使用:從morphia里直接調用底層的java driver讀取數據(手動):
          Morphia morphia = ...;
          Mongo mongo = ...;
          DB db = mongo.getDB("BlogSite");
          //要讀取的ID
          String blogEntryId = ...;
          //調用java driver從mongdoDB取出一個DBObject
          BasicDBObject blogEntryDbObj = (BasicDBObject) db.getCollection("BlogEntries").findOne(new BasicDBObject("_id", new ObjectId(blogEntryId));
          //讓morphia將DBObject轉為POJO
          BlogEntry blogEntry = morphia.fromDBObject(BlogEntry.class, blogEntryDbObj);
          -------------------------------------------------
          DAO層的封裝:
          morphia已經提供了一個DAO接口和一個BasicDAO類。
          我們只要繼承BasicDAO類,覆蓋其中的構造方法,將mongo和morphia對象傳入即可:
          public class BlogEntryDAO extends BasicDAO<BlogEntry, ObjectId> {
              public BlogEntryDAO( Morphia morphia, Mongo mongo,String dbName) {
                  super(mongo, morphia, dbName);
              }
          }
          然后就可以實現大部分操作:
          BlogEntryDAO blogEntryDAO = new BlogEntryDAO(...);
          ObjectId  blogEntryId = ...;
          BlogEntry myBlogEntry = blogEntryDAO.get(blogEntryId);//查詢
          myBlogEntry.setTitle("My Blog Entry");
          blogEntryDAO.save(myBlogEntry);//保存
          blogEntryDAO.deleteById(myBlogEntry.getId());//刪除
          然后還需要自定義一些查詢方法(注意這里用了正則匹配文章標題):
          public List<BlogEntry> findByTitle( String title ) {
              Pattern regExp = Pattern.compile(title + ".*", Pattern.CASE_INSENSITIVE);
              return ds.find(entityClazz).filter("title", regExp).sort("title").asList();
          }
          星游注:話說,加入DAO層不就是為了與morphia解耦么?現在DAO層的父類本身就在morphia包里,“這不科學呀。。。”
          建議參照其BasicDAO,自己寫一個,這才實現了與持久層解耦:
          http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/dao/BasicDAO.java

          posted on 2012-09-20 00:02 王星游 閱讀(13892) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 琼结县| 大埔县| 交城县| 肃北| 阳西县| 宿迁市| 洪泽县| 聊城市| 龙口市| 天镇县| 色达县| 兰溪市| 阿拉善右旗| 太康县| 诸暨市| 库车县| 永春县| 都兰县| 新昌县| 云南省| 普兰县| 浮山县| 永城市| 碌曲县| 呼伦贝尔市| 万州区| 精河县| 迁安市| 新疆| 泰来县| 云浮市| 金川县| 普安县| 镇远县| 天全县| 卢氏县| 闸北区| 托里县| 灌阳县| 盘锦市| 固原市|