castor概述
(
by huihoo.com reimon)
Castor是ExoLab Group下面的一個(gè)開放源代碼的項(xiàng)目,它主要實(shí)現(xiàn)的是O/R映射功能。它主要API和數(shù)據(jù)接口為:JDO-like, SQL, OQL, JDBC, LDAP, XML, DSML。它支持分布式目錄事務(wù)處理和時(shí)間;提供處理XML、Directory、XADirectory的類庫,提供從XML到JAVA類的轉(zhuǎn)換機(jī)制。
上面這段是網(wǎng)上的一段評(píng)論,大概介紹了Castor的一些特性,我們需要注意的是,Castor所提供的是Castor JDO,而不是SUN所提供的JDO規(guī)范(JSR000012),所以在上面的評(píng)論中使用了JDO-like這樣的評(píng)語,在后面我們會(huì)進(jìn)一步討論這個(gè)問題的。
呵呵,不過在我看來,Castor對(duì)我們的意義,最重要的就是其XML映射機(jī)制和其獨(dú)創(chuàng)的Castor JDO的機(jī)制了。下面我會(huì)分別就這兩個(gè)問題加以介紹:
一、Castor XML我們可以認(rèn)為這是一個(gè)獨(dú)立的Framework,專門負(fù)責(zé)與XML打交道。在了解眾多的類和API之前,我們有必要了解其整體的構(gòu)建思想。在OOP中,我們都知道,對(duì)象是類的實(shí)體化,我們也知道,在OP中所要完成的工作是將對(duì)象持久保存下來,那么相對(duì)的,我們同樣可以用類似的方法將類的定義和信息也保存下來,這是否可以稱為CP呢?呵呵,不管叫什么名字,在Castor中的XML處理中,就是根據(jù)這樣的思想完成了兩個(gè)獨(dú)立的功能塊,一塊負(fù)責(zé)類到XML的保存和加載,另一塊負(fù)責(zé)對(duì)象到XML的保存和加載。具體的構(gòu)造我們可以參看下圖:

如上圖所描述的,marshaller和unmarshaller負(fù)責(zé)對(duì)象和XML文件間的轉(zhuǎn)換。這種轉(zhuǎn)換過程主要在服務(wù)器端完成。在運(yùn)行中,客戶端的請(qǐng)求發(fā)送到服務(wù)器端后,對(duì)內(nèi)存中的對(duì)象進(jìn)行相應(yīng)操作,然后將處理完畢的對(duì)象marshall成為XML文件,或者進(jìn)一步將XML文件格式化為HTML文件發(fā)送到客戶端。如果以XML文件格式發(fā)送到客戶端,那么在客戶端實(shí)行unmarshall操作將XML文件還原為內(nèi)存中的對(duì)象,這樣就完成了交互過程。

