JDO : 1-N Relationships with Sets

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 Set of objects of
another class. Please note that Sets do not 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 e 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 Set.

This page is aimed at Set fields and so applies to fields of Java type
java.util.HashSet, java.util.LinkedHashSet, java.util.Set, java.util.SortedSet, java.util.TreeSet

Please note that RDBMS supports the full range of options on this page, whereas other datastores
(ODF, Excel, HBase, MongoDB, etc) persist the Set in a column in the owner object (as well as a column
in the non-owner object when bidirectional) rather than using join-tables or foreign-keys since
those concepts are RDBMS-only.

equals() and hashCode()

Important : The element of a Collection ought to define the methods equals and
hashCode so that updates are detected correctly. This is because any Java Collection will
use these to determine equality and whether an element is contained in the Collection.
Note also that the hashCode() should be consistent throughout the lifetime of a persistable
object. By that we mean that it should not use some basis before persistence and then use
some other basis (such as the object identity) after persistence, for this reason we do not
recommend usage of JDOHelper.getObjectId(obj) in the equals/hashCode methods.

1-N Set Unidirectional

We have 2 sample classes Account and Address. These are related in such a way as Account
contains a Set 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

The join table will, by default, be given a primary key. If you want to omit this then you can turn it off
using the DataNucleus metadata extension "primary-key" (within <join>) set to false.

If you want the set to include nulls, you can turn on this behaviour by adding the extension
metadata "allow-nulls" to the <field> set to true

Using Foreign-Key

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 DataNucleus has to use columns in the datastore representation of the Address class. So we define
the XML metadata like this

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. Also be aware that each Address object can have only one owner,
since it has a single foreign key to the Account. If you wish to have an Address assigned to multiple
Accounts then you should use the "Join Table" relationship above.

If you wish to fully define the schema table and column names etc, follow these tips

To specify the name of the table where a class is stored, specify the table attribute on the class
element

To specify the names of the columns where the fields of a class are stored, specify the column attribute
on the field element.

To specify the foreign-key between container table and element table, specify <foreign-key> below
either the <field> element or the <element> element.

1-N Set Bidirectional

We have 2 sample classes Account and Address. These are related in such a way as Account
contains a Set 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

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

To specify the name of the table where a class is stored, specify the table attribute on the class
element

To specify the names of the columns where the fields of a class are stored, specify the column attribute
on the field element.

To specify the foreign-key between container table and element table, specify <foreign-key> below
either the <field> element or the <element> element.

When forming the relation please make sure that you set the relation at BOTH sides since DataNucleus
would have no way of knowing which end is correct if you only set one end.

1-N Set of non-persistable objects

All of the examples above show a 1-N relationship between 2 persistable classes. DataNucleus 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

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 DataNucleus when the column type of the element is not valid to be
part of a primary key (with the RDBMS being used). If the column type of your element is acceptable for use as part
of a primary key then you will not have this "ADPT_PK_IDX" column.
You can control the name of this column by adding an <order> element and specifying the column name for the order column (within <field>).

Embedded into a Join Table

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.

Serialised into a Join Table

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