MAGICIAN  IS CASTOR  (Castor  JDO,Castor XML,魔术) 
 
  
作者: 乐晨光 [email protected]  (2002/3/18)
 
 
 
  
 
  
INDEX
 
 
  1.INTRO
  2.MAGICAL TOUCHING OF <CASTOR - JDO>
  3.MAGICAL TOUCHING OF <CASTOR - XML> 
 
  4.MAPPING
  5.CONCLUSION 
 
 
 
  1.  INTRO 
  
 
  
1.1  Before Intro 
 
 
  基于中间件体系的业务系统,一般是由 数据库管理系统(关系数据库,面向对象数据库等),数据事务处理系统(Enterprise  JavaBean),以及客户表现负责层(JSP,Servlet-基于WEB)这三方面构成;业务过程中,XML将是远程数据通信的格式。
 
 
  其具体的运作过程为:
 
 
  数据库管理系统,负责存储整个业务逻辑的所有永久数据,其类型可能是最流行的关系数据库,或是面向对象数据库或其它。数据事务处理系统(如EJB)根据其客户的业务需求调用,负责从数据库管理系统中读出数据,并将其转换成该管理系统的工作格式(譬如转换成OO对象)根据一定的事务规则进行业务处理,结果可能是将该转换对象重新存入数据库,或是把一些对象或数据传递给它的客户;注意这个客户或许是该系统的客户表现负责层(JSP,Servlet-基于WEB),也可能是远程的应用程序,当然也可能是别的数据事务处理系统。客户表现负责层(JSP,Servlet-基于WEB),负责WEB客户的页面输送,它可以把从数据事务处理系统获取的对象转换成XML数据,然后进一步利用XSL生成HTML页面传给WEB客户;它也可能将XML数据直接传给客户,由客户负责将其转换成别的格式(HTML,对象) 
 
 
 
 
 
 
  
 
 
  关注以下几个过程:
 
 
  1. 数据事务处理系统(如EJB)将从数据库管理系统查询的数据转换成系统特定的类对象,进行业务处理。系统需要将数据库逻辑(譬如关系数据库)根据一套映射规则定义成类,  并且把对数据库查询所获取的数据填充类生成对象。大都数来讲,关系数据库中表以及描述逻辑并不是很复杂,类似于编程语言中的结构(包含若干数据属性)。 
 
 
 
  2. 数据事务处理系统(如EJB)将类对象通过查询语句转换成数据库数据反返存入数据库管理系统。处理方式与上类似,可视为其逆过程) 
 
 
  3. 客户表现负责层(JSP,Servlet-基于WEB) 或是数据事务处理系统在业务过程中将Object转换成XML数据作为传输介质。系统要为该转换定义一个映射规则,特别还要处理面向对象的继承与组合规则的XML映射描述。 
 
 
 
  4. 客户应用程序将获取的XML数据重新转换成对象,作业务处理。处理方式与上类似,是一个逆过程) 
 
 
  这些过程由下图概括: 
 
   
 
 
 
  这个过程几乎存在于任何一个系统,数据载体映射规则在一个新的业务系统中都可能将被重定义。我们可以将这个映射规则反映在代码中,当然也可以放在一个脚本文件里。后者获取会有更大的重用性与易用性。这是将回是一个复用模块。  能否将 数据库数据,OO类对象,XML这3个当今应用最广泛的数据载体格式之间的互换映射 建立出一个通用的映射规则,形成复用框架,甚至达成标准,将会大大提高业务开发的效率。 
 
 
 
  1.2  Preface Of Castor
 
 
  Castor是ExoLab Group下面的一个开放源代码的项目,它主要实现的是O/R映射功能。它主要API和数据接口为:JDO-like,  SQL, OQL, JDBC, LDAP, XML, DSML。它支持分布式目录事务处理和时间;提供处理XML、Directory、XADirectory的类库,提供从XML到JAVA类的转换机制。 
 
 
 
  Castor将以上所提的数据载体格式的映射转换规则搭成了一个应用框架: 
  Castor JDO负责数据库数据与OO Object的映射转换。 
  Castor XML负责XML数据与OO Object的映射转换。marshaller和unmarshaller负责对象和XML数据间的转换。 
 
 
 
  Castor  JDO (Java Data Objcet) 
 
 
  实现的是O/R映射,它最主要的功能可以直接将面向对象的类对象从关系数据库中读出或永久存取。JDO用户完全可以可将关系数据库以及SQL查询语句透明化,这将由JDO的O/R映射代劳。工作模式如下描述: 
 
 
   
 
  Castor  XML 
 
 
  实现的是XML与OO Class/Object的映射,我们可以认为这是一个独立的Framework,专门负责与XML打交道。我们非常需要把类的定义的信息永久的保存起来(用XML),根据这个思想,在Castor中的XML处理中,完成了两个独立的功能块:一块负责类到XML的保存和加载,另一块负责对象到XML的保存和加载。 
 
  具体的构造我们可以参看下图: 
 
   
 
 
 
  如上图所描述的,marshaller和unmarshaller负责对象和XML文件间的转换。这种转换过程主要在服务器端完成。在运行中,客户端的请求发送到服务器端后,对内存中的对象进行相应操作,然后将处理完毕的对象marshall成为XML文件,或者进一步将XML文件格式化为HTML文件发送到客户端。如果以XML文件格式发送到客户端,那么在客户端实行unmarshall操作将XML文件还原为内存中的对象,这样就完成了交互过程。  Castor自带的source generator工具可以根据XML描述文件自动生成对应的JAVA代码了,它自动生成的代码和BEAN的风格很类似,很容易看懂。
 
 
  整体的运行框架如下图描述:
    
 
 
 
  以下将详细描述Castor JDO与Castor XML的应用方法。 
 
 
 
 
 
 
 
 
 
 
  2.  MAGICAL TOUCHING OF <CASTOR - JDO>
 
 
  2.1  Open A JDO Database(打开一个JDO数据库) 
 
 
 
  CatorJDO支持两种类型的环境,客户端应用程序和J2EE服务器。客户端应用程序负责设置数据库连接和管理事务处理。J2EE应用程序使用JNDI来获取预先配置好的数据库连接,并且使用UserTransaction或者CMT(container  managed transaction)来管理事务。 
 
 
  2.1.1 客户端应用程序 
 
 
  客户端应用程序负责设置数据库连接和管理事务处理。数据库的设置是通过一个与映射文件相连接的独立的XML文件来实现的。(一般是database.xml,但是我们可以使用任何名字)  org.exolab.castor.jdo.JDO定义了数据库的名字和属性,并被用来打开一个数据库连接。通过使用setConfiguration命令来指定配置文件URL,我们可以装载数据库的配置。使用同一个配置来创建多个JDO对象只会装载配置文件一次。  org.exolab.castor.jdo.Database对象实现了到数据库的开放连接。在定义里,database对象不是进程安全的,因而不应该用于并发进程中。当开启多个Database对象是会有一点开销,并且JDBC连接仅只能在一个打开的事务中获取(后台)。
 
 
  以下代码片描述了如何在Client App中打开关闭一个JDO数据库: 
 
 
  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(); 
 
 
 
  
 
  
