castor概述


(by huihoo.com reimon)

Castor是ExoLab Group下面的一个开放源代码的项目,它主要实现的是O/R映射功能。它主要API和数据接口为:JDO-like, SQL, OQL, JDBC, LDAP, XML, DSML。它支持分布式目录事务处理和时间;提供处理XML、Directory、XADirectory的类库,提供从XML到JAVA类的转换机制。

上面这段是网上的一段评论,大概介绍了Castor的一些特性,我们需要注意的是,Castor所提供的是Castor JDO,而不是SUN所提供的JDO规范(JSR000012),所以在上面的评论中使用了JDO-like这样的评语,在后面我们会进一步讨论这个问题的。

呵呵,不过在我看来,Castor对我们的意义,最重要的就是其XML映射机制和其独创的Castor JDO的机制了。下面我会分别就这两个问题加以介绍:

一、Castor XML

我们可以认为这是一个独立的Framework,专门负责与XML打交道。在了解众多的类和API之前,我们有必要了解其整体的构建思想。 在OOP中,我们都知道,对象是类的实体化,我们也知道,在OP中所要完成的工作是将对象持久保存下来,那么相对的,我们同样可以用类似的方法将类的定义和信息也保存下来,这是否可以称为CP呢?呵呵,不管叫什么名字,在Castor中的XML处理中,就是根据这样的思想完成了两个独立的功能块,一块负责类到XML的保存和加载,另一块负责对象到XML的保存和加载。 具体的构造我们可以参看下图:


如上图所描述的,marshaller和unmarshaller负责对象和XML文件间的转换。这种转换过程主要在服务器端完成。在运行中,客户端的请求发送到服务器端后,对内存中的对象进行相应操作,然后将处理完毕的对象marshall成为XML文件,或者进一步将XML文件格式化为HTML文件发送到客户端。如果以XML文件格式发送到客户端,那么在客户端实行unmarshall操作将XML文件还原为内存中的对象,这样就完成了交互过程。


至于source generator则和TOPLINK中实现的相类似,不过它没有提供可视化的XML Schema的工具而已。我们编写好描述类结构的XML文件,然后运行Castor提供的source generator就可以自动生成JAVA代码了。有趣的是,它自动生成的代码和BEAN的风格很类似,很容易看懂。

对于XML而言,只要把握了这样的整体,其他的诸如代码生成中的两种模式、映射规范、数据结构问题等细节问题就很简单了,在文档中附有很实用的例子和解释,这里就不再多说了。

二、Castor JDO

要了解JDO首先要认识两个基本的问题:

1、和SUN JDO的关系

Castor JDO和SUN JDO并不相同,Castor JDO是独立于SUN JDO发展起来的,但是它们所完成的工作非常相似。虽然也可以将Castor JDO重新定制成SUN JDO那个样子,但是由于两者在锁定策略上存在的根本差异,这样做并不划算。在内部,Castor JDO为所有事务中的每个活动的持久性对象只维护一份锁定(和缓存)的备份。而在SUN中则对每个事务中的每个持久性对象各维护一个备份,并且SUN JDO还需要字节码编辑器,而CastorJDO并不需要。Castor还提供了其他的一些特性,比如说key generators,long transaction support 以及 OQL query,这些都在SUN的JSR中是看不到的。所以我们在前面对Castor的评价是JDO-LIKE的。

2、和EJB的关系

Castor和EJB很难说谁更好一些。

一个实体Bean可以管理它自身的持久化。它要么调用BMP(Bean Managed Persistence)来自己管理持久化,要么就依靠它的容器的CMP(Container Managed Persistence)来做到持久化。

对于BMP来说,一个实体BEAN可以使用Castor JDO作为它的持久化机制,也可以使用其他的机制,比如说直接调用JDBC。

