[[one-to-m-bidirectional-relationships]]
= 1-m Bidirectional relationships

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.



Consider a bidirectional one-to-many association between two entities; a collection member in the "parent" and a property member on the "child".

We can tell DataNucleus about the bidirectionality using `@Persistent(mappedBy=...)`, or we can take responsibility for
this aspect ourselves.

In addition, the two entities can be associated either without or with a join table (indicated by the `@Join` annotation):

* without a join table is more common; a regular foreign key in the child table for `FermentationVessel` points back up to the associated parent `Batch`
* with a join table; a link table holds the tuple representing the linkage.

Testing (against `dn-core 4.1.7`/`dn-rdbms 4.1.9`) has determined there are two main rules:

* If not using `@Join`, then the association must be maintained by setting the child association on the parent. +
+
It is not sufficient to simply add the child object to the parent's collection.

* `@Persistent(mappedBy=...)` and `@Join` cannot be used together. +
+
Put another way, if using `@Join` then you must maintain both sides of the relationship in the application code.


In the examples that follow, we use two entities, `Batch` and `FermentationVessel` (from a brewery domain).  In the
original example domain the relationship between these two entities was optional (a `FermentationVessel` may
have either none or one `Batch` associated with it); for the purpose of this article we'll explore both mandatory and
optional associations.

== Mandatory, no `@Join`

In the first scenario we have use `@Persistent(mappedBy=...)` to indicate a bidirectional association, without any `@Join`:

[source,java]
----
public class Batch {

    // getters and setters omitted

    @Persistent(mappedBy = "batch", dependentElement = "false")     // <1>
    private SortedSet<FermentationVessel> vessels = new TreeSet<FermentationVessel>();
}
----
<1> "mappedBy" means this is bidirectional

and

[source,java]
----
public class FermentationVessel implements Comparable<FermentationVessel> {

    // getters and setters omitted

    @Column(allowsNull = "false")       // <1>
    private Batch batch;

    @Column(allowsNull = "false")
    private State state;                // <2>
}
----
<1> mandatory association up to parent
<2> State is an enum (omitted)


Which creates this schema:

[source,sql]
----
CREATE TABLE "batch"."Batch"
(
    "id" BIGINT GENERATED BY DEFAULT AS IDENTITY,
    ...
    "version" BIGINT NOT NULL,
    CONSTRAINT "Batch_PK" PRIMARY KEY ("id")
)
CREATE TABLE "fvessel"."FermentationVessel"
(
    "id" BIGINT GENERATED BY DEFAULT AS IDENTITY,
    "batch_id_OID" BIGINT NOT NULL,
    "state" NVARCHAR(255) NOT NULL,
    ...
    "version" TIMESTAMP NOT NULL,
    CONSTRAINT "FermentationVessel_PK" PRIMARY KEY ("id")
)
----

That is, there is an mandatory foreign key from `FermentationVessel` to `Batch`.


In this case we can use this code:

[source,java]
----
public Batch transfer(final FermentationVessel vessel) {
    vessel.setBatch(this);                                  // <1>
    vessel.setState(FermentationVessel.State.FERMENTING);
    return this;
}
----
<1> set the parent on the child

This sets up the association correctly, using this SQL:

[source,sql]
----
UPDATE "fvessel"."FermentationVessel"
   SET "batch_id_OID"=\<0>
       ,"state"=<'FERMENTING'>
       ,"version"=<2016-07-07 12:37:14.968>
 WHERE "id"=\<0>
----


The following code will also work:

[source,java]
----
public Batch transfer(final FermentationVessel vessel) {
    vessel.setBatch(this);                                  // <1>
    getVessels().add(vessel);                               // <2>
    vessel.setState(FermentationVessel.State.FERMENTING);
    return this;
}
----
<1> set the parent on the child
<2> add the child to the parent's collection.

However, obviously the second statement is redundant.


== Optional, no `@Join`

If the association to the parent is made optional:

[source,java]
----
public class FermentationVessel implements Comparable<FermentationVessel> {

    // getters and setters omitted

    @Column(allowsNull = "true")       // <1>
    private Batch batch;

    @Column(allowsNull = "false")
    private State state;
}
----
<1> optional association up to parent


Which creates this schema:

[source,sql]
----
CREATE TABLE "batch"."Batch"
(
    "id" BIGINT GENERATED BY DEFAULT AS IDENTITY,
    ...
    "version" BIGINT NOT NULL,
    CONSTRAINT "Batch_PK" PRIMARY KEY ("id")
)
CREATE TABLE "fvessel"."FermentationVessel"
(
    "id" BIGINT GENERATED BY DEFAULT AS IDENTITY,
    "batch_id_OID" BIGINT NULL,
    "state" NVARCHAR(255) NOT NULL,
    ...
    "version" TIMESTAMP NOT NULL,
    CONSTRAINT "FermentationVessel_PK" PRIMARY KEY ("id")
)
----

