exolab旗下的castor是目前流行的、开放源码的jdo实现包。 它主要用来实现o/r mapping。运用该软件包可以大大减轻程序员在处理对象-关系数据库的负担。本文介绍了castor的基本用法, 并用有大量代码实例进行解释。
内容提要:
? 打开jdo数据库 client应用
? j2ee应用
? 使用jdo数据库对象
? 区别瞬时jdo对象和持久jdo对象
? oqlquery
? 创建与更新和删除
? 使用jdo和xml
打开jdo数据库
castor jdo支持两种类型的环境,client应用和j2ee服务器。 client应用被用来负责配置数据库连接和明确地管理事务。 j2ee应用使用jndi来获得预先设好的数据库连接并利用usertransaction或容器管理事务(cmt)来处理事务。 如果你曾经在这两种环境中使用过jdbc,那么应该比较熟悉这两种模型和他们之间的区别。
client应用
client应用负责定义jdo数据库配置,和明确地管理事务处理。 数据库通过一个单独的xml文件被配置 并连接到一个mapping文件。 在例子代码中我将数据库文件命名为database.xml,但是你可以使用任何别的名称。 更多信息参见castor jdo数据库配置。
org.exolab.castor.jdo.jdo定义数据库名称和属性并且被用来打开数据库连接。 在上
可以通过设置setconfiguration文件的url来要求jdo层装载哪个数据库配置。 注意:castor jdo在建立多个用同样的配置的jdo对象的情况下,将会仅仅只执行一次装载数据库配置。
org.exolab.castor.jdo.database对象代表数据库的一个打开的连接。
线程 jdo对象定义不是线程安全的,因此不应该在并发多线程中使用jdo对象。还有,建立多个jdo对象仅仅 需要少量额外工作,而jdbc连接在每个事务处理中仅仅打开一次。这样处理的模式能够大大提高性能。
下列的代码片断展示了在client应用中很常用的组合:“打开数据库,执行sql,关闭数据库。”
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容器内嵌支持castor。
j2ee应用依赖于j2ee容器(servlet,ejb,等等)构成数据库关连和使用jndi找到它,使用它。 j2ee应用模型允许应用部署从一个中央的地方构成数据库中心,并且提供了j2ee容器能够管理横跨多重的数据源的分布式的处理能力。
在j2ee环境中,应用程序使用jndi lookup代替org.exolab.castor.jdo.jdo 来构造jdo对象。 专家一般推荐把jdo对象的jndi放在java:comp/env/jdo 之下的命名空间,这样可兼容jdbc资源列表的规定。
下列的代码片断使用jndi来查询数据库对象,并且使用usertransaction来管理事务处理
initialcontext ctx;
usertransaction ut;
database db;
// 用jndi的查询得到databse对象
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在特别是实体 bean中时,不需要明确地与指明事务的开始和结束提交;应用服务器将会照料那些正在进行的处理的事务,并在适当的时间的提交或会滚数据库请求下列的代码片断展示了如何利用依赖于容器管理事务:
initialcontext ctx;
usertransaction ut;
database db;
// 用jndi的查询得到databse对象
ctx = new initialcontext();
db = (database) ctx.lookup( "java:comp/env/jdo/mydb" );
// 业务逻辑
. . .
// 关闭数据库
db.close();
使用jdo数据库对象
区别瞬时jdo对象和持久jdo对象
所有jdo操作在事务处理的上下文之内发生。
jdo通过把数据库中的数据装载到内存中的对象,并允许应用程序修改对象,然后存储对象的新的状态到数据库中去当应用程序提交事务时。
所有对象只可能有两种状态: 瞬时或者持久(transient / persistent)
瞬时: 瞬时jdo对象,当应用程序提交事务时,其状态将不被保存到的数据库。瞬时jdo对象的变化将不在数据库中反映出来。
持久:持久jdo对象当应用程序提交事务时,其状态将被保存的任何对象数据库。持久jdo对象的变化将在数据库中反映出来。
一个对象可以有两种途径变成为持久jdo对象: 它是由查询产生的,(该查询不是被设置为只读方式)或者 否则它用create(java.lang.object)或者update(java.lang.object)方法来存入数据库。
? 所有不是持久jdo对象的对象是瞬时jdo对象。
? 当应用程序提交事务或者回滚时,所有持久jdo对象自动变回得瞬时jdo对象。 在client应用中,我们可以使用begin(),commit()和rollback()管理事务。 但是在j2ee应用中,依赖于容器的jdo对象或者是隐含地(基于bean的事务属性)或者是明确地使用javax.transaction.usertransaction接口来管理事务。
如果一个持久jdo对象在处理期间被修改,在应用程序提交事务时相关的修改被存入数据库。 一旦事务回滚,将不会有任何相关的修改存入数据库。
一旦事务处理完成,对象将再一次变回瞬时jdo对象。为了在两种不同的事务处理中使用相同的对象,你必须再一次提交查询来生成它。
一个jdo对象是属于瞬时jdo对象还是持久jdo对象取决于当时的数据库处理是处在那个事务处理中。在实际环境中,往往会发现一个jdo对象在一个的数据库连接中属于持久jdo对象,而在另一个数据库连接将返回调用ispersistent(java.lang.object)却返回false(就是说它是瞬时jdo对象)。这使得可以让一个jdo对象同时在2个数据库连接属于持久态:在一个数据库连接查询它在另一个数据库连接创建它。
oql查询
oql查询通常用于在数据库中查找并创建一个jdo对象. oql查询盒类似于sql查询,不过用对象名称而不是 sql名称并且不支持join子句。 例如,如果所装载的对象是testobject类,oql查询将从testobject加载,而不管数据库的实际的表名称是test,test_object还是任何其它名称。如果相关的对象需要join操作,castor将自动地执行join操作。 下列的代码片断使用oql查询来将所有对象装载在一个给定的组中。 注意,产品表和产品组表是相关的对象,jdbc查询包括了join操作:
oqlquery oql;
queryresults results;
// construct a new query and bind its parameters
// 建造一个新的查询并且设定其参数
oql = db.getoqlquery( "select p from product p where group=$1" );
oql.bind( groupid );
// 取回结果并且逐个打印
results = oql.execute();
while ( results.hasmore() ) {
system.out.println( results.next() );
}
下列的代码片断使用上面的查询结果来获得product对象,把他们的价格减少25%,并且把修改的结果存储到数据库(本例采用client应用模式):
while ( results.hasmore() ) {
product prod;
prod = (product) results.next(); //获得product对象
prod.markdown( 0.25 ); //价格减少25%
prod.setonsale( true );
}
// 明确地提交事务处理
db.commit();
db.close();
如前面所说明的,查询以三个步骤被执行。 首先, 使用oql语句从数据库中创建查询jdo对象。 如果有参数,那么第二个步骤包括设定这些paramaters。 参数按相同的顺序出现在oql语句中。第三个步骤包括执行查询和获得 org.exolab.castor.jdo.queryresults 类型的结果集合。
查询能创建一次多次执行。 每次它执行完成相关的参数自动消失,第二次查询必须再次提供。 当查询第二次被执行时(有可能得到是不同的查询结果),上次查询的结果(旧的)还是能继续使用。
查询的一种特殊的形式提供了调用存储过程的可能性。
如:
oql = db.getoqlquery( "call sp_something($) as myapp.product" );
这里sp_something是一个存储过程返回一个或更多resultset对象,这些对象的顺序和oql查询“select p from myapp.product p”的得到的顺序是相同的。(对于一个没有关联的数据库他的顺序是首先按照唯一主键排列其次按照mapping.xml中指明的字段顺序排列)
jdo对象的插入,删除和修改
方法create(java.lang.object)在数据库中创建一个新的对象或者用jdo术语来说是:′持久化瞬时对象。′ 以create方法创建的对象仍然是在提交数据库事务中被存储入数据库。一旦提交数据库回滚事务,那么会在数据库中上删除这条纪录。如果发生主键冲突,那么将抛出(identity already exists in the database)异常。 下列的代码片断创建一种新的 塑料转椅对象(属于产品类) 并把它关联到以前查询的得到的 furnitures (产品归类) 对象中去:
product prod;
// 创建一个塑料转椅对象
prod = new product();
prod.setsku( 5678 );
prod.setname( "塑料转椅" );
prod.setprice( 55.0 );
prod.setgroup( furnitures ); //加入到产品归类的家具归类实例中
// make is persistent
db.create( prod ); // 存储
remove方法(java.lang.object)是和create方法相反的操作。是删除一个持久的jdo对象。 一旦提交数据库事务处理,该对象就会在数据库中被删除, 同时在全部其他事务处理中消失。
不过,一旦提交回滚,那么对象将仍然保留在数据库中。
还有如果试图删除的纪录不存在,那么将抛出异常。
使用jdo和xml
xml在实际应用中越来越重要,可以说它的重要性仅次于database。结合执行castor jdo和castor xml能使得以xml作为输入和输出的形式来处理数据库操作等等事务。 这样能够大大提升你的软件的应用范围,同时并不需要付出很大的代价。
下列的代码片断结合持久jdo对象和瞬时do对象来描述一种金融的操作。 在这个例子中,我们要作的是:把金额从一个账号转账到另外一个账号中。 这两个账号对象原来是存在数据库中的。转账处理完毕后还必须把转账过程用xml文件表达出来。
在下面的实现中,我们将转账过程使用瞬时对象(即在数据库中没有记录对应)描述,然后被用来产生描述转账过程的xml文件。 通过调用一个额外程序(篇幅限制,这里不作详细解释)用xslt来把xml文件转变成为html页格式来描述转账过程。
transfer tran;
account from;
account to;
oqlquery oql;
tran = new transfer();
// 建造查询并且装载两个账号
oql = db.getoqlquery( "select a from account a where id=$" );
oql.bind( fromid );
from = oql.execute().nextelement();
oql.bind( toid );
to = oql.execute().nextelement();
// 转账处理
if ( from.getbalance() 〉= amount ) {
from.decbalance( amount );
to.incbalance( amount );
trans.setstatus( transfer.complete );
trans.setaccount( from );
trans.setamount( amount );
} else {
// 金额不够,转账失败
trans.setstatus( transfer.overdraft );
}
// 生成xml描述
marshaller.marshal( trans, outputstream );
运行上述代码产生的xml看起来有可能象下面的样子:
〈 ?xml version="1.0"?〉
〈 report〉
〈 status〉completed〈 /status〉
〈 account id="1234-5678-90" balance="50" / 〉
〈 transfer amount="49.99" / 〉
〈 /report〉
闽公网安备 35060202000074号