Morphia官方文檔翻譯(一)Quick Tour
原文鏈接:https://mongodb.github.io/morphia/1.2/getting-started/quick-tour/
快速指南
Morphia封裝了MongoDB Java driver,所以熟悉driver會(huì)有所幫助。Morphia 已經(jīng)盡了最大努力去從driver中抽象出來(lái),但是如果遇到疑惑不解的問(wèn)題,請(qǐng)同時(shí)參考java driver文檔。
以下代碼片段來(lái)自theQuickTour.java,點(diǎn)擊獲取 Morphia source。
設(shè)置Morphia
下面的例子展示了如何創(chuàng)建Morphia實(shí)例。使用該實(shí)例,你可以對(duì)Morphia如何進(jìn)行entities映射和提交查詢做配置。
final Morphia morphia = new Morphia();
// tell Morphia where to find your classes
// can be called multiple times with different packages or classes
morphia.mapPackage("org.mongodb.morphia.example");
// create the Datastore connecting to the default port on the local host
final Datastore datastore = morphia.createDatastore(new MongoClient(), "morphia_example");
datastore.ensureIndexes();
這段代碼創(chuàng)建了我們例子程序?qū)⒂玫降腗orphia實(shí)例。Morphia類可以配置Mapper和一些系統(tǒng)范圍的選項(xiàng),也負(fù)責(zé)創(chuàng)建Datastore。Datastore有兩個(gè)參數(shù):MongoClient和數(shù)據(jù)庫(kù)的名字。通過(guò)創(chuàng)建不同的Datastore,我們可以只配置Morphia一次,但連接不同的數(shù)據(jù)庫(kù),實(shí)際應(yīng)用中,這種情況不常見(jiàn),但有可能存在。
被我們跳過(guò)的第二行,值得更進(jìn)一步說(shuō)明。在這個(gè)例子中,我們告訴Morphia搜索指定的包,找到每一個(gè)@Entity(后續(xù)說(shuō)明這個(gè)注解)注解的類,進(jìn)一步發(fā)現(xiàn)要映射的元數(shù)據(jù)(metadata )。有幾種不同的映射并且可以多次調(diào)用,以便覆蓋你所有的entities。
映射選項(xiàng)(Mapping Options)
創(chuàng)建Morphia實(shí)例后,可以通過(guò)MappingOptions類配置映射選項(xiàng)。雖然在創(chuàng)建Morphia實(shí)例的時(shí)候也可以指定Mapper,但大部分人會(huì)使用默認(rèn)的mapper。不管哪種情況,都可以通過(guò)Morphia實(shí)例的getMapper()方法獲取Mapper。最常用的兩個(gè)選項(xiàng)是storeEmpties和storeNulls。默認(rèn)情況下,Morphia不會(huì)存儲(chǔ)空List或Map,也不會(huì)存儲(chǔ)null值到MongoDB上。如果你的應(yīng)用需要用到empty or null 值,需設(shè)置相應(yīng)的選項(xiàng)為true。還有一些其他的配置選項(xiàng),在這兒不會(huì)提到。
映射類(Mapping Classes)
Morphia處理你的類有兩種方式:上層的entities或嵌入式的entities(as top level entities or embedded in others)。任何@Entity注解標(biāo)注的類都會(huì)被視為上層entities,直接對(duì)應(yīng)MongoDB中的collection的document。任何@Entity注解標(biāo)注的類必須有一個(gè)@Id注解的字段,對(duì)應(yīng)MongoDB的document的_id。@Embedded表示這個(gè)類是嵌入式的文檔,不要求有@Id注解的字段。
@Entity("employees")
@Indexes( @Index(value = "salary", fields = @Field("salary")))
class Employee {
@Id
private ObjectId id;
private String name;
@Reference
private Employee manager;
@Reference
private List<Employee> directReports;
@Property("wage")
private Double salary;}
@Entity注解里有個(gè)"employees",默認(rèn)情況下Morphia使用類名作集合名,如果傳入了字符串,就會(huì)使用這個(gè)字符串作集合名。更多注解的細(xì)節(jié)請(qǐng)查閱annotations guide
@Indexes注解列出了Morphia將創(chuàng)建哪些索引,在這個(gè)例子中,我們?cè)谧侄蝧alary上定義了一個(gè)名為salary的索引,默認(rèn)升序。更多信息在這里
我們標(biāo)記了id字段為我們的主鍵(document的_id字段),ID的類型為Java driver的ObjectId類型。ID可以是任何類型,但通常是ObjectId或Long類型。需要指出的是,Morphia將嘗試拷貝每一個(gè)字段到數(shù)據(jù)庫(kù)中,而不是transient and static。
@Property注解是一個(gè)可選項(xiàng),如果不使用這個(gè)注解,Morphia會(huì)使用Java字段名做document的字段名,使用@Property注解可以指定document的字段名。
@Reference告訴Morphia這個(gè)字段引用了另外的Morphia mapped entities。在這種情況下,Morphia會(huì)按照MongoDB中DBRef 的方式存儲(chǔ),其實(shí)就是集合名和鍵值。這些引用的entities必須已經(jīng)存儲(chǔ)了或者至少已分配了ID,否則Morphia會(huì)拋出異常。
存儲(chǔ)數(shù)據(jù)(Saving Data)
像平常使用Java對(duì)象一樣使用:
final Employee elmer = new Employee("Elmer Fudd", 50000.0);
datastore.save(elmer);
更進(jìn)一步,定義一些關(guān)系并存儲(chǔ):
final Employee daffy = new Employee("Daffy Duck", 40000.0);
datastore.save(daffy);
final Employee pepe = new Employee("Pepé Le Pew", 25000.0);
datastore.save(pepe);
elmer.getDirectReports().add(daffy);
elmer.getDirectReports().add(pepe);
datastore.save(elmer);
正如你所看到的,我們只需要?jiǎng)?chuàng)建和保存其他Employees,然后就可以添加他們到direct reports鏈表并保存。Morphia負(fù)責(zé)保存Daffy and Pepé相關(guān)的keys到Elmer’s document。更新MongoDB中的數(shù)據(jù)就像更新你的Java對(duì)象一樣簡(jiǎn)單,最后記著調(diào)用datastore.save()。對(duì)于大量的更新(比如每個(gè)人都晉升了),這不是最有效的更新方式。在不拉取每個(gè)document的前提下直接更新數(shù)據(jù)庫(kù)的數(shù)據(jù)是有可能的,轉(zhuǎn)換成Java對(duì)象,更新,轉(zhuǎn)換回document,然后寫(xiě)入MongoDB.在展示這個(gè)機(jī)制之前,先要看看怎么查詢。
查詢(Querying)
Morphia嘗試使你的查詢盡可能類型安全。轉(zhuǎn)換你數(shù)據(jù)的所有細(xì)節(jié)都直接被Morphia處理了,幾乎沒(méi)有什么額外的操作需要你來(lái)進(jìn)行。
final Query<Employee> query = datastore.createQuery(Employee.class);
final List<Employee> employees = query.asList();
這是一個(gè)簡(jiǎn)單的Morphia查詢。我們讓Datastore創(chuàng)建一個(gè)Employee類型的查詢,把查到Employee存到List中。對(duì)于很大的查詢結(jié)果,這很可能導(dǎo)致內(nèi)存不足。在這個(gè)簡(jiǎn)單的例子中使用了asList(),但實(shí)際上,fetch()是更合適的選擇。大多數(shù)查詢都會(huì)以某種方式過(guò)濾數(shù)據(jù),有兩種方式實(shí)現(xiàn):
underpaid = datastore.createQuery(Employee.class)
.field("salary")
.lessThanOrEq(30000) .asList();
field()方法用來(lái)過(guò)濾字段并且返回一個(gè)接口的實(shí)例,這個(gè)接口含有一組用于構(gòu)建查詢的方法。這種方式是有益的,編譯時(shí)檢查是必要的。這種查詢構(gòu)建的方式對(duì)于避免javac因缺失方法失敗和輔助IDE的自動(dòng)完成都很有用。
另一種使用filter()方法的途徑比f(wàn)ield()更加自由和簡(jiǎn)潔。我們可以直接嵌入操作符到查詢字符串中。雖然這種方式?jīng)]有那么繁冗,它確實(shí)放了更多的東西到字符串中來(lái)驗(yàn)證并有可能出錯(cuò):
List<Employee> underpaid = datastore.createQuery(Employee.class)
.filter("salary <=", 30000)
.asList();
更新(Updates)
掌握了查詢之后,我們可以回到in-database更新的問(wèn)題。這類更新包含兩個(gè)組件:一個(gè)查詢,一系列更新操作。在這個(gè)例子中,我們找出所有領(lǐng)取薪酬的員工,給他們加薪10000。第一步是創(chuàng)建查詢找出領(lǐng)取薪酬的員工,這段代碼我們之前已見(jiàn)到過(guò):
final Query<Employee> underPaidQuery = datastore.createQuery(Employee.class)
.filter("salary <=", 30000);
To define how we want to update the documents matched by this query, we create an UpdateOperations instance:
為了定義我們?nèi)绾胃耫ocuments,我們先創(chuàng)建一個(gè)UpdateOperations 實(shí)例:
final UpdateOperations<Employee> updateOperations = datastore.createUpdateOperations(Employee.class)
.inc("salary", 10000);
這個(gè)類有很多種操作,但在這個(gè)例子中,我們只更新salary字段(增加10000),用到了$inc
operator。最后一步:
final UpdateResults results = datastore.update(underPaidQuery, updateOperations);
這行代碼執(zhí)行了數(shù)據(jù)庫(kù)內(nèi)的update,沒(méi)有拉取documents。UpdateResults實(shí)例返回一系列更新操作的統(tǒng)計(jì)數(shù)據(jù)。
刪除(Removes)
final Query<Employee> overPaidQuery = datastore.createQuery(Employee.class)
.filter("salary >", 100000);
datastore.delete(overPaidQuery);
delete()有一些其他的用法,但這就是最普通的用法。如果你手上已經(jīng)有了一個(gè)對(duì)象,有一個(gè)delete可以取得引用并刪除它。更多信息請(qǐng)參考javadoc