This is almost exactly the same, except the foreign key from `FermentationVessel` to `Batch` is now nullable.


In this case then setting the parent on the child still works:

[source,java]
----
public Batch transfer(final FermentationVessel vessel) {
    vessel.setBatch(this);                                  // <1>
    vessel.setState(FermentationVessel.State.FERMENTING);
    return this;
}
----
<1> set the parent on the child

*HOWEVER*, if we (redundantly) update both sides, then - paradoxically - the association is NOT set up

[source,java]
----
public Batch transfer(final FermentationVessel vessel) {
    vessel.setBatch(this);                                  // <1>
    getVessels().add(vessel);                               // <2>
    vessel.setState(FermentationVessel.State.FERMENTING);
    return this;
}
----
<1> set the parent on the child
<2> add the child to the parent's collection.

[NOTE]
====
It's not clear if this is a bug in `dn-core 4.1.7`/`dn-rdbms 4.19`; an earlier thread on the mailing list from 2014 actually gave
the opposite advice, see http://isis.markmail.org/thread/ipu2lzqqikqdglox[this thread] and in particular this http://markmail.org/message/hblptpw675mlw723[message].

In fact we also have had a different case raised (url lost) which argues that the parent should only be set on the child, and the child _not_ added to the parent's collection.
This concurs with the most recent testing.
====

Therefore, the simple advice is that, for bidirectional associations, simply set the parent on the child, and this will work
reliably irrespective of whether the association is mandatory or optional.


== With `@Join`

Although DataNucleus does not complain if `@Persistence(mappedBy=...)` and `@Join` are combined, testing (against `dn-core 4.1.7`/`dn-rdbms 4.19`) has shown that the bidirectional association is not properly maintained.

Therefore, we recommend that if `@Join` is used, then manually maintain both sides of the relationship and do not indicate
that the association is bidirectional.

For example:

[source,java]
----
public class Batch {

    // getters and setters omitted

    @Join(table = "Batch_vessels")
    @Persistent(dependentElement = "false")
    private SortedSet<FermentationVessel> vessels = new TreeSet<FermentationVessel>();
}
----

and

[source,java]
----
public class FermentationVessel implements Comparable<FermentationVessel> {

    // getters and setters omitted

    @Column(allowsNull = "true")       // <1>
    private Batch batch;

    @Column(allowsNull = "false")
    private State state;
}
----
<1> optional association up to parent


creates this schema:

[source,sql]
----
CREATE TABLE "batch"."Batch"
(
    "id" BIGINT GENERATED BY DEFAULT AS IDENTITY,
    ...
    "version" BIGINT NOT NULL,
    CONSTRAINT "Batch_PK" PRIMARY KEY ("id")
)
CREATE TABLE "fvessel"."FermentationVessel"
(
    "id" BIGINT GENERATED BY DEFAULT AS IDENTITY,
    "state" NVARCHAR(255) NOT NULL,
    ...
    "version" TIMESTAMP NOT NULL,
    CONSTRAINT "FermentationVessel_PK" PRIMARY KEY ("id")
)
CREATE TABLE "batch"."Batch_vessels"
(
    "id_OID" BIGINT NOT NULL,
    "id_EID" BIGINT NOT NULL,
    CONSTRAINT "Batch_vessels_PK" PRIMARY KEY ("id_OID","id_EID")
)
----

That is, there is NO foreign key from `FermentationVessel` to `Batch`, instead the `Batch_vessels` table links the two together.


These should then be maintained using:

[source,java]
----
public Batch transfer(final FermentationVessel vessel) {
    vessel.setBatch(this);                                  // <1>
    getVessels().add(vessel);                               // <2>
    vessel.setState(FermentationVessel.State.FERMENTING);
    return this;
}
----
<1> set the parent on the child
<2> add the child to the parent's collection.


that is, explicitly update both sides of the relationship.

This generates this SQL:

[source,sql]
----
INSERT INTO "batch"."Batch_vessels" ("id_OID","id_EID") VALUES (<0>,<0>)
UPDATE "batch"."Batch"
   SET "version"=\<3>
 WHERE "id"=\<0>
UPDATE "fvessel"."FermentationVessel"
   SET "state"=<'FERMENTING'>
      ,"version"=<2016-07-07 12:49:21.49>
 WHERE "id"=\<0>
----


It doesn't matter in these cases whether the association is mandatory or optional; it will be the same SQL generated.
