![]() | ![]() |
![]() |
| Project | Ver 1.1 | Ver 1.2 | JDO | JPA | Guides | Tools |
| 1.1 | Preparation | O/R Mapping | Runtime | Extensions | Developer |
You have a 1-N (one to many) or N-1 (many to one) when you have one object of a class that has a Collection of objects of another class. Please note that Collections allow duplicates, and so the persistence process reflects this with the choice of primary keys. There are two ways in which you can represent this in a datastore : Join Table (where are join table is used to provide the relationship mapping between the objects), and Foreign-Key (where a foreign key is placed in the table of the object contained in the Collection. The various possible relationships are described below.
Important : If you declare a field as a Collection, you can instantiate it as either Set-based or as List-based. With a List an "ordering" column is required, whereas with a Set it isn't. Consequently JPOX needs to know if you plan on using it as Set-based or List-based. You do this by adding an "order" element to the field if it is to be instantiated as a List-based collection. If there is no "order" element, then it will be assumed to be Set-based.
We have 2 sample classes Account and Address. These are related in such a way as Account contains a Collection of objects of type Address, yet each Address knows nothing about the Account objects that it relates to. Like this ![]() There are 2 ways that we can persist this relationship. These are shown below
![]() If you define the Meta-Data for these classes as follows
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent">
<collection element-type="org.jpox.Address"/>
<join/>
</field>
</class>
<class name="Address" identity-type="datastore">
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>The crucial part is the join element on the field element - this signals to JDO to use a join table. This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown below. ![]() The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where you want to retain the independence of one class from the other class. If you wish to fully define the schema table and column names etc, follow these tips
![]() In this relationship, the Account class has a List of Address objects, yet the Address knows nothing about the Account. In this case we don't have a field in the Address to link back to the Account and so JPOX has to use columns in the datastore representation of the Address class. So we define the MetaData like this
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent">
<collection element-type="org.jpox.Address"/>
</field>
</class>
<class name="Address" identity-type="datastore">
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>Again there will be 2 tables, one for Address, and one for Account. Note that we have no "mapped-by" attribute specified, and also no "join" element. If you wish to specify the names of the columns used in the schema for the foreign key in the Address table you should use the element element within the field of the collection. ![]() In terms of operation within your classes of assigning the objects in the relationship. You have to take your Account object and add the Address to the Account collection field since the Address knows nothing about the Account. If you wish to fully define the schema table and column names etc, follow these tips
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate Collection entries, then use the "Join Table" variant above.
We have 2 sample classes Account and Address. These are related in such a way as Account contains a Collection of objects of type Address, and each Address has a reference to the Account object that it relates to. Like this ![]() There are 2 ways that we can persist this relationship. These are shown below
![]() If you define the Meta-Data for these classes as follows
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<collection element-type="org.jpox.Address"/>
<join/>
</field>
</class>
<class name="Address" identity-type="datastore">
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="account" persistence-modifier="persistent">
</field>
</class>
</package>The crucial part is the join element on the field element - this signals to JDO to use a join table. This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown below. ![]() The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where you want to retain the independence of one class from the other class. If you wish to fully define the schema table and column names etc, follow these tips
![]() Here we have the 2 classes with both knowing about the relationship with the other. If you define the Meta-Data for these classes as follows
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<collection element-type="org.jpox.Address"/>
</field>
</class>
<class name="Address" identity-type="datastore">
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="account" persistence-modifier="persistent">
</field>
</class>
</package>The crucial part is the mapped-by attribute of the field on the "1" side of the relationship. This tells the JDO implementation to look for a field called account on the Address class. This will create 2 tables in the database, one for Address (including an ACCOUNT_ID to link to the ACCOUNT table), and one for Account. Notice the subtle difference to this set-up to that of the Join Table relationship earlier. ![]() If you wish to fully define the schema table and column names etc, follow these tips
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate Collection entries, then use the "Join Table" variant above.
![]() All of the examples above show a 1-N relationship between 2 PersistenceCapable classes. JPOX can also cater for a Collection of primitive or Object types. For example, when you have a Collection of Strings. This will be persisted in the same way as the "Join Table" examples above. A join table is created to hold the collection elements. Let's take our example. We have an Account that stores a Collection of addresses. These addresses are simply Strings. We define the Meta-Data like this
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent">
<collection element-type="java.lang.String"/>
<join/>
<element column="ADDRESS"/>
</field>
</class>In the datastore the following is created ![]() The ACCOUNT table is as before, but this time we only have the "join table". In our MetaData we used the <element> tag to specify the column name to use for the actual address String. Please note that the column ADPT_PK_IDX is added by JPOX so that duplicates can be stored. You can override the default naming of this column by specifying the JPOX extension "adapter-column-name" within the <field> for the Collection.
![]() The above relationship types assume that both classes in the 1-N relation will have their own table. A variation on this is where you have a join table but you embed the elements of the collection into this join table. To do this you use the embedded-element attribute on the collection MetaData element. This is described in Embedded Collection Elements.
![]() The above relationship types assume that both classes in the 1-N relation will have their own table. A variation on this is where you have a join table but you serialise the elements of the collection into this join table in a single column. To do this you use the serialised-element attribute on the collection MetaData element. This is described in Serialised Collection Elements
![]() The relationships using join tables shown above rely on the join table relating to the relation in question. JPOX does (from version 1.1.1) allow the possibility of sharing a join table between relations. The example below demonstrates this. We take the example as show above (1-N Unidirectional Join table relation), and extend Account to have 2 collections of Address records. One for home addresses and one for work addresses, like this ![]() We now change the metadata we had earlier to allow for 2 collections, but sharing the join table
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="workAddresses" persistence-modifier="persistent" table="ACCOUNT_ADDRESSES">
<collection element-type="org.jpox.Address"/>
<join column="ACCOUNT_ID_OID"/>
<element column="ADDRESS_ID_EID"/>
<extension vendor-name="jpox" key="relation-discriminator-column" value="ADDRESS_TYPE"/>
<extension vendor-name="jpox" key="relation-discriminator-pk" value="true"/>
<extension vendor-name="jpox" key="relation-discriminator-value" value="work"/>
</field>
<field name="homeAddresses" persistence-modifier="persistent" table="ACCOUNT_ADDRESSES">
<collection element-type="org.jpox.Address"/>
<join column="ACCOUNT_ID_OID"/>
<element column="ADDRESS_ID_EID"/>
<extension vendor-name="jpox" key="relation-discriminator-column" value="ADDRESS_TYPE"/>
<extension vendor-name="jpox" key="relation-discriminator-pk" value="true"/>
<extension vendor-name="jpox" key="relation-discriminator-value" value="home"/>
</field>
</class>
<class name="Address" identity-type="datastore">
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>So we have defined the same join table for the 2 collections "ACCOUNT_ADDRESSES", and the same columns in the join table, meaning that we will be sharing the same join table to represent both relations. The important step is then to define the 3 JPOX extension tags. These define a column in the join table (the same for both relations), and the value that will be populated when a row of that collection is inserted into the join table. In our case, all "home" addresses will have a value of "home" inserted into this column, and all "work" addresses will have "work" inserted. This means we can now identify easily which join table entry represents which relation field. This results in the following database schema ![]()
![]() The relationships using foreign keys shown above rely on the foreign key relating to the relation in question. JPOX does (from version 1.1.1) allow the possibility of sharing a foreign key between relations between the same classes. The example below demonstrates this. We take the example as show above (1-N Unidirectional Foreign Key relation), and extend Account to have 2 collections of Address records. One for home addresses and one for work addresses, like this ![]() We now change the metadata we had earlier to allow for 2 collections, but sharing the join table
<package name="org.jpox">
<class name="Account" identity-type="datastore">
<field name="firstName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="workAddresses" persistence-modifier="persistent">
<collection element-type="org.jpox.Address"/>
<element column="ACCOUNT_ID_OID"/>
<extension vendor-name="jpox" key="relation-discriminator-column" value="ADDRESS_TYPE"/>
<extension vendor-name="jpox" key="relation-discriminator-value" value="work"/>
</field>
<field name="homeAddresses" persistence-modifier="persistent">
<collection element-type="org.jpox.Address"/>
<element column="ACCOUNT_ID_OID"/>
<extension vendor-name="jpox" key="relation-discriminator-column" value="ADDRESS_TYPE"/>
<extension vendor-name="jpox" key="relation-discriminator-value" value="home"/>
</field>
</class>
<class name="Address" identity-type="datastore">
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>So we have defined the same foreign key for the 2 collections "ACCOUNT_ID_OID", The important step is then to define the 2 JPOX extension tags. These define a column in the element table (the same for both relations), and the value that will be populated when a row of that collection is inserted into the element table. In our case, all "home" addresses will have a value of "home" inserted into this column, and all "work" addresses will have "work" inserted. This means we can now identify easily which element table entry represents which relation field. This results in the following database schema ![]() |