对于CMP来说,一个EJB Container可以将它们的CMP基于Castor JDO来实现,那么Castor JDO将被用来将实体BEAN持久化。 如果开发者需要EJB的life-cycle管理, , "write once deploy anywhere" 和其他分布式应用程序的优点,那么建议你直接使用EJB。否则,使用Castor将会更简单,而且还开放源代码,开销更小,并提供更多的设计自由性,以及所提供的集成的XML支持。我们应当根据实际情况来选择所使用的机制。

具体的使用我就不知道怎么说了,呵呵,下面把它的一个概括性介绍的文档给翻译了下来,Castor JDO的一些基本的使用特性里面基本上都介绍或者提及了,比如说JDO的两种使用环境的区别、OQL query、生存期问题、基本语义、与XML联用等,如果需要更具体的情况请自行查询Castor的文档。

<翻译来自castor的联机文档--Castor JDO概况>

打开一个JDO数据库

CatorJDO支持两种类型的环境,客户端应用程序和J2EE服务器。客户端应用程序负责设置数据库连接和管理事务处理。J2EE应用程序使用JNDI来获取预先配置好的数据库连接,并且使用UserTransaction或者CMT(container managed transaction)来管理事务。如果你在这两种环境中用过JDBC,那么你对它们之间的异同会很了解。

客户端应用程序

客户端应用程序负责设置数据库连接和管理事务处理。数据库的设置是通过一个与映射文件相连接的独立的XML文件来实现的。在例子中就是database.xml,但是其实我们可以使用任何的名字。

org.exolab.castor.jdo.JDO定义了数据库的名字和属性,并被用来打开一个数据库连接。通过使用setConfiguration命令来指定配置文件URL,我们可以装载数据库的配置。使用同一个配置来创建多个JDO对象只会装载配置文件一次。 org.exolab.castor.jdo.Database对象实现了到数据库的开放连接。在定义里,database对象不是thread safe因而不应该用于并发进程中。当开启多个Database对象是会有一点开销,并且JDBC连接is acquired only per open transaction。下面的代码片显示了在典型的客户端应用程序中的打开一个数据库,运行一个事务,然后关闭数据库的过程。

JDO jdo;
Database db;

// 定义JDO对象
jdo = new JDO();
jdo.setDatabaseName( "mydb" );
jdo.setConfiguration( "database.xml" );
jdo.setClassLoader( getClass().getClassLoader() );

// 获取一个新的数据库

db = jdo.getDatabase();

// 开始事务处理
db.begin();

// 事务过程
. . .

// 提交事务,关闭数据库
db.commit();
db.close();

J2EE 程序( 不太懂,所以没译,大概说的是在J2EE中由于使用持久化方式的不通而产生的编码不同的问题,可以参考一下我们前面对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来查找一个数据库,并且使用UserTransaction来进行事务管理:

InitialContext ctx;
UserTransaction ut;
Database db;

// 在JNDI中查找数据库
ctx = new InitialContext();
db = (Database) ctx.lookup( "java:comp/env/jdo/mydb" );

// 开始事务处理
ut = (UserTransaction) ctx.lookup( "java:comp/UserTransaction" );
ut.begin();

// 事务过程
. . .

// 提交事务,关闭数据库
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();

总结:开启一个数据库在客户端和在服务器端是有区别的,主要体现在事务处理的过程、配置问题等上面,需要注意。

使用JDO数据库
临时对象和持久对象
所有的JDO操作都发生在事务中。事务提交时,JDO将数据从数据库中加载到内存中的对象中,让应用程序对对象进行操作,然后再将对象的新状态存储到数据库中。所有的对象可以是两种状态:临时的或者持久的。
临时的:指当事务提交时状态不会被保存的对象。对对象的修改不会表现在数据库中。
持久的:指当事务提交时状态将被保存的对象。对对象的修改将反映在数据库中。
在下面情况下对象为持久的: 1、一个查询的结果,
2、使用create(java.lang.Object)或者update(java.lang.Object)将它加入到数据库中。
所有不是持久性的对象就是临时性对象,当事务提交或者回滚时,所有的持久性对象变成临时对象。
在客户端程序中,使用begin(),commit()和rollback()来管理事务。在J2EE程序中,JDO依靠container来含蓄或明确(基于bean的transaction属性值)的使用javax.transaction.UserTransaction接口来管理事务。
如果一个持久对象在事务中被改变了,在提交时修改会存入数据库。如果事务回滚,数据库不会被改变。如果要在两个不同的事务中使用同一个对象,你必须重新查询。
对象的临时和持久是从事务所属的数据库的角度来看的。一般来说持久对象只是对于一个数据库而言,如果在另外一个数据库中调用isPersistent(java.lang.Object)将返回false。当然也可以让一个持久对象对应于两个数据库,比如说在一个数据库中运行查询,而在另一个数据库中创建它。
OQLQuery
OQL查询被用来从数据库中查询和寻找对象。OQL查询和SQL查询类似,但是使用对象名称而不是SQL名称,并且不需要join子句。比如说,如果类型为TestObject的对象被加载了,OQL查询将使用“FROM TestObject”,而不管到底表名为什么。如果加载关联的对象需要join,Castor将自动实现连接。
下面的代码片使用OQL查询来加载指定组中所有的对象。注意产品和组是关联的对象,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() );
}