2.1.2 J2EE程序 
 
 
  我们将会在J2EE容器中使用Castor进行数据载体的映射工作,J2EE程序依赖于J2EE容器(包括Servlet,JSP,EJB,etc)来设置数据库连接,并且使用JNDI去找到它。这个工作模型允许容器配置人员在一个工作中心来设置数据库属性,并且让J2EE容器能够跨多个数据(库)源来管理分部式的DB事务。J2EE程序通过JNDI命名空间来找到这个数据源。规范建议把JDO对象设置在java:comp/env/jdo命名空间里,使其类似与JDBC数据源的命名。 
 
  
  下面的代码片使用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();  
 
  
当事务是由容器管理时,譬如EJB(特别是实体Bean[entity bean]),这就没有必要明确地做事务开始与提交操作(begin/commite)。Application  Server将实时负责数据更新。 
 
  
以下代码片描述了由容器管理的事务过程: 
 
  
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();  
 
  
总结:开启一个数据库在客户端和在服务器端是有区别的,主要体现在事务处理的过程、配置问题等上面,需要注意。 
 
 
  2.2  JDO Transaction(JDO事务处理) 
 
 
 
  2.2.1 临时对象和持久对象
 
 
  所有的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。当然也可以让一个持久对象对应于两个数据库,比如说在一个数据库中运行查询,而在另一个数据库中创建它。 
 
 
  
 
  