至于source generator則和TOPLINK中實(shí)現(xiàn)的相類似,不過它沒有提供可視化的XML Schema的工具而已。我們編寫好描述類結(jié)構(gòu)的XML文件,然后運(yùn)行Castor提供的source generator就可以自動(dòng)生成JAVA代碼了。有趣的是,它自動(dòng)生成的代碼和BEAN的風(fēng)格很類似,很容易看懂。
對(duì)于XML而言,只要把握了這樣的整體,其他的諸如代碼生成中的兩種模式、映射規(guī)范、數(shù)據(jù)結(jié)構(gòu)問題等細(xì)節(jié)問題就很簡單了,在文檔中附有很實(shí)用的例子和解釋,這里就不再多說了。
二、Castor JDO要了解JDO首先要認(rèn)識(shí)兩個(gè)基本的問題:
1、和SUN JDO的關(guān)系
Castor JDO和SUN JDO并不相同,Castor JDO是獨(dú)立于SUN JDO發(fā)展起來的,但是它們所完成的工作非常相似。雖然也可以將Castor JDO重新定制成SUN JDO那個(gè)樣子,但是由于兩者在鎖定策略上存在的根本差異,這樣做并不劃算。在內(nèi)部,Castor JDO為所有事務(wù)中的每個(gè)活動(dòng)的持久性對(duì)象只維護(hù)一份鎖定(和緩存)的備份。而在SUN中則對(duì)每個(gè)事務(wù)中的每個(gè)持久性對(duì)象各維護(hù)一個(gè)備份,并且SUN JDO還需要字節(jié)碼編輯器,而CastorJDO并不需要。Castor還提供了其他的一些特性,比如說key generators,long transaction support 以及 OQL query,這些都在SUN的JSR中是看不到的。所以我們?cè)谇懊鎸?duì)Castor的評(píng)價(jià)是JDO-LIKE的。
2、和EJB的關(guān)系
Castor和EJB很難說誰更好一些。
一個(gè)實(shí)體Bean可以管理它自身的持久化。它要么調(diào)用BMP(Bean Managed Persistence)來自己管理持久化,要么就依靠它的容器的CMP(Container Managed Persistence)來做到持久化。
對(duì)于BMP來說,一個(gè)實(shí)體BEAN可以使用Castor JDO作為它的持久化機(jī)制,也可以使用其他的機(jī)制,比如說直接調(diào)用JDBC。
對(duì)于CMP來說,一個(gè)EJB Container可以將它們的CMP基于Castor JDO來實(shí)現(xiàn),那么Castor JDO將被用來將實(shí)體BEAN持久化。如果開發(fā)者需要EJB的life-cycle管理, , "write once deploy anywhere" 和其他分布式應(yīng)用程序的優(yōu)點(diǎn),那么建議你直接使用EJB。否則,使用Castor將會(huì)更簡單,而且還開放源代碼,開銷更小,并提供更多的設(shè)計(jì)自由性,以及所提供的集成的XML支持。我們應(yīng)當(dāng)根據(jù)實(shí)際情況來選擇所使用的機(jī)制。
具體的使用我就不知道怎么說了,呵呵,下面把它的一個(gè)概括性介紹的文檔給翻譯了下來,Castor JDO的一些基本的使用特性里面基本上都介紹或者提及了,比如說JDO的兩種使用環(huán)境的區(qū)別、OQL query、生存期問題、基本語義、與XML聯(lián)用等,如果需要更具體的情況請(qǐng)自行查詢Castor的文檔。
<翻譯來自castor的聯(lián)機(jī)文檔--Castor JDO概況>
打開一個(gè)JDO數(shù)據(jù)庫
CatorJDO支持兩種類型的環(huán)境,客戶端應(yīng)用程序和J2EE服務(wù)器。客戶端應(yīng)用程序負(fù)責(zé)設(shè)置數(shù)據(jù)庫連接和管理事務(wù)處理。J2EE應(yīng)用程序使用JNDI來獲取預(yù)先配置好的數(shù)據(jù)庫連接,并且使用UserTransaction或者CMT(container managed transaction)來管理事務(wù)。如果你在這兩種環(huán)境中用過JDBC,那么你對(duì)它們之間的異同會(huì)很了解。
客戶端應(yīng)用程序
客戶端應(yīng)用程序負(fù)責(zé)設(shè)置數(shù)據(jù)庫連接和管理事務(wù)處理。數(shù)據(jù)庫的設(shè)置是通過一個(gè)與映射文件相連接的獨(dú)立的XML文件來實(shí)現(xiàn)的。在例子中就是database.xml,但是其實(shí)我們可以使用任何的名字。
org.exolab.castor.jdo.JDO定義了數(shù)據(jù)庫的名字和屬性,并被用來打開一個(gè)數(shù)據(jù)庫連接。通過使用setConfiguration命令來指定配置文件URL,我們可以裝載數(shù)據(jù)庫的配置。使用同一個(gè)配置來創(chuàng)建多個(gè)JDO對(duì)象只會(huì)裝載配置文件一次。 org.exolab.castor.jdo.Database對(duì)象實(shí)現(xiàn)了到數(shù)據(jù)庫的開放連接。在定義里,database對(duì)象不是thread safe因而不應(yīng)該用于并發(fā)進(jìn)程中。當(dāng)開啟多個(gè)Database對(duì)象是會(huì)有一點(diǎn)開銷,并且JDBC連接is acquired only per open transaction。下面的代碼片顯示了在典型的客戶端應(yīng)用程序中的打開一個(gè)數(shù)據(jù)庫,運(yùn)行一個(gè)事務(wù),然后關(guān)閉數(shù)據(jù)庫的過程。
JDO jdo;
Database db;
// 定義JDO對(duì)象
jdo = new JDO();
jdo.setDatabaseName( "mydb" );
jdo.setConfiguration( "database.xml" );
jdo.setClassLoader( getClass().getClassLoader() );
// 獲取一個(gè)新的數(shù)據(jù)庫
db = jdo.getDatabase();
// 開始事務(wù)處理
db.begin();
// 事務(wù)過程
. . .
// 提交事務(wù),關(guān)閉數(shù)據(jù)庫
db.commit();
db.close();
J2EE 程序( 不太懂,所以沒譯,大概說的是在J2EE中由于使用持久化方式的不通而產(chǎn)生的編碼不同的問題,可以參考一下我們前面對(duì)EJB持久化問題的探討)
Note: We are now working on supporting Castor inside a J2EE container. Stay tuned for more information. J2EE applications depend on the J2EE container (Servlet, EJB, etc) to configure the database connection and use JNDI to look it up. This model allows the application deployer to configure the database properties from a central place, and gives the J2EE container the ability to manage distributed transactions across multiple data sources. Instead of constructing a org.exolab.castor.jdo.JDO the application uses the JNDI namespace to look it up. We recommend enlisting the JDO object under the java:comp/env/jdo namespace, compatible with the convention for listing JDBC resources.
下面的代碼片使用JNDI來查找一個(gè)數(shù)據(jù)庫,并且使用UserTransaction來進(jìn)行事務(wù)管理:
InitialContext ctx;
UserTransaction ut;
Database db;
// 在JNDI中查找數(shù)據(jù)庫
ctx = new InitialContext();
db = (Database) ctx.lookup( "java:comp/env/jdo/mydb" );
// 開始事務(wù)處理
ut = (UserTransaction) ctx.lookup( "java:comp/UserTransaction" );
ut.begin();
// 事務(wù)過程
. . .
// 提交事務(wù),關(guān)閉數(shù)據(jù)庫
ut.commit();
db.close();
When the transaction is managed by the container, a common case with EJB beans and in particular entity beans, there is no need to being/commit the transaction explicitly. Instead the application server takes care of enlisting the database in the ongoing transaction and commiting/rollingback at the proper time.
The following code snippet relies on the container to manage the transaction:
InitialContext ctx;
UserTransaction ut;
Database db;
// Lookup databse in JNDI
ctx = new InitialContext();
db = (Database) ctx.lookup( "java:comp/env/jdo/mydb" );
// Do something
. . .
// Close the database
db.close();
總結(jié):開啟一個(gè)數(shù)據(jù)庫在客戶端和在服務(wù)器端是有區(qū)別的,主要體現(xiàn)在事務(wù)處理的過程、配置問題等上面,需要注意。
使用JDO數(shù)據(jù)庫
臨時(shí)對(duì)象和持久對(duì)象
所有的JDO操作都發(fā)生在事務(wù)中。事務(wù)提交時(shí),JDO將數(shù)據(jù)從數(shù)據(jù)庫中加載到內(nèi)存中的對(duì)象中,讓應(yīng)用程序?qū)?duì)象進(jìn)行操作,然后再將對(duì)象的新狀態(tài)存儲(chǔ)到數(shù)據(jù)庫中。所有的對(duì)象可以是兩種狀態(tài):臨時(shí)的或者持久的。
臨時(shí)的:指當(dāng)事務(wù)提交時(shí)狀態(tài)不會(huì)被保存的對(duì)象。對(duì)對(duì)象的修改不會(huì)表現(xiàn)在數(shù)據(jù)庫中。
持久的:指當(dāng)事務(wù)提交時(shí)狀態(tài)將被保存的對(duì)象。對(duì)對(duì)象的修改將反映在數(shù)據(jù)庫中。
在下面情況下對(duì)象為持久的: 1、一個(gè)查詢的結(jié)果,
2、使用create(java.lang.Object)或者update(java.lang.Object)將它加入到數(shù)據(jù)庫中。
所有不是持久性的對(duì)象就是臨時(shí)性對(duì)象,當(dāng)事務(wù)提交或者回滾時(shí),所有的持久性對(duì)象變成臨時(shí)對(duì)象。
在客戶端程序中,使用begin(),commit()和rollback()來管理事務(wù)。在J2EE程序中,JDO依靠container來含蓄或明確(基于bean的transaction屬性值)的使用javax.transaction.UserTransaction接口來管理事務(wù)。
如果一個(gè)持久對(duì)象在事務(wù)中被改變了,在提交時(shí)修改會(huì)存入數(shù)據(jù)庫。如果事務(wù)回滾,數(shù)據(jù)庫不會(huì)被改變。如果要在兩個(gè)不同的事務(wù)中使用同一個(gè)對(duì)象,你必須重新查詢。
對(duì)象的臨時(shí)和持久是從事務(wù)所屬的數(shù)據(jù)庫的角度來看的。一般來說持久對(duì)象只是對(duì)于一個(gè)數(shù)據(jù)庫而言,如果在另外一個(gè)數(shù)據(jù)庫中調(diào)用isPersistent(java.lang.Object)將返回false。當(dāng)然也可以讓一個(gè)持久對(duì)象對(duì)應(yīng)于兩個(gè)數(shù)據(jù)庫,比如說在一個(gè)數(shù)據(jù)庫中運(yùn)行查詢,而在另一個(gè)數(shù)據(jù)庫中創(chuàng)建它。
OQLQuery
OQL查詢被用來從數(shù)據(jù)庫中查詢和尋找對(duì)象。OQL查詢和SQL查詢類似,但是使用對(duì)象名稱而不是SQL名稱,并且不需要join子句。比如說,如果類型為TestObject的對(duì)象被加載了,OQL查詢將使用“FROM TestObject”,而不管到底表名為什么。如果加載關(guān)聯(lián)的對(duì)象需要join,Castor將自動(dòng)實(shí)現(xiàn)連接。
下面的代碼片使用OQL查詢來加載指定組中所有的對(duì)象。注意產(chǎn)品和組是關(guān)聯(lián)的對(duì)象,JDBC查詢使用了連接:
OQLQuery oql;
QueryResults results;
// Construct a new query and bind its parameters
oql = db.getOQLQuery( "SELECT p FROM Product p WHERE Group=$" );
oql.bind( groupId );
// Retrieve results and print each one
results = oql.execute();
while ( results.hasMore() ) {
System.out.println( results.next() );
}
下面的代碼片使用前面的查詢來獲得產(chǎn)品,將它們的價(jià)格減少25%,并且將它們存會(huì)數(shù)據(jù)庫(在這里使用了客戶端程序事務(wù)):
while ( results.hasMore() ) {
Product prod;
prod = (Product) results.next();
prod.markDown( 0.25 );
prod.setOnSale( true );
}
// Explicitly commit transaction
db.commit();
db.close();
如上面所描述的,查詢分三個(gè)步驟實(shí)現(xiàn):一、使用OQL語法從數(shù)據(jù)庫中創(chuàng)建一個(gè)query對(duì)象;二、如果有參數(shù),就將參數(shù)綁定到查詢中。參數(shù)綁定的順序和它們?cè)贠QL語法中出現(xiàn)的順序保持一致;三、執(zhí)行查詢并獲得一系列為org.exolab.castor.jdo.QueryResults的對(duì)象。查詢創(chuàng)建一次后可以多次使用,不過每次都得重新綁定參數(shù)。查詢?cè)俅螆?zhí)行后,上一次的結(jié)果集可以繼續(xù)使用。參考下面的代碼來調(diào)用存儲(chǔ)過程:
oql = db.getOQLQuery( "CALL sp_something($) AS myapp.Product" ); 在這里sp_something是一個(gè)返回一個(gè)或多個(gè)結(jié)果集的存儲(chǔ)過程,字段順序和前面相同(對(duì)于沒有關(guān)聯(lián)的對(duì)象,順序?yàn)椋簶?biāo)識(shí)符,接下來是所有其他的字段,順序和mapping.xml中定義的順序一致)。
Create/remove/update
create(java.lang.Object)方法在數(shù)據(jù)庫中創(chuàng)建一個(gè)新的對(duì)象,或者在JDO terminology中使得一個(gè)臨時(shí)對(duì)象持久化。如果事務(wù)被提交的話,這個(gè)使用create方法創(chuàng)建的對(duì)象將保持在數(shù)據(jù)庫中,否則將被刪除。如果使用數(shù)據(jù)庫中已經(jīng)存在的標(biāo)識(shí)來創(chuàng)建對(duì)象,將會(huì)產(chǎn)生一個(gè)異常。 下面的代碼片為先前查詢出來的一個(gè)組創(chuàng)建了一個(gè)新的產(chǎn)品:
Product prod;
// Create the Product object
prod = new Product();
prod.setSku( 5678 );
prod.setName( "Plastic Chair" );
prod.setPrice( 55.0 );
prod.setGroup( furnitures );
// Make is persistent
db.create( prod );
remove(java.lang.Object)方法產(chǎn)生相反的效果,它將刪除一個(gè)持久性對(duì)象。一旦刪除,任何事務(wù)都無法使用該對(duì)象。如果事務(wù)提交,這個(gè)對(duì)象將從數(shù)據(jù)庫中刪除,否則對(duì)象將仍然存在于數(shù)據(jù)庫中。如果試圖刪除一個(gè)非持久性對(duì)象,將產(chǎn)生異常。
使用 JDO 和 XML
CastorJDO和CastorXML結(jié)合起來,可以通過使用XML作為輸入和輸出來實(shí)現(xiàn)事務(wù)性數(shù)據(jù)庫操作。下面的程序片使用持久性和臨時(shí)性對(duì)象的結(jié)合來描述一個(gè)財(cái)務(wù)操作。
這個(gè)例子獲取了兩個(gè)account對(duì)象并且從其中一個(gè)轉(zhuǎn)移了一筆金額到另外一個(gè)賬戶。轉(zhuǎn)移被描述為使用一個(gè)臨時(shí)對(duì)象(比如說無數(shù)據(jù)庫中的記錄),這個(gè)對(duì)象接下來被用于生成一個(gè)XML文檔來描述這次轉(zhuǎn)移過程。另外還有一個(gè)附加的過程(這里沒有表現(xiàn)出來),是使用XSLT來將XML文檔轉(zhuǎn)換為一個(gè)HTML頁面。
Transfer tran;
Account from;
Account to;
OQLQuery oql;
tran = new Transfer();
// Construct a query and load the two accounts
oql = db.getOQLQuery( "SELECT a FROM Account a WHERE Id=$" );
oql.bind( fromId );
from = oql.execute().nextElement();
oql.bind( toId );
to = oql.execute().nextElement();
// Move money from one account to the other
if ( from.getBalance() >= amount ) {
from.decBalance( amount );
to.incBalance( amount );
trans.setStatus( Transfer.COMPLETE );
trans.setAccount( from );
trans.setAmount( amount );
} else {
// Report an overdraft
trans.setStatus( Transfer.OVERDRAFT );
}
// Produce an XML describing the transfer
Marshaller.marshal( trans, outputStream );
The XML produced by the above code might look like:
Completed
總結(jié):在使用數(shù)據(jù)庫的過程中,我們需要區(qū)分好臨時(shí)對(duì)象和持久對(duì)象,并掌握靈活運(yùn)用它們的方法,另外,Castor所支持的OQL的用法也值得研究,我們可以參考Castor所提供的詳細(xì)文檔進(jìn)一步了解。