This relationship type extends the one-to-many relations by the ability to assign from one tables row to more than one row from the other table and vice versa. These relations are stored in a separate third table, the so-called lookup table. Both source tables do not contain any information that deals with relations.
To continue our example, we have to assign any number of addresses to a person and any number of persons to an address.
The table structure now looks like this:
Database Table TPERSON:
| Field | Usage |
|---|---|
| FKEY | person's primary key |
| FLASTNAME | person's last name |
| FFIRSTNAME | person's first name |
Note that there are no columns dealing with addresses.
Database Table TADDRESS:
| Field | Usage |
|---|---|
| FKEY | addresses' primary key |
| FSTREET | addresses' street name |
| FCITY | addresses' city name |
Note that there are no columns dealing with persons.
Database Table TPERSADDR:
| Field | Usage |
|---|---|
| FPERSKEY | primary key for one person |
| FADDRKEY | primary key for one address |
The relation between the tables TPERSON and TADDRESS is defined using a lookup table TPERSADDR. This table stores only relations between rows of the first two tables. Any number of relations is possible. The primary key of every table, which is part of the relation, is stored here.
Within the class structure itself, the third table is not reflected. The person's connection to an address is still the same - a one-to-many relationship. But an address is no longer connected to a single person. Instead it is connected to any number of persons. This must be considered in the application's logic, but makes no difference to the implementation of the class itself.
The class Person is the same as in the one-to-many implementation. The class Address remains the same too.
As in the one-to-many implementation, the attribute addr must be defined as a Collection in the JDO file. Besides that, the element-type must be declared. The result-type is an implementation of the Collection interface, as in one-to-many relations.
All three tables must now be used in the nested extensions of the mapping-extension of the addr field, because they are all part of the whole relation. But because the relationship is reflected only from the view of the Person class and because no back-reference is set, the change appears only within the definition of class Person .
The relationship between the classes and the tables is defined in the mapping-extension with the value " many-to-many ". Potential nested extensions of this mapping are the same as in the mapping-extension of the one-to-many mapping except the reference -extension. The specification of the source and destination joins can be defined independently. You may use different numbers of primary key fields on both sides. So different joins for the source and destination side of the lookup-table can be specified for more flexibility.
The relations between the tables are described by the following extensions instead of the reference-extension:
| Extension | Description | Default value |
|---|---|---|
| src-table | the name of the table containing the referencing object | the table name defined for the class |
| dst-table | the name of the table containing the referenced object | extracted from field type name using naming rules |
| lookup-table | the name of the lookup table used for this mapping | generated using naming rules |
| src-reference | list of source reference fields | |
| src-field | the name of the field that contains the used reference value | extracted from field name using naming rules |
| sql-type | the SQL type (only necessary if the database is not able to detect the correct type) | automatic detection |
| dst-field | the name of the field where the reference value is stored (lookup table) | automatic detection |
| dst-reference | list of destination reference fields |
The JDO file for the Person:
<class name="Person" ...>
...
<field name="key" ...>...
<field name="lastName"...>....
<field name="firstName"...>....
<field name="addr">
<collection element-type="com.test.Address"/>
<extension vendor-name="ssibo" key="jdbc">
<extension vendor-name="ssibo" key="mapping" value="many-to-many">
<extension vendor-name="ssibo" key="result-type" value="java.util.ArrayList"/>
<extension vendor-name="ssibo" key="cascade-delete" value="true"/>
<extension vendor-name="ssibo" key="src-table" value="TPERSON"/>
<extension vendor-name="ssibo" key="dst-table" value="TADDRESS"/>
<extension vendor-name="ssibo" key="lookup-table" value="TPERSADDR"/>
<extension vendor-name="ssibo" key="src-reference">
<extension vendor-name="ssibo" key="src-field" value="FKEY"/>
<extension vendor-name="ssibo" key="dst-field" value="FPERSKEY"/>
</extension>
<extension vendor-name="ssibo" key="dst-reference">
<extension vendor-name="ssibo" key="src-field" value="FADDRKEY"/>
<extension vendor-name="ssibo" key="dst-field" value="FKEY"/>
</extension>
</extension>
</extension>
</field>
</class>
Sample application:
//Creation of two persons
Person p1 = new Person();Angabe der Rückreferenz
p1.setLastName("Smith");
pm.makePersistent(p1);
Person p2 = new Person();
p2.setLastName("Miller");
pm.makePersistent(p2);
// Creation of two addresses
Address a1 = new Address();
a1.setCity("New York");
Address a2 = new Address();
a2.setCity("Boston");
// Assigning the addresses to the persons
p1.getAddresses().add(a1);
p1.getAddresses().add(a2);
p2.getAddresses().add(a2);
...
// Reading the addresses of the persons
List list = p1.getAddresses();
for (int i = 0; i < list.size(); i++)
{
Address a = (Address) list.get(i);
System.out.println(a.getCity());
}
...
A many-to-many relation is already bidirectional, so no additional settings are needed. The elements inverse and reverse-relation are not valid for many-to-many mappings.