1.2.2 OQLQuery (对象查询语言) 
 
  
OQL查询被用来从数据库中查询和寻找对象。OQL查询和SQL查询类似,但是使用对象名称而不是SQL名称,并且不需要join子句。比如说,如果类型为TestObject的对象被加载了,OQL查询将使用"FROM  TestObject",而不管到底表名为什么。如果加载关联的对象需要join,Castor将自动实现连接。  
 
  
下面的代码片使用OQL查询来加载指定组中所有的对象。注意产品和组是关联的对象,JDBC查询使用了连接: 
  
 
  
OQLQuery oql;
  QueryResults results;  
 
  
// 构造新的query查询并绑定参数
  oql = db.getOQLQuery( "SELECT p FROM Product p WHERE Group=$"  ); 
  oql.bind( groupId );  
 
  
// 遍历结果并将其打印
  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 ); 
  }  
 
  
// 提交事务并关闭结束
  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; 
 
 
  // 创建 Product 对象
  prod = new Product(); 
  prod.setSku( 5678 ); 
  prod.setName( "Plastic Chair" ); 
  prod.setPrice( 55.0 ); 
  prod.setGroup( furnitures );  
 
  
// 在数据库中创建一个新的对象(持久性的)
  db.create( prod );  
 
  
// 创建 Product 对象
  prod = new Product(); 
  prod.setSku( 5678 ); 
  prod.setName( "Plastic Chair" ); 
  prod.setPrice( 55.0 ); 
  prod.setGroup( furnitures );  
 
  
// 在数据库中创建一个新的对象(持久性的)
  db.create( prod );  
 
  
remove(java.lang.Object)方法产生相反的效果,它将删除一个持久性对象。一旦删除,任何事务都无法使用该对象。如果事务提交,这个对象将从数据库中删除,否则对象将仍然存在于数据库中。如果试图删除一个非持久性对象,将产生异常。 
 
 
  
 
 
 
 
  2.3  JDO Mapping(JDO映射) 
 
 
  Castor JDO用户需定义一份mapping描述文件来定义O/R映射,Castor将其作为一个XML文件,并定义了mapping的dtd/schema。限于篇幅,用户可自下下载详细考察。 
 
  
Castor Mapping DTD: http://castor.exolab.org/mapping.dtd
  Castor Mapping XML Schema: http://castor.exolab.org/mapping.xsd 
 
  
其中包含了Castor JDO O/R映射规则以及Castor XML的映射规则。
 
 
 
 
 
 
 
 
 
 
 
  
 
  
3.  MAGICAL TOUCHING OF <CASTOR - XML> 
 
 
 
  利用Castor XML可使OO对象与XML/Schema数据进行映射互换。用户需根据Castor Mapping Schema定义一个mapping映射规则。(实现在mapping.xml中)。转换过程由org.exolab.castor.xml.Marshaller类来实现。 
 
 
 
  3.1  Marshaller 
  用户定义标识映射规则的mapping.xml,再用org.exolab.castor.mapping.Mapping将其载入,
  接着用org.exolab.castor.xml.Marshaller将Mapping载入。接下去,用户可使用Marshaller.marshal(  .. )方法来做Object到XML数据的映射转换。 
 
  
以下代码示意了Marshaller 的配置以及几种映射过程: 
 
  
Marshaller marshaller;
  Mapping _mapping;
  PrintWriter writer;
  MyClass myObject;
  ………………
  // 创建marshaller对象并载入Mapping映射规则
  marshaller = new Marshaller( writer );
  marshaller.setMapping( _mapping ); 
 
  
// OO对象到XML数据 (可以是流,DOM树节点,或是SAX遍历句柄)  的映射转换
  java.io.Writer out;
  marshaller.marshal(myObject, out); 
 
  
