You have a M-to-N (or Many-to-Many) relationship if an object of a class A has associated objects of class B, and class B has associated objects of class A. This relationship may be achieved through Java Collection, Set, List or subclasses of these, although the only one that supports a true M-N is Set.
With DataNucleus this can be set up as described in this section, using what is called a Join Table relationship. Let's take the following example and describe how to model it with the different types of collection classes. We have 2 classes, Product and Supplier as below.
Here the Product class knows about the Supplier class. In addition the Supplier knows about the Product class, however with these relationships are really independent.
Please note when adding objects to an M-N relation, you MUST add to the owner side as a minimum, and optionally also add to the non-owner side. Just adding to the non-owner side will not add the relation.
The various possible relationships are described below.
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 in the equals/hashCode methods.
If you define the Meta-Data for these classes as follows
<entity-mappings> <entity class="mydomain.Product"> <table name="PRODUCT"/> <attributes> <id name="id"> <column name="PRODUCT_ID"/> </id> ... <many-to-many name="suppliers" mapped-by="products"> <join-table name="PRODUCTS_SUPPLIERS"> <join-column name="PRODUCT_ID"/> <inverse-join-column name="SUPPLIER_ID"/> </join-table> </many-to-many> </attributes> </entity> <entity class="mydomain.Supplier"> <table name="SUPPLIER"/> <attributes> <id name="id"> <column name="SUPPLIER_ID"/> </id> ... <many-to-many name="products"/> </attributes> </entity> </entity-mappings>
or alternatively using annotations
public class Product { ... @ManyToMany(mappedBy="products") @JoinTable(name="PRODUCTS_SUPPLIERS", joinColumns={@JoinColumn(name="PRODUCT_ID")}, inverseJoinColumns={@JoinColumn(name="SUPPLIER_ID")}) Collection<Supplier> suppliers } public class Supplier { ... @ManyToMany Collection<Product> products; ... }
Note how we have specified the information only once regarding join table name, and join column names as well as the <join-table>. This is the JPA standard way of specification, and results in a single join table. The "mapped-by" ties the two fields together.
If you define the Meta-Data for these classes as follows
<entity-mappings> <entity class="mydomain.Product"> <table name="PRODUCT"/> <attributes> <id name="id"> <column name="PRODUCT_ID"/> </id> ... <many-to-many name="suppliers" mapped-by="products"> <order-by>name</order-by> <join-table name="PRODUCTS_SUPPLIERS"> <join-column name="PRODUCT_ID"/> <inverse-join-column name="SUPPLIER_ID"/> </join-table> </many-to-many> </attributes> </entity> <entity class="mydomain.Supplier"> <table name="SUPPLIER"/> <attributes> <id name="id"> <column name="SUPPLIER_ID"/> </id> ... <many-to-many name="products"> <order-by>name</order-by> </many-to-many> </attributes> </entity> </entity-mappings>
or using annotations
public class Product { ... @ManyToMany @JoinTable(name="PRODUCTS_SUPPLIERS", joinColumns={@JoinColumn(name="PRODUCT_ID")}, inverseJoinColumns={@JoinColumn(name="SUPPLIER_ID")}) @OrderBy("id") List<Supplier> suppliers } public class Supplier { ... @ManyToMany @OrderBy("id") List<Product> products }
There will be 3 tables, one for Product, one for Supplier, and the join table. The difference from the Set example is that we now have <order-by> at both sides of the relation. This has no effect in the datastore schema but when the Lists are retrieved they are ordered using the specified order-by.
Note that you cannot have a many-to-many relation using indexed lists since both sides would need its own index.
Please be aware of the following.