![]() |
![]() |
This page will help you to edit JDO 2 persistent descriptors: .jdo files. It is structured as the following outline :
Back to the Speedo documentation
The .jdo files describe the classes of your application are persistent. JDO specification requires the definition of one .jdo file per java package or one .jdo file per persistent class. In first case the file name of the .jdo file must be 'package.jdo' whereas in the second case the name of the .jdo file must be the name of the unique persistent class described.
For each class the developer must specify the persistent field. By default primitive fields (int, Integer, String, ...) are persistent. Since JDO 2.0 the O/R mapping is standard. In previous version of specification the mapping was defined with the help of extensions specific to each JDO provider.
Below is an example of .jdo file describing the persistence of the class 'simpleclass.Address'. This class has 5 persistent fields.
<!DOCTYPE jdo PUBLIC "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN" "jdo2.dtd"> <jdo> <package name="simpleclass"> <class name="Address" identity-type="application" table="SIMPLE_ADDRESS"> <field name="street" primary-key="true" column="STREET"/> <field name="city" column="CITY"/> <field name="state" column="STATE"/> <field name="zip" column="ZIPCODE"/> <field name="deliveryInstructions"> <column name="DELIV_INS" sql-type="VARCHAR(50)"/> </field> </class> </package> </jdo>
The 'class' xml node defines a persistent class as sub node of 'package'. At class level the 'table' attribute specifies the main table where the class is stored.
<class name="Address" table="SIMPLE_ADDRESS"> ... </class>
If no table are specified, Speedo will compute automaticaly a table name from the short name of the persistent class.
A persistent class must have an identifier format. The JDO specification defines two kinds of identifier:
Speedo supports 4 types of identifiers:
<class name="Invoice" identity-type="application"> <field name="number" primary-key="true"/> </class>
<class name="Address" identity-type="application
identity-type="application"
objectid-class="invoice.AddressID">
<field name="name" primary-key="true"/>
<field name="street" primary-key="true"/>
<field name="town" primary-key="true"/>
</class>
class AddressID {
public String name;
public String street;
public String town;
...
}
These identifier values are computed from a persistent generator managed by Speedo and stored in the database. The identifier is a single long. The 20 high bits identify the class whereas the 44 low bits identify the object instance. This identifier supports inheritance. To use this identifier you only have to specifiy any identity type because 'datastore' is the default value.
<class name="A"> ... </class>
This naming is interesting if your relational database does not support
sequence or if you have inheritance in your data model.
In details: The identifier generators are persistent into the table
'jf_longgen' whereas the class identifiers are stored into the table 'jf_classid'.
Even if the identifier is allocated by a speedo you can want to have access to the identifier field. You can declare a persistent field as primary which the type is long or java.lang.Long. If you choose 'long' the null references are stored with the -1 value. If you choose 'java.lang.Long' the null references are with the NULL SQL value.
<class name="MyClass" identity-type="datastore"> <datastore-identity strategy="sequence" sequence="my_seq"/> <field name="other_field"/> </class>
<class name="MyClass" identity-type="application"> <field name="id" primary-key="true" value-strategy="sequence" sequence="my_seq"/> <field name="other_field"/> </class>
<class name="A" identity-type="datastore">
<extension vendor-name="speedo" key="id" value="sequence"/>
</class>
<class name="A" identity-type="datastore">
<extension vendor-name="speedo" key="sql-seq-name" value="SEQ_TABLE_A"/>
</class>
Even if the identifier is allocated by a sequence you can want to have access to the identifier field. You can declare a persistent field as primary which the type is long or java.lang.Long. If you choose 'long' the null references are stored with the -1 value. If you choose 'java.lang.long' the null references are with the NULL SQL value.
NOTE: The other datastore identity types (autoassign, increment, uuid-string and uuid-hex) will be supported later.
With Speedo a field is a primitive field when its types is a primitive type or a wrapper to a primitive type. Here is the list of field types manages as primitive type in Speedo:
Its is not required to declare primitive fields in the .jdo file, excepted if you want to specify constraints or column name. In the .jdo file a persistent field is declared thanks to the 'field' xml node (sub node of 'class' node) Here a simple example.
<field name="myField"/>
JDO 2 specification defines several attributs to declare constraints:
In relational support a primitive field is mapped over a single column. The column definition can be specified with the sub xml node column as shwon by the following example:
<field name="myField"> <column name="MY_COL" sql-type="VARCHAR(28)"/> </field>
On this column tag, the 'sql-type', the 'size' or the 'scale' can be specified.
The 'sql-type' is the SQL column type.
The 'size' attribut is useful for String, BigDecimal or BigInteger
fields. For BigXXX fields, this property represents the number of
digits (integer + decimal). For String fields, the attribut represents
the maximal String length. This attribut is ignored if the sql-type attribut
is specified.
The 'scale' attribut is useful for float and double field. This attribut
is ignored if the sql-type attribut is specified.
An extension named 'field-converter' permits to change the usual translation between the memory type and the data support type for a primitive field. For example, if the user has a legacy relationnal database, it can map an integer field into a String (VARCHAR on a RDB). To do this, the user must implement the org.objectweb.speedo.api.UserFieldMapping interface provided by Speedo. This implementation is able to convert a memory value to a data store value and reversly. Finally, the user must indicate, as value of the extenstion 'field-converter', the class name of the interface implementation.
<field name="f1">
<extension vendor-name="speedo" key="field-converter"
value="com.foo.bar.String2intConverter"/>
</field>
And here is the source code of the String2intConverter class:
package com.foo.bar;
public class String2intConverter implements UserFieldMapping {
public Class getStorageType() {
return String.class;
}
public Class getMemoryType() {
return Integer.TYPE;
}
public Object toMemory(Object storagevalue) {
return Integer.valueOf((String) storagevalue)
}
public Object toStorage(Object memoryvalue) {
return (memoryvalue == null ? null : memoryvalue.toString());
}
}
The extension named 'user-cache' permits to specify that a primitive field is a part of an unique index in an user cache. For more details about the user cache feature see the corresponding chapter. The value of the extension is the name of the user cache. If the index of the cache is composite, you simply have to specify the extension for each field with the same user cache name. A class can have several unique indexes.
The following examples a class containg 4 fields. The fields 'id' is the identifier (primary key). The couple (f1,f2) is a secondary key. The field 'f3' is another key.
<field name="id" primary-key="true"/>
<field name="f1">
<extension vendor-name="speedo" key="user-cache"
value="myCache1"/>
</field>
<field name="f2">
<extension vendor-name="speedo" key="user-cache"
value="myCache1"/>
</field>
<field name="f3">
<extension vendor-name="speedo" key="user-cache"
value="myCache2"/>
</field>
A reference field is a field which references one persistent instance. In case of there is no bidirectional relationship (no reverse field), the reference to the other persistent class is mapped over column in tables of the field owner class. In other words the foreign-key is in the table of class owning the reference. The column corresponding to the foreign key can be defined with 'column' attribute or sub xml node. Below is a simple example of showing a reference from the class 'A' to the class 'B'. The class 'A' is stored on the 'TA' table whereas the class 'B' is stored on the 'TB' table.
class A { String myIda; B myB; } class B { long myIdb; } |
![]() ![]() |
Below is the corresponding jdo file part describing both persistent class and the mapping of the reference:
<class name="A" identity-type="application" table="TA"> <field name="myIda" primary-key="true" column="IDA"/> <field name="myB" column="FK_IDB"/> </class> <class name="B" identity-type="application" table="TB"> <field name="myIdb" primary-key="true" column="IDB"/> </class>
The 'column' defines the name of the foreign key column (FK_IDB). As the
referenced class has an identified composed of a single column (IDB), the
correspondance is implicit.
If the identifier of the referenced class is composite, you have to specify
the target column for each column of the foreign key as shown by the
following example:
<class name="A" identity-type="application" table="TA"> <field name="myIda" primary-key="true" column="IDA"/> <field name="myB"> <column name="FK_IDB1" target="IDB1"/> <column name="FK_IDB2" target="IDB2"/> </field> </class> <class name="B" identity-type="application" table="TB"> <field name="myIdb1" primary-key="true" column="IDB1"/> <field name="myIdb2" primary-key="true" column="IDB2"/> </class>
A multivalued field is a field referencing several primitive values or several persistent instances (mixing is forbiden). Speedo supports several types of structure (java.util.Collection, java.util.Set, java.util.List, ...). This chapter is divided in several parts corresponding to the content of multivalued field and the type of structure.
When a multivalued field references a collection of primitive values, an additional table is required. This table must be join to the main table of the field owner. Currently, Speedo supports only joins based on the identifier. Here is an example of a 'A' persistent class references a set of String value.
class A { String ida; Collection strings; } |
![]() |
<class name="A" > <field name="ida" primary-key="true" column="IDA"/> <field name="strings" table="T_SET"> <collection element-type="String"/> <element column="STR"/> <join column="FKIDA"/> </field> </class>
The sets of String are stored into the 'T_SET' table. The column corresponding to the element is STR. The join to this table is based on the identifier of the class. The foreign key column is 'FKIDA'. If the identifier of the owner class is composite the join declaration is a bit more complex:
<class name="A" > <field name="ida1" primary-key="true" column="IDA1"/> <field name="ida2" primary-key="true" column="IDA2"/> <field name="strings" table="T_SET"> <collection element-type="String"/> <element column="STR"/> <join> </column name="FK_IDA1" target="IDA1"/> </column name="FK_IDA2" target="IDA2"/> </join> </field> </class>
For each column of the foreign key, the target column has to be specified.
This case is the same than a reference to several primitive value except for the element definition. Be careful, if this field composes a bidirectional relation ship, you have to read the dedicated chapter about bidirectional relationship. Here is an example of a class 'A' having a collection of 'B' instance.
class A { String ida; Collection myBs; } class B { long idb; } |
![]() ![]() |
<class name="A" > <field name="ida" primary-key="true" column="IDA"/> <field name="myBs" table="TAB"> <collection element-type="B"/> <element column="FKIDB"/> <join column="FKIDA"/> </field> </class> <class name="B" > <field name="idb" primary-key="true" column="IDB"/> </class>
The sets of B are stored into the 'TAB' table. The column corresponding to the reference to B instances is FKIDB. This column implicitly references the identifier column of the referenced class (B). The join to the TAB table is based on the identifier of the class 'A'. The foreign key column is 'FKIDA'. If the identifier of the referenced class is composite the element declaration is a bit more complex:
<class name="A" > <field name="ida" primary-key="true" column="IDA"/> <field name="myBs" table="TAB"> <collection element-type="B"/> <element> <column name="FKIDB1" target="IDB1"> <column name="FKIDB2" target="IDB2"> </element> <join column="FKIDA"/> </field> </class> <class name="B" > <field name="idb1" primary-key="true" column="IDB1"/> <field name="idb2" primary-key="true" column="IDB2"/> </class>
For each column of the foreign key, the target column has to be specified.
A map defines an association between keys and their corresonding values. Speedo supports only primitive types as map key. In the other hand, the value of the map can be a primitive values or instances of a persistent class.
When the value type is primitive, the only possible mapping is to
have a table dedicated for the map storing.
The example shows a map referenced by the 'A' class
('mapOfStringIndexedByLong' field). The key of the map is a
Long value. The value type of the map is a String. Finally
the join to the map table is based on the identifier of 'A' class.
class A { long ida; Map<B,String> mapOfBIndexedByString; } |
<class name="A"> <field name="ida" primary-key="true" column="IDA"/> <field name="mapOfStringIndexedByLong" persistence-modifier="persistent" table="MAP_LONG_TO_STRING"> <map key-type="java.lang.Long" value-type="java.lang.String"/> <join column="FKIDA"/> <key column="LONG_KEY_COL"/> <value column="STRING_VALUE_COL"/> </field> </class> |
When value type are instance of persistent class, there are two cases of mapping for a Map field.
The example below shows a map referenced by the A class ('mapOfBIndexedByString' field). The map is stored on table 'MAP_TABLE_NAME'. The keys of the map are string valuevalue whereas the values are instances of B persistent class. The join to the map table is based on the identifier of 'A' class.
class A { long ida; Map<B,String> mapOfBIndexedByString; } class B { long idb; } |
<class name="A"> <field name="ida" primary-key="true" column="IDA"/> <field name="mapOfBIndexedByString" persistence-modifier="persistent" table="MAP_TABLE_NAME"> <map key-type="java.lang.String" value-type="B"/> <join column="FKIDA"/> <key column="STRING_KEY_COL"/> <value column="FKIDB"/> </field> </class> <class name="B" > <field name="idb" primary-key="true" column="IDB"/> </class> |
Secondly the map field is stored into the table of the persistent
class stored in value. In this case the key of the map is a field of
the class, and there is a bidirectional relationship between both classes.
The example shows a map referenced by the A class. The value type of
the map is the persistent class B. The key of the map is the 'f1' field
value of the B class.
<class name="A"> <field name="f1_to_B" persistence-modifier="persistent"> mapped-by="myA"> <map key-type="java.lang.String" value-type="B"/> <extension vendor-name="speedo" key="key-field" value="f1"/> </field> </class> <class name="B"> <field name="idb" column="IDB"/> <field name="f1"/> <field name="myA" column="FKIDA"/> </class>
When two classes have reference to each other, you have a bidirectional
relationship. In many cases, both fields share the same data structure in
the data support. For this reason, the JDO specification permits to define
only the mapping in one side of the raltion. The second one is defined from
the mapping of the first. For one-one or one-Many relations, a common
advice is to define the mapping of the reference having the foreign key. For
Many-Many relation, the mapping can be defined on side that you want.
In addition to define the mapping only one side, Speedo supports coherency of
relationship. This means that when you modify a field value, the
reverse field is automatically updated in order to keep the coherence of the
relation type.
This chapter has sections dedicated to each type of relation ship in order to
explain their mapping.
One-One relationship can use only one or two foreign keys. In first
case you have to define the foreign column on the side of the foreign key.
In the other reference, you simply specifies the 'mapped-by' attribut
with the name ofthe reverse field.
Here is an example of a One-One relationship between 'A' and 'B'
classes. The foreign key 'FKIDB' is in the 'TA' table. The mapping of the
field 'myA' is defined from the mapping of the field 'myB'.
class A { long ida; B myB; } class B { long idb; A myA; } |
![]() ![]() |
<class name="A" table"TA"> <field name="ida" column="IDA"/> <field name="myB"> column="FKIDB"/> </class> <class name="B" table"TB"> <field name="idb" column="IDB"/> <field name="myA" mapped-by="myB"/> </class> |
Here is another example of a One-One relationship between 'A' and 'B' classes. Here there is two foreign keys, one in each table.
class A { long ida; B myB; } class B { long idb; A myA; } |
![]() ![]() |
<class name="A" table"TA"> <field name="ida" column="IDA"/> <field name="myB"> column="FKIDB"/> </class> <class name="B" table"TB"> <field name="idb" column="IDB"/> <field name="myA" column="FKIDA/> </class> |
With a One-Many bidirectional relationship, the usual mapping is
to have one foreign key in the Many side of the relation. We strongly
advice you to define the mapping of the One side with simple columns
and to specify the 'mapped-by' on the Many side.
Here is an example of One-Many relation ship between 'A' and 'B'
classes. The 'A' class references a collection of 'B' instances whereas
the 'B' class reference a single A instance.
class A { long ida; Collection myBs; } class B { long idb; A myA; } |
![]() ![]() |
<class name="A" table="TA"> <field name="ida" column="IDA"/> <field name="myBs" mapped-by="myA"> <collection element-type="B"/> </field> </class> <class name="B" table="TB"> <field name="idb" column="IDB"/> <field name="myA" column="FKIDA"/> </class> |
With a Many-Many bidirectional relationship, there is one join table
and two foreign keys. The mapping is defined on only one side and
the mapped-by attribut is used on the other.
Here is an example of Many-Many bidirectional relationship between
the 'A' and 'B' classes. The mapping is only defined on the reference
'myAs' (table name and both foreign keys).
class A { long ida; Collection myBs; } class B { long idb; Collection myAs; } |
![]() ![]() |
<class name="A" table="TA"> <field name="ida" column="IDA"/> <field name="myBs" table="TAB"> <collection element-type="B"/> <join column="FKIDA"/> <element column="FKIDB"/> </field> </class> <class name="B" table="TB"> <field name="idb" column="IDB"/> <field name="myA" mapped-by="myBs"> <collection element-type="A"/> </field> </class> |
In case of legacy database schema, sometimes it is interesting to map a
persistent over several table. Currently Speedo supports this mapping if
the join conditions between tables are the identifier columns of the
persistent class.
Below is an example of a persistent class (Address) stored on 3 tables
(SECONDTAB_ADDRESS_MAIN, SECONDTAB_ADDRESS_DELIV, SECONDTAB_ADDRESS_MAP).
<class name="Address" identity-type="application" table="SECONDTAB_ADDRESS_MAIN"> <join table="SECONDTAB_ADDRESS_DELIV"> <column name="FK_D_STREET" target-column="STREET"/> </join> <field name="street" primary-key="true" column="STREET"/> <field name="city" column="CITY"/> <field name="state" column="STATE"/> <field name="zip" column="ZIPCODE"/> <field name="deliveryInstructions" table="SECONDTAB_ADDRESS_DELIV"> <column name="DELIV_INS" sql-type="VARCHAR(50)"/> </field> <field name="signatureRequired" table="SECONDTAB_ADDRESS_DELIV"/> <field name="mapJPG" table="SECONDTAB_ADDRESS_MAP" column="IMAGE"> <join column="FK_M_STREET" target-column="STREET"/> </field> </class>
The fields 'street', 'city', 'state' and 'zip' are stored on the main table (SECONDTAB_ADDRESS_MAIN). The fields 'deliveryInstructions' and 'signatureRequired' are stored on the table 'SECONDTAB_ADDRESS_DELIV'. Finally the field 'signatureRequired' is stored on the table 'SECONDTAB_ADDRESS_MAP'. The example shows both ways (at class level or at field level) to declare the secondary tables and their join. When secondary table is declared at field level, any other field of the class can be stored on this secondary table.
An inheritance tree can be mapped according to several strategy: filtered, horizontal, vertical. Here is an example of the inheritance strategies:
class A { String id; String f1; } class B extends A { String f2; }
Name | Description | Schemas of both classes |
Filtered | A single table contains all fields of all classes. | ![]() |
Horizontal | Each class has its own table containing its fields and its inherited fields. | ![]() ![]() |
Vertical | Each class has its own table containing field of the current class. This table has a join to the parent table. Inherited fields are not mapped again because parent table is used. | ![]() ![]() |
The parent class and inheritance strategy have to be defined in the .jdo file as shown by this example.
<class name="B" persistence-capable-superclass="A"> <inheritance strategy="superclass-table"/> ... </class>
Currently Speedo supports only two strategies of inheritance mapping: Filtered and horizontal. The next chapters describes details of each strategy.
This strategy stores the sub class in the same table than its parent. The common table is defined at parent level. Field mapping is defined once time.
<class name="A" table="TA"> <inheritance strategy="new-table"/> <field name="id" primary-key="true" column="ID"/> <field name="f1" column="F1"/> </class> <class name="B" persistence-capable-superclass="A"> <inheritance strategy="superclass-table"/> <field name="f2" column="F2"/> </class>
To distringuish class of instances a discriminator is required. There are three possibles case for this discriminator:
<class name="A" table="TA"> <inheritance strategy="new-table"> <discriminator column="ID2" stategy="value-map" value="toto"/> </inheritance"> <field name="id1" primary-key="true" column="ID1"/> <field name="id2" primary-key="true" column="ID2"/> <field name="f1" column="F1"/> </class> <class name="B" persistence-capable-superclass="A"> <inheritance strategy="superclass-table"> <discriminator column="ID2" value="titi"/> </inheritance"> <field name="f2" column="F2"/> </class>
<class name="A" table="TA"> <inheritance strategy="new-table"> <discriminator column="ID2" stategy="value-map" value="toto"/> </inheritance"> <field name="id1" primary-key="true" column="ID1"/> <field name="id2" column="ID2"/> <field name="f1" column="F1"/> </class> <class name="B" persistence-capable-superclass="A"> <inheritance strategy="superclass-table"> <discriminator column="ID2" value="titi"/> </inheritance"> <field name="f2" column="F2"/> </class>
<class name="A" table="TA"> <inheritance strategy="new-table"> <discriminator column="MY_COL" stategy="class-name"/> </inheritance"> <field name="id1" primary-key="true" column="ID1"/> <field name="f1" column="F1"/> </class> <class name="B" persistence-capable-superclass="A"> <inheritance strategy="superclass-table"> <discriminator column="MY_COL"/> </inheritance"> <field name="f2" column="F2"/> </class>
If you use the speedo identifier (long value auto computed), no discriminator is required because it is included into the long value. It is the best choice when identifier structure is not defined by the application.
With the horizontal strategy, each class has its table. Currently Speedo supports this strategy only if Speedo manages the identifier it already includes a discriminator. With this stategy, the sub class must define the mapping of inherited field. To distinguish fields, you have to add the class name as prefix (fully qualified name if not in the same package).
<class name="A" table="TA"> <inheritance strategy="new-table"/> <field name="f1" column="F1"/> </class> <class name="B" persistence-capable-superclass="A" table="TB"> <inheritance strategy="new-table"/> <field name="A.f1" column="F1"/> <field name="f2" column="F2"/> </class>
The JDO 2 specification permits to declare constraints and indexes in relational database. For the three type of declaration there are the same ways to define them.
The first method is to specify the corresponding attribut on the column or the field:
<class name="A" table="TA"> <field name="ida" primary-key="true" column="STREET"/> <field name="f1" unique="true"/> <field name="f2" index="true"/> <field name="f3"> ... <column name="COL_F3" unique="true"/> </field> </class>
The field can be a primitive field, a direct reference or a multivalued reference.
The second method is to declare a node at class level. This solution permits to include into the declaration several field or columns.It permits also to give a name of the constraint or the index.
<class name="A" table="TA"> <field name="ida" primary-key="true" column="STREET"/> <field name="f1" unique="true"/> <field name="f2" index="true"/> <field name="f3"/> <index name="MY_INDEX_NAME"> <field name="f1"/> <field name="f2"/> </index> <unique name="MY_UNIQUE_CONTRAINT_NAME"> <field name="f3"/> </unique> </class>
Note: Currently Speedo supports only indexes and unique constraint.
The sequence element identifies a sequence number generator that
can be used for several purposes:
A sequence is defined at the package level. The list of attributes to define your sequence is as follows:
<package ...>
<sequence name="my_seq" strategy="transactional" datastore-sequence="name_of_datastore_seq"/>
...
</package>
You can retrieve the sequences using the getSequence(String name) method on the persistent manager:
Sequence s = pm.getSequence(SEQ_NAME); //SEQ_NAME is the fully qualified name of the sequence : package + seq_nameThe actions you can perform on a sequence are:
PersistenceManager pm = pmf.getPersistenceManager();
//first, load the class
pm.getObjectIdClass(Article.class);
//then get the sequence
Sequence s = pm.getSequence(ARTICLE_SEQ);