org.w3c.dom.Node node
  marshaller.marshal(myObject, node); 
 
  
org.xml.sax.DocumentHandler handler
  marshaller.marshal(myObject, handler);
  ………………
 
 
  Castor XML运行框架如下图所示:
    
 
 
 
 
 
  3.2  XML Mapping(XML映射定义)
 
 
  Castor XML用户需定义一份mapping描述文件来定义OO Object to XML映射,Castor将其作为一个XML文件,并定义了mapping的dtd/schema。限于篇幅,用户可自下下载详细考察。 
 
  
Castor Mapping DTD: http://castor.exolab.org/mapping.dtd
  Castor Mapping XML Schema: http://castor.exolab.org/mapping.xsd 
 
  
其中包含了Castor JDO O/R映射规则以及Castor XML的映射规则。
 
 
 
 
 
 
 
 
 
 
 
 
  4.  MAPPING
 
 
  4.1  Castor Mapping
 
 
  Castor Mapping DTD: http://castor.exolab.org/mapping.dtd
  Castor Mapping XML Schema: http://castor.exolab.org/mapping.xsd
 
 
  其中包含了Castor JDO O/R映射规则以及Castor XML的映射规则。 
 
  
以下是Castor Mapping的mapping.xml的一段示意代码 
 
  
<mapping>
  - <!-- 
  Mapping for ProductGroup 
  --> 
    <class name="myapp.ProductGroup" identity="id"  access="shared">
      <description>Product group</description> 
      <map-to table="prod_group" xml="group" />
      <field name="id" type="integer" required="false"  direct="false" lazy="false">
        <sql name="id" type="integer" dirty="check"  /> 
        <xml node="attribute" /> 
      </field>
      <field name="name" type="string" required="false"  direct="false" lazy="false">
        <sql name="name" type="char" dirty="check"  /> 
        <xml node="text" /> 
      </field>
    </class>
  …………………..
  </mapping> 
 
  
注释: 每一个Class的对象集映射为关系数据库的一张表,表中的字段为该Class的属性集,映射表中需标识每个属性的Class名称、SQL字段名称、XML元素或字段名称,以及各自的类别。Castor  Mapping支持类别的继承关系以及对象的组合关系,详细语法参看Castor Mapping DTD/ Castor Mapping  XML Schema。 
 
  
4.2  JDO Configuration 
 
  
以下是JDO Configuration的database.xml的一段示意代码: 
 
  
<!DOCTYPE databases (View Source for full doctype...)> 
 
  <database name="test" engine="sybase">
    <data-source class-name="com.sybase.jdbc2.jdbc.SybDataSource">
      <params user="myname" password="secret" port-number="4100"  server-name="airwalker" /> 
    </data-source>
    <mapping href="mapping.xml" /> 
  </database> 
 
  
注释: database的engine属性标识了Castor JDO支持的数据库厂商,标列如下:
 
 
  generic Generic JDBC engine 
  oracle Oracle 7 and Oracle 8
  sybase Sybase 11 and SQL Anywhere 
  sql-server Microsoft SQL Server
  postgresql PostgreSQL 6.5 and 7
 
 
  data-source的class-name属性标识了JDBC的实现类,接着的一些元素及其属性标识了数据库的访问入口。mapping的href属性标识了Castor  Mapping的映射XML文件(一般都起名为mapping.xml) 
 
  
 
 
 
 
 
 
  5. CONCLUSION
 
 
  
Castor框架提出了一个近乎与标准的O/R映射规则及实现,它让框架客户能较为方便的实现系统数据载体格式的转换,在关系数据库、面向对象语言,以及XML大兴其道的业界,Castor框架使客户无需做代码式的数据填充,以及将JDBC的SQL查询透明化,用户在Castor  框架的熟悉过程,能充分利用其各方面的优越性来获取开发效率的提高。 
 
  
Castor是一个复用性非常强的框架,但其独创的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的关系 
 
  
一个实体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支持。我们应当根据实际情况来选择所使用的机制。 
 
 
 
 
 
 
 
 
 
 
  
 
  
#  Reference
  Castor官方介绍文档。  
 
  
                                         乐晨光(2002/3/18)
 
  |