<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 22, 文章 - 0, 評論 - 1, 引用 - 0
    數(shù)據(jù)加載中……

    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創(chuàng)建后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))來調(diào)用,它默認會同步的創(chuàng)建index和capped collections,這可以在每次系統(tǒng)啟動時確保索引存在(包括第一次運行的時候)。
    查詢一條記錄:
    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方法的快捷方式,根據(jù)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方法:根據(jù)一個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;
       }
    }
    有關(guān)update方法的更多高級操作:
    http://code.google.com/p/morphia/wiki/Updating
    -------------------------------------------------
    Query接口介紹:
    可以添加查詢條件,排序,限定返回結(jié)果條數(shù)和位置。實現(xiàn)了QueryResults接口。
    -------------------------
    Filter方法:
    Query q = ds.createQuery(MyEntity.class).filter("foo >", 12);
    第一個參數(shù)是屬性名和比較符,第二個參數(shù)是比較值。
    多個fileter直接的關(guān)系是“與(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:如果查詢時屬性名找不到,則會拋出異常。如果用到“點號”指定屬性,則表達式每一部分都必須匹配類的結(jié)構(gòu)圖。
    如果服務器可以強制轉(zhuǎn)換數(shù)據(jù),則數(shù)據(jù)類型的錯誤會被作為警告記錄下來。
    上述校驗特性可以在任何一個查詢或查詢的一部分處被關(guān)閉:
    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(); // 僅不返回這個字段
    指定字段名的參數(shù)可以是字符串list或者數(shù)組:
    MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo", "bar").get();
    -------------------------
    取數(shù)據(jù):
    使用QueryResults接口的方法即可取到數(shù)據(jù),這些方法不會影響Query對象本身(返回的是副本),所以可以反復調(diào)用。
    get()     使用limit(1),返回一條記錄
    asList()     返回所有記錄,大數(shù)據(jù)量時可能較慢
    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之前調(diào)用,可以返回一個DBObject以替代一個空值
    @PostPersist - 在save之后調(diào)用
    @PreLoad - 在將數(shù)據(jù)映射到POJO之前調(diào)用,DBObject作為參數(shù)傳入,你可以手動修改其中的值
    @PostLoad - 在將數(shù)據(jù)映射到POJO之后調(diào)用
    測試類例子:
    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注解,將生命周期事件的實現(xiàn)放到外部類當中:
    @EntityListeners(BackAccountWatcher.class)
    class BankAccount {
      @Id String id;
      Date lastUpdated = new Date();
    }
    class BankAccountWatcher{
      @PrePersist void prePersist(BankAccount act) {act.lastUpdated = new Date();}
    }
    注意:沒有delete操作相關(guān)的生命周期事件。
    -------------------------------------------------
    類注解:
    -------------------------
    @Entity
    標記一個要被映射的類。此注解為可選,一般寫上沒壞處。
    @Entity("collectionName")
    指定對應的collection的名稱,這種情況POJO里必須有無參構(gòu)造方法
    @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 標記字段為數(shù)據(jù)庫主鍵
    POJO里的對應字段可以是任意支持持久化的類型,例如int,uuid,Object
    使用ObjectId可以實現(xiàn)自動生成,否則在每次存儲前需要指定此值
    -------------------------
    @Indexed  注解索引
    每次調(diào)用datastore.ensureIndexes()時,這些索引就會被應用。
    注:如果在一個已經(jīng)存在數(shù)據(jù)和索引的系統(tǒng)上,調(diào)用datastore.ensureIndexes()方法,將不會產(chǎn)生任何操作,所以可以放心的調(diào)用。
    @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中,所以被嵌入的對象里不允許出現(xiàn)@Id注解。
    也可以指定被嵌入對象在mongoDB里的屬性名:
    @Embedded("blog_comments")
    -------------------------
    @Property POJO屬性注解
    在POJO中,java原生類型和基本類型默認是不需要注解即可完成讀取和寫入的(除非注解了@Transient)。
    mongoDB只支持四種數(shù)據(jù)類型:Integer,Long,Double,String
    morphia會自動映射java基本數(shù)據(jù)類型和String,這些類型的數(shù)組,以及List,Set,Map到mongoDB中。
    另外,以下對象也會被自動轉(zhuǎn)換和讀取/寫入:
    enum 轉(zhuǎn)為String存儲
    java.util.Date 轉(zhuǎn)為毫秒數(shù)存儲
    java.util.Locale 轉(zhuǎn)為String存儲
    com.mongodb.DBRef
    com.mongodb.ObjectId
    注:morphia默認使用POJO屬性名作為collection里的字段名,這個行為可以被覆蓋:
    @Property("my_integer")
    private int myInt;
    -------------------------
    @Reference 引用注解,標記某一個字段存在另一個collection中。
    該注解有三種屬性:
    lazy:懶漢模式,被調(diào)用時才從數(shù)據(jù)庫獲取此字段
    ignoreMissing:讀取引用失敗時不產(chǎn)生異常
    concreteClass:產(chǎn)生的實例的類類型
    例子:
    @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;
    -------------------------
    集合類相關(guān)的屬性注解:
    morphia支持Collection(List,Set,Map):
    private Set<String> tags;
    private Map<String,Translation> translations;
    @Reference private List<Article> relatedArticles;
    默認情況下,morphia讀取數(shù)據(jù)創(chuàng)建實例時會使用以下實現(xiàn)類:
    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 字段被轉(zhuǎn)為二進制后儲存
    @NotSaved 字段可以被讀取,但在寫入時忽略
    @AlsoLoad 字段可以被任何支持的名字所讀取
    @Version 版本號標記,自動實現(xiàn)樂觀鎖,標記后修改操作時可能會拋出ConcurrentModificationException
    -------------------------------------------------
    Morphia的擴展:
    -------------------------
    校驗擴展ValidationExtension:
    符合JSR303,可以直接調(diào)用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,在系統(tǒng)啟動時執(zhí)行一次:
    MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class);
    -------------------------------------------------
    手動映射對象到DBObjects(以便傳遞給底層的driver):
    -------------------------
    創(chuàng)建Morphia實例(建議只創(chuàng)建一次,然后重用它):
    Morphia morphia = new Morphia();
    morphia.map(BlogEntry.class).map(Author.class);
    每一個這樣手動映射的類都會被檢查,如果失敗會拋出MappingException。
    也可以讓morphia自動掃描某個包,并自動映射:
    morphia.mapPackage("my.package.with.only.mongo.entities");
    -------------------------
    高級使用:從morphia里直接調(diào)用底層的java driver存儲數(shù)據(jù)(手動):
    Morphia morphia = ...;
    Mongo mongo = ...;
    DB db = mongo.getDB("BlogSite");
    //這是注解過的POJO
    BlogEntry blogEntry = ...;
    //讓morphia將POJO轉(zhuǎn)為java driver需要的DBObject
    DBObject blogEntryDbObj = morphia.toDBObject(blogEntry);
    //用java driver將其寫入mongodb
    db.getCollection("BlogEntries").save(blogEntryDbObj);
    -------------------------
    高級使用:從morphia里直接調(diào)用底層的java driver讀取數(shù)據(jù)(手動):
    Morphia morphia = ...;
    Mongo mongo = ...;
    DB db = mongo.getDB("BlogSite");
    //要讀取的ID
    String blogEntryId = ...;
    //調(diào)用java driver從mongdoDB取出一個DBObject
    BasicDBObject blogEntryDbObj = (BasicDBObject) db.getCollection("BlogEntries").findOne(new BasicDBObject("_id", new ObjectId(blogEntryId));
    //讓morphia將DBObject轉(zhuǎn)為POJO
    BlogEntry blogEntry = morphia.fromDBObject(BlogEntry.class, blogEntryDbObj);
    -------------------------------------------------
    DAO層的封裝:
    morphia已經(jīng)提供了一個DAO接口和一個BasicDAO類。
    我們只要繼承BasicDAO類,覆蓋其中的構(gòu)造方法,將mongo和morphia對象傳入即可:
    public class BlogEntryDAO extends BasicDAO<BlogEntry, ObjectId> {
        public BlogEntryDAO( Morphia morphia, Mongo mongo,String dbName) {
            super(mongo, morphia, dbName);
        }
    }
    然后就可以實現(xiàn)大部分操作:
    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解耦么?現(xiàn)在DAO層的父類本身就在morphia包里,“這不科學呀。。。”
    建議參照其BasicDAO,自己寫一個,這才實現(xiàn)了與持久層解耦:
    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 王星游 閱讀(13890) 評論(0)  編輯  收藏


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


    網(wǎng)站導航:
     
    主站蜘蛛池模板: 成年人免费网站在线观看| 久久99国产综合精品免费| 中文在线日本免费永久18近| 久久久久久99av无码免费网站| 成人毛片免费观看视频在线| 精品剧情v国产在免费线观看| 亚洲成av人片天堂网老年人| 亚洲中文字幕无码一区| 精品亚洲aⅴ在线观看| 亚洲人片在线观看天堂无码| 无码免费又爽又高潮喷水的视频 | 成人毛片视频免费网站观看| 亚洲AV成人精品日韩一区18p| 最新亚洲成av人免费看| 亚洲黄网在线观看| 中文有码亚洲制服av片| 污视频网站免费观看| 男的把j放进女人下面视频免费| 色播精品免费小视频| 高清在线亚洲精品国产二区| 亚洲AV无码专区国产乱码4SE| 成人区精品一区二区不卡亚洲| 国产成人综合亚洲| 日本亚洲精品色婷婷在线影院| 亚洲天天做日日做天天看| 亚洲精品国产suv一区88| eeuss影院免费92242部| 免费A级毛片无码A∨| 在线观看免费大黄网站| 亚洲一级黄色视频| 亚洲黄色免费在线观看| 亚洲一卡2卡3卡4卡乱码 在线| 综合一区自拍亚洲综合图区| 日韩在线一区二区三区免费视频 | 亚洲熟妇无码av另类vr影视| 老司机午夜精品视频在线观看免费| 99在线视频免费观看| 午夜国产精品免费观看| 免费在线观看你懂的| 久久亚洲精品成人综合| 亚洲香蕉久久一区二区|