下面的代码片使用前面的查询来获得产品,将它们的价格减少25%,并且将它们存会数据库(在这里使用了客户端程序事务):
while ( results.hasMore() ) {
Product prod;
prod = (Product) results.next();
prod.markDown( 0.25 );
prod.setOnSale( true );
}

// Explicitly commit transaction
db.commit();
db.close();

如上面所描述的,查询分三个步骤实现:一、使用OQL语法从数据库中创建一个query对象;二、如果有参数,就将参数绑定到查询中。参数绑定的顺序和它们在OQL语法中出现的顺序保持一致;三、执行查询并获得一系列为org.exolab.castor.jdo.QueryResults的对象。 查询创建一次后可以多次使用,不过每次都得重新绑定参数。查询再次执行后,上一次的结果集可以继续使用。 参考下面的代码来调用存储过程:
oql = db.getOQLQuery( "CALL sp_something($) AS myapp.Product" ); 在这里sp_something是一个返回一个或多个结果集的存储过程,字段顺序和前面相同(对于没有关联的对象,顺序为:标识符,接下来是所有其他的字段,顺序和mapping.xml中定义的顺序一致)。
Create/remove/update
create(java.lang.Object)方法在数据库中创建一个新的对象,或者在JDO terminology中使得一个临时对象持久化。如果事务被提交的话,这个使用create方法创建的对象将保持在数据库中,否则将被删除。如果使用数据库中已经存在的标识来创建对象,将会产生一个异常。 下面的代码片为先前查询出来的一个组创建了一个新的产品:

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)方法产生相反的效果,它将删除一个持久性对象。一旦删除,任何事务都无法使用该对象。如果事务提交,这个对象将从数据库中删除,否则对象将仍然存在于数据库中。如果试图删除一个非持久性对象,将产生异常。

使用 JDO 和 XML

CastorJDO和CastorXML结合起来,可以通过使用XML作为输入和输出来实现事务性数据库操作。下面的程序片使用持久性和临时性对象的结合来描述一个财务操作。

这个例子获取了两个account对象并且从其中一个转移了一笔金额到另外一个账户。转移被描述为使用一个临时对象(比如说无数据库中的记录),这个对象接下来被用于生成一个XML文档来描述这次转移过程。另外还有一个附加的过程(这里没有表现出来),是使用XSLT来将XML文档转换为一个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




总结:在使用数据库的过程中,我们需要区分好临时对象和持久对象,并掌握灵活运用它们的方法,另外,Castor所支持的OQL的用法也值得研究,我们可以参考Castor所提供的详细文档进一步了解。

原文下载