索引
2.
功能特性
3.
简单示例
4.
JDO操作详解
1)
建立数据库连接
i.
JDO用于独立应用
ii.
JDO用于J2EE应用
i.
暂存对象与持久化对象
ii.
执行OQL查询
iii.
创建持久化对象
iv.
删除持久化对象
v.
更新持久化对象
3)
同时使用JDO和XML
5.
JDO数据库配置文件
2)
JDO数据库配置概述
3)
事务声明
i.
本地事务
ii.
全局事务
4)
不同数据库配置示例
1. Castor JDO 概述
Castor JDO是一个纯Java的对象关系(O/R)映射和数据绑定框架。Castor使用用户定义的映射信息在关系数据库和Java对象之间建立起映射转换关系。从另一个层面说,Castor JDO为Java对象提供了一个数据持久层。
Castor JDO对于Java对象的操作被自动转换为对数据库的查询,更新,插入和删除操作。在事务边界中,对于Java对象的修改,在执行提交(commit)操作时自动提交给数据源。因此, Java开发人员在构建了数据库表和映射信息后,视角的重心从数据库操作层面转移到Java对象的操作上。Castor JDO将数据的序列化和业务逻辑分离为更好的进行面向对象的设计提供了支持,这在大型项目中特别重要。
Castor JDO采用类似于Castor XML样式的Mapping映射文件。下文我们将详述Castor JDO的概念与应用。
2. 功能特性
Ø Castor JDO是一个事务框架。当数据对象被Castor加载后,Castor会对该数据对象进行锁定并对其他事务隔离。Castor通过
xa.Synchronzation支持完整的两阶段事务提交。Castor提供数据对象的“shared”、“exclusive”、“database locked”和“read-only”等多种数据读写策略。
n 共享读(shared),Castor默认的数据锁定策略,在多个事务查询同一数据对象,更新操作作用于各自的对象拷贝时适用。
n 排斥读(exclusive),Castor提供一个内存锁,多个事务在同一数据对象上执行更新操作时,以竞争的方式获取内存锁来完成对象的更新操作。
n 数据库锁(dabase locked),此策略应用于多个应用程序同时访问数据库数据的场景,如果其他应用程序使用Castor以外的其他方式访问数据库,则考虑使用该策略。
n 只读(read-only),不对数据做任何锁定。使用该策略的所有数据对象都不加锁,这些对象也不参与事务提交和回滚。
Ø 支持“长时间事务(long transactions)”,通过内置的数据“脏检查(dirty-checking)”防止用户在初始事务中对数据变更后被重复修改,从而允许用户在一个事务内读取对象,修改更新,然后在第二个事务中提交。通过自动的数据“脏检查(dirty-checking)”和死锁侦测,Castor可以确保数据的一致性并减少不必要的数据库更新操作。
Ø 支持OMG(Object Management Group)3.0对象查询语言规范OQL的一个子集用来与数据库进行交互。OQL与SQL类似,但其操作作用于Java对象而不是数据库表。
Ø 实现了一个数据缓存池来减少数据库的频繁访问,同时提供了几种可选的LRU类型的缓存策略。
Ø 支持一对一,一对多,多对多等多种数据库关系,支持对象和数据库记录的继承。Castor区分对象数据生命周期中的关联(如联合)和依赖(如聚合)关系,在各自对象的生命周期中,自动创建和删除依赖的对象数据。
Ø 支持数据库表多主键,并提供多种主键生成器。
Ø 自动处理包含Java集合类型的对象的存储,包括Vector,Hashtable,Collection,Set和Map。通过“延迟加载”减少不必要的数据库访问。延迟加载可以在每一个集合类型的字段上设定。
Ø 支持Java对象类型与数据库类型间的自动转换。
Ø Castor JDO在同一个应用中使用多个类加载器,使得其可以在EJB容器或Servlet容器中使用。
Ø Castor XML框架和Castor JDO可以无缝集成使用,同时处理O/R映射和Java对象-XML 数据绑定。
Ø Castor JDO支持以下数据库系统:
n DB2
n Derby
n Generic DBMS
n Hypersonic SQL
n Informix
n InstantDB
n Interbase
n MySQL
n Oracle
(8.1.x,9.x,10g)
n PostgreSQL
n Progress
n SAP DB / MaxDB
n SQLServer
n Sybase
用户可以针对特定数据库提供自己的JDBC驱动接口实现插入到Castor中,前提是该数据库支持JDBC2.0,多结果集(ResultSet)和事务。
3. 简单的示例
下面我们以一个简单的示例来展示Castor JDO使用过程中的一些关键要素。类似于Castor XML,Castor JDO中用户需要提供数据实体的Java类定义,Java类模型与数据库表结构的映射信息。此外,在多数情况下用户还要提供数据库配置文件。在Castor XML中,用户使用Marshaller和Unmarshaller编组和解编对象/XML数据,在Castor JDO中,用户使用JDOManager持久化和读取Java对象/关系数据。
首先,我们定义两个Java类:产品分类Catalogue和产品Product。每一产品分类可以存在多种产品。
|
为了实现上述Java对象的持久化操作,如查询产品,从一种产品分类中删除产品等,我们需要定义Java类与数据库表及列之间的映射关系。Castor JDO Mapping文件与Castor XMLMapping文件具有相同的结构,如下所示:
|
定义了Castor JDO Mapping
文件后,我们即可以使用Castor JDOManager
实现Java
对象与数据库表记录之间的互操作。在此之前我们先看一下Castor JDO Configuraton
文件,在此文件中定义了数据库连接配置信息。Castor JDO
配置文件不是必须的,Castor JDO
提供了使用API
直接设定数据连接参数。
|
在下述代码中,Castor JDO
读取数据库配置信息并构造一个名为“sample
”的JDOManager
实例;然后通过getDatabase()
方法获取与数据库的连接,从数据库中获取一个产品分类的ID
(主键)值为“1
”的记录并加载成一个产品分类对象,最后提交并关闭与数据库的连接。
|
4. JDO操作详述
4.1连接数据库
Castor JDO支持独立应用和J2EE应用两种运行环境。独立应用需要配置数据库连接并显式管理事务,J2EE应用使用JNDI获取一个预先配置的数据库连接并使用
UserTransaction或容器控制的事务(CMT)来处理事务。
4.1.1 JDO用于独立应用
在独立的Java应用中,用户需要自己定义JDO数据配置信息,控制事务及其调用。JDO数据库配置信息使用一个独立的XML配置文件定义,并在其中指明映射文件。用户也可以使用
org.exolab.castor.jdo.util.JDOConfFactory类在程序代码中设定数据库配置。关于JDO数据库的详细配置请参考后续文档。
org.exolab.castor.jdo.JDOManager设定要构造数据库实例名称及其属性,用来与数据库建立连接。JDOManager的构造过程分为两步:
Ø 使用JDOManager的静态方法
loadConfiguration()加载数据库配置信息。
Ø 使用静态方法
createInstance(java.lang.String)方法构造一个JDOManager实例,createInstance()方法接收一个String类型的参数,用于指明第一步加载的数据库配置信息中定义的数据库名称。
Castor JDO使用org.exolab.castor.jdo.Database对象表示与数据库的一个连接。Database实例是非线程安全的,用户不应该在并发线程中使用。同时构造多个Database对象实例的开销很小,Castor JDO在每一个开放的事务中获取一个连接。
下面的代码为名称为“mydb”数据库构造一个JDOManager实例, 打开一个数据库连接,开始一个事务,然后关闭数据库连接。实际使用中,用户应该进行必要的异常处理。
|
4.1.2 JDO用于J2EE Application
J2EE
应用程序依赖于J2EE
容器来配置数据库连接并使用JNDI
查找配置。Castor JDO
在J2EE
应用系统中不希望直接构造JDOManager
实例用JNDI
对其进行查找,而是推荐用户将JDOManager
实例添加到java:comp/enb/jdo
名称空间下,以遵循查找JDBC
资源的惯例。下述代码展示了用JNDI
查找一个Database
实例并使用JTA UserTransaction
接口来控制处理事务:
|
对于EJB实体Bean,如果事务是由容器控制控制处理,则没有必要显式的调用begin/commit方法,这一切都由应用服务器来控制开始一个事务,并在合适的时候执行commit/rollback方法。下面的代码展示了容器控制下的事务:
|
4.2使用JDO Database执行持久化操作
4.2.1 暂存对象与对持久化象
所有的JDO
操作都在一个事务上下文之内。JDO
将数据从数据库加载到内存对象,允许对对象进行修改,然后在事务提交时将对象的新状态持久化到数据库。所有的对象可以分为两种状态:暂存(transient)
和持久化(persistent)
。
Ø 暂存(transient)
:
当事务提交时,不需要将对象信息持久化到数据库中的对象。对暂存对象的修改不会在数据库中体现。
Ø 持久化(persistent)
:当事务提交时,需要将对象信息持久化到数据库中的对象。对持久化对象的修改将会在数据库中体现。
在两种情况下,一个对象要成为可持久化的:该对象是一次查询的结果(而且该查询不是在read-only
策略下执行的)或者该对象是使用JDO Database
实例的create(java.lang.Object)
或update(java.lang.Object)
方法添加到数据库的。所有不可持久化的对象都是暂存对象。当事务提交或回滚后,所有可持久化对象都变为暂存对象。
在独立Java
应用中,使用begin()
,commit()
和rollback()
方法来控制事务。如果一个持久化对象在事务内部被修改,在提交阶段所有的修改会更新到数据库。如果事务回滚,对数据库数据所做的所有更改将撤销。当事务完成后,该对象由持久化转入暂存状态,因此,如果在两个不同的事务中使用同一个对象,必须再一次执行查询。
对象的持久化与暂存状态是相对于事务所属的JDO Database
实例来说的。如果一个对象在某一JDO Database
实例中是可持久化的,在另一个JDO Database
实例中调用方法isPersistent(java.lang.Object)
时返回false
。将一个对象持久化到两个数据库中可以通过在一个数据库中查询并在另一个数据库中重新构建来实现。
4.2.2 执行OQL查询
OQL
查询用于从数据库中查询对象。OQL
查询类似于SQL
查询,但在查询语句中不是使用SQL
名称而是对象名称,而且OQL
不需要联合查询(join)
语句。例如,如果对象以TestObject
类型加载,则OQL
查询使用“FROM TestObject
”语句而不用关心该对象所对应的数据库表名称为”test”,”test_object”
或其他。在SQL
中,如果需要查找关联表必须使用SQL
的关联查询来实现;在OQL
中,加载关联对象时Castor
会自动完成关联查询。下述代码使用OQL
从产品(Product)
数据表中查询属于某一组的所有对象。注意:产品和产品组是关联对象,使用JDBC
查询时需要使用join
联合查询:
|
下述代码使用上面代码的查询获取所有的产品,并修改这些产品的价格减少到原有价格的25%然后重新写入数据库。
|
正如上述代码所示,在Castor JDO中,一次查询分为三个步骤。首先使用OQL语句从数据库中查询对象;然后,如果存在任何查询参数,第二步是绑定这些参数,有限的参数按照其顺序依次绑定(如$1为第一个参数,$2为第二个参数,依次类推)。第三步是执行查询并获得类型为
org.exolab.castor.jdo.QueryResult的查询结果。
一个查询在创建后可以执行多次。每次执行后,该查询所绑定的参数将会丢失必须再次进行绑定。查询的结果可以在执行新一次查询后继续使用。Castor JDO也支持调用数据库存储过程:
oql = db.getOQLQuery("CALL sp_something($) AS myapp.Product");
上面的语句中sp_something是一个存储过程并返回一个或多个ResultSets,这些结果集的返回顺序与Castor生成的SELECT语句“SELECT p FROM myapp.Product p”相同。对于没有关系的对象,返回顺序为:主键,然后按Mapping映射文件中指定的字段顺序排序。
4.2.3 创建持久化对象
JDO Database
类中的create(java.lang.Object)
实例方法用于在数据库中构建一个新的对象,以JDO
术语来说是将暂存对象持久化。使用create()
方法构造的对象在事务提交后仍存在于数据库中;如果事务回滚则该对象将从数据库中删除。如果在数据库中创建具有相同主键的对象将会抛出异常。下述代码使用前述查询的产品组创建一个新的产品:
|
4.2.4 删除持久化对象
JDO Database
类中的remove(java.lang.Object)
方法从数据库中删除持久化数据对象。如果一个对象被删除,则该对象对所有的事务都不可见。如果事务提交,该对象将从数据库中彻底删除;如果事务回滚,则该对象仍然存在于数据库中。当尝试删除一个非持久化的对象时将抛出异常。下面的代码删除前述构建的产品实例:
|
4.2.5 更新持久化对象
在org.exolab.castor.jdo.Database
类中没有特定的方法用于更新现有的持久化对象。更新持久化对象的过程可以通过加载需要更新的对象,修改其属性然后提交事务实现。Castor JDO
会自动确定要提交的对象是否存在变更,如果存在则将变更持久化到数据库。下述代码加载前述创建的产品实例,然后变更其产品描述信息,最后提交事务将变更持久化回数据库。
|
4.3同时使用JDO和XML
Castor JDO
与Castor XML
的结合可以使XML
作为数据库数据的输入和输出形式。下面的代码用持久化和暂存对象描述了一次账户转存操作。在这次金融操作中存在两个账户对象,操作将一个账户的金额部分转入另一个账户。示例中使用Transfer
描述一个暂存的对象,该对象在数据库中并没有对应的记录。操作完成后使用Castor XML Marshaller
将这次账户转存操作的描述用XML
文档输出。
|
上述代码生成的XML文档如下所示:
|
5. JDO数据库配置文件
默认方式下,Castor JDO
访问一个特定的数据库时使用数据库配置文件获取配置信息。该配置文件详细描述了从某一数据库服务器上获取一个连接的信息以及在该数据库服务器上的数据表与Java
类之间的映射信息。用户也可以使用JDOConfFactory
来编程设定数据库配置。
5.1JDO数据库配置Schema定义
Castor JDO
数据库配置XML Schema
定义如下:
<?xml version="1.0" encoding="UTF-8"?>
<
xsd:schema
xmlns:xsd
="
http://www.w3.org/2001/XMLSchema
"
targetNamespace
="
http://castor.org/JDO
"
xmlns
="
http://castor.org/JDO
"
>
<
xsd:element
name
="
jdo-conf">
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="
database
"
minOccurs
="
1
"
maxOccurs
="
unbounded
"
/>
<
xsd:element
ref
="
transaction-demarcation
"
minOccurs
="
0
"
maxOccurs
="
1
"
/>
</
xsd:sequence
>
<
xsd:attribute
name
="
name
"
type
="
xsd:string
"
use
="
optional
"
default
="
jdo-conf
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
database">
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:choice
>
<
xsd:element
ref
="
driver
"
/>
<
xsd:element
ref
="
data-source
"
/>
<
xsd:element
ref
="
jndi
"
/>
</
xsd:choice
>
<
xsd:element
ref
="
mapping
"
minOccurs
="
1
"
maxOccurs
="
unbounded
"
/>
</
xsd:sequence
>
<
xsd:attribute
name
="
name
"
type
="
xsd:ID
"
use
="
required
"/>
<
xsd:attribute
name
="
engine
"
type
="
xsd:string
"
use
="
optional
"
default
="
generic
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
driver">
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="
param
"
minOccurs
="
0
"
maxOccurs
="
unbounded
"/>
</
xsd:sequence
>
<
xsd:attribute
name
="
url
"
type
="
xsd:string
"
use
="
required
"
/>
<
xsd:attribute
name
="
class-name
"
type
="
xsd:string
"
use
="
required
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
data-source">
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="
param
"
minOccurs
="
0
"
maxOccurs
="
unbounded
"/>
</
xsd:sequence
>
<
xsd:attribute
name
="
class-name
"
type
="
xsd:string
"
use
="
required
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
jndi">
<
xsd:complexType
>
<
xsd:attribute
name
="
name
"
type
="
xsd:string
"
use
="
required
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
mapping">
<
xsd:complexType
>
<
xsd:attribute
name
="
href
"
type
="
xsd:string
"
use
="
required
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
transaction-demarcation">
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="
transaction-manager
"
minOccurs
="
0
"
maxOccurs
="
1
"
/>
</
xsd:sequence
>
<
xsd:attribute
name
="
mode
"
type
="
xsd:string
"
use
="
required
"/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
transaction-manager">
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="
param
"
minOccurs
="
0
"
maxOccurs
="
unbounded
"/>
</
xsd:sequence
>
<
xsd:attribute
name
="
name
"
type
="
xsd:string
"
default
="
local
"
/>
</
xsd:complexType
>
</
xsd:element
>
<
xsd:element
name
="
param">
<
xsd:complexType
>
<
xsd:attribute
name
="
name
"
type
="
xsd:string
"
use
="
required
"
/>
<
xsd:attribute
name
="
value
"
type
="
xsd:string
"
use
="
required
"
/>
</
xsd:complexType
>
</
xsd:element
>
</
xsd:schema
>
Castor
名称空间 URI
:
http://castor.org/
。用户如果希望校验JDO
数据库配置文件,使用如下设定:
|
Castor JDO
数据库配置XML
文档举例如下:
<?xml version="1.0"?>
<
jdo-conf
>
<
database
name
="
Supplier-Update
"
engine
="
oracle
">
<
driver
class-name
="
oracle.jdbc.driver.OracleDriver
"
url
="
jdbc:oracle:thin:@192.168.0.1:1521:MyDB
">
<
param
name
="
user
"
value
="
test
"/>
<
param
name
="
password
"
value
="
test
"/>
</
driver
>
<
mapping
href
="
TestMapping.xml
"/>
<
mapping
href
="
OtherMapping.xml
"/>
</
database
>
<
transaction-demarcation
mode
="
local
">
<
transaction-manager
name
="
local
"/>
</
transaction-demarcation
>
</
jdo-conf
>
J2EE
配置方式举例:
<?xml version="1.0"?>
<
jdo-conf
>
<
database
name
="
ebiz
"
engine
="
oracle
">
<
jndi
name
="
java:comp/env/jdbc/mydb
"/>
<
mapping
href
="
ebiz.xml
"/>
</
database
>
<
transaction-demarcation
mode
="
global
">
<
transaction-manager
name
="
jndi
">
<
param
name
="
jndiEnc
"
value
="
java:comp/env/TransactionManager
"/>
</
transaction-manager
>
</
transaction-demarcation
>
</
jdo-conf
>
如上例所示,Castor JDO
使用数据库配置文件访问特定数据库,在配置中<
database
>
节点的
name
属性指定了访问该数据库的名称,其子节点<
mapping>
指定了该数据库上的对象关系映射信息.
engine
属性指定了数据库服务的类型。不同的数据库系统Castor
为
engine
属性定义了不同的值,Castor
支持的数据类型的
engine
名称如下表所示:
Engine
名称
|
数据库类型
|
Engine
名称
|
数据库类型
|
db2
|
DB2
|
mysql
|
MySQL
|
derby
|
Derby
|
oracle
|
Oracle7-Oracle 10g
|
generic
|
Generic JDBC Support
|
postgresql
|
PostgreSQL
|
hsql
|
Hypersonic SQL
|
sapdb
|
SAP DB/MaxDB
|
informix
|
Informix
|
Sql-server
|
Microsoft SQL Server
|
instantdb
|
InstantDB
|
sybase
|
Sybase 11
|
interbase
|
Interbase
|
pointbase
|
Borland Pointbase
|
progress
|
Progress RDBMS
|
|
|
注意:Castor不支持Sun的JDBC-ODBC桥,因此不支持MS Access 。
5.2 JDO数据库配置概述
JDO数据库配置中存在三种方式设定数据库连接:设定JDBC驱动的URL;设定JDBC DataSource数据源;以JNDI方式查找JDBC数据源。Castor应用在J2EE应用服务器中时,推荐使用JNDI查找(参考jndi节点),让应用服务器管理连接池和分布式事务。
同一database配置中允许指明多个数据映射定义文件,两个数据库配置可以共享同一个数据映射定义文件。考虑到并发和数据一致性的原因,两个数据库配置不应该使用重叠的映射文件。理想情况是每一个数据库服务器使用一个数据库配置。
数据映射定义文件使用URL指定路径,通常情况是使用file:URL。如果数据库配置文件与映射定义文件在同一个目录下,则可以使用相对路径URL。相对路径URL在数据库配置文件和数据映射定义文件从JAR文件获取且位于相同的类路径(ClassPath)下时也可以使用。
<driver>节点指定从数据库服务器获取一个新连接所使用的JDBC驱动类型(
class-name)和位置(
url),其他属性设定使用<
param>节点。
下面是Oracle8i JDBC驱动
(type 4)的配置示例:
|
<data-source>节点指以JDBC 数据源的方式获取数据库连接。DataSource在JDBC2.0规范中定义。
class-name属性指定JDBC数据源的实现类全名称。数据源的属性配置使用类似Java Bean的getter/setter方法由
<param>节点配置设定。下面是PostgreSQL 7.1 DataSource配置示例:
|
<jndi>节点指定使用JNDI查找方式获取JDBC数据源实例。下面是J2EE数据源设定的示例:
|
5.3事务声明
Castor
在使用JDO
操作数据库数据时,使用JDO
配置文件配置事务声明。
<transaction-demarcation>节点的mode属性值可以设置为local或者global,分别代表着两种事务处理模式。
5.3.1 本地事务(Local Mode)
当Castor JDO
在独立的Java
应用中使用时,需要Castor
控制事务的声明,
<transaction-demarcation>节点的配置如下:
|
5.3.2 全局事务(Global Mode)
当Castor
运行于J2EE
应用服务器时,如果使用全局事务(XA
事务),
<transaction-demarcation>节点的配置如下:
|
在此模式下,<transaction-manager> 节点用于确定J2EE容器用于控制这些事务的事务管理器。Castor支持的事务管理器如下表所示:
|
除了设定事务管理其名称外,可以在<transaction-manager>节点内添加<param>子节点,用于设置事务管理器的名称/键值对属性。注意:目前只有JNDI事务管理器支持此种属性设定。在次上下文中,使用jndiEnc属性可以指定使用那种JNDI ENC来查找事务管理器。配置示例如下:
|
5.4不同数据库的配置示例
|
|
|
|
|
5.5以编程方式配置Castor JDO
很多应用系统使用不同的用户账户来连接数据库,为实现此种需求,Castor
提供了帮助类JDOConfFactory
和JDOManager.loadConfiguration(org.exolab.castor.jdo.conf.JdoConf)
方法。下面的代码示例展示了在不使用数据库XML
配置文件的情况下设定JDO
数据库配置:
|
下面代码以另一种方式通过使用org.exolab.castor.jdo.conf.JdoConf来配置JDBC 2.0 DataSource:
|