You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2016/07/07 16:21:28 UTC

isis git commit: ISIS-1335: updating docs on bidirectional associations

Repository: isis
Updated Branches:
  refs/heads/master 497e09379 -> cf7b23d20


ISIS-1335: updating docs on bidirectional associations


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/cf7b23d2
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/cf7b23d2
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/cf7b23d2

Branch: refs/heads/master
Commit: cf7b23d2048e9257c7ccfa899660d79f5b0c3047
Parents: 497e093
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Thu Jul 7 15:48:54 2016 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Thu Jul 7 15:56:23 2016 +0100

----------------------------------------------------------------------
 .../_ugbtb_decoupling_contributed-members.adoc  |   9 +
 .../guides/_ugbtb_decoupling_contributions.adoc |  70 +++-
 .../main/asciidoc/guides/_ugfun_how-tos.adoc    |   2 -
 .../_ugfun_how-tos_contributed-members.adoc     |  69 ----
 .../_ugfun_how-tos_entity-relationships.adoc    |  15 -
 ...aged-1-to-m-bidirectional-relationships.adoc |  59 ----
 .../asciidoc/guides/_ugfun_jdo-mappings.adoc    |  10 +
 ...ings_1-to-m-bidirectional-relationships.adoc | 321 +++++++++++++++++++
 .../src/main/asciidoc/guides/ugfun.adoc         |   1 +
 9 files changed, 408 insertions(+), 148 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributed-members.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributed-members.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributed-members.adoc
new file mode 100644
index 0000000..4752a8c
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributed-members.adoc
@@ -0,0 +1,9 @@
+[[_ugbtb_decoupling_contributed-members]]
+= Contributed Members
+: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.
+:_basedir: ../
+:_imagesdir: images/
+
+
+
+

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributions.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributions.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributions.adoc
index dbb3526..79adfeb 100644
--- a/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributions.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/_ugbtb_decoupling_contributions.adoc
@@ -8,16 +8,80 @@
 Contributed services provide many of the same benefits as xref:ugbtb.adoc#_ugbtb_decoupling_mixins[mixins];
 indeed mixins are an evolution and refinement of the contributions concept.
 
+[WARNING]
+====
+It's possible that contributions may be deprecated and eventually removed in a future version of the framework, to be replaced entirely by mixins.
+====
+
 The main difference between contributed services and mixins is that the actions of a contributed service will
 contribute to _all_ the parameters of its actions, whereas a mixin only contributes to the type accepted in its
 constructor.  Also, contributed services are long-lived
 singletons, whereas mixins are instantiated as required (by the framework) and then discarded almost immediately.
 
-For more on contributed services:
+[NOTE]
+====
+There's further useful information on contributed services in the reference guide, discussing the xref:rgant.adoc#_rgant-DomainService_nature[@DomainService#nature()] attribute, for the `NatureOfService.VIEW_CONTRIBUTIONS_ONLY` nature.
+====
+
+
+== Syntax
+
+Any n-parameter action provided by a service will automatically be contributed to the list of actions for each of its (entity) parameters. From the viewpoint of the entity the action is called a contributed action.
+
+For example, given a service:
+
+[source,java]
+----
+public interface Library {
+    public Loan borrow(Loanable l, Borrower b);
+}
+----
+
+and the entities:
+
+[source,java]
+----
+public class Book implements Loanable { ... }
+----
+
+and
+
+[source,java]
+----
+public class LibraryMember implements Borrower { ... }
+----
+
+then the `borrow(...)` action will be contributed to both `Book` and to `LibraryMember`.
+
+This is an important capability because it helps to decouple the concrete classes from the services.
+
+If necessary, though, this behaviour can be suppressed by annotating the service action with `@org.apache.isis.applib.annotations.NotContributed`.
+
+For example:
+
+[source,java]
+----
+public interface Library {
+    @NotContributed
+    public Loan borrow(Loanable l, Borrower b);
+}
+----
+
+If annotated at the interface level, then the annotation will be inherited by every concrete class. Alternatively the annotation can be applied at the implementation class level and only apply to that particular implementation.
+
+Note that an action annotated as being `@NotContributed` will still appear in the service menu for the service. If an action should neither be contributed nor appear in service menu items, then simply annotate it as `@Hidden`.
+
+
+## Contributed Action
+
+NOTE: TODO
+
+## Contributed Property
 
-* the syntax of writing contributed actions/properties/collections is described in this xref:ugfun.adoc#_ugfun_how-tos_contributed-members[how-to]
+NOTE: TODO
 
-* there's also useful information in the reference guide, discussing the xref:rgant.adoc#_rgant-DomainService_nature[@DomainService#nature()] attribute, for the `NatureOfService.VIEW_CONTRIBUTIONS_ONLY` nature.
+## Contributed Collection
 
+NOTE: TODO
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos.adoc
index 4c05fe6..dfcb5ce 100644
--- a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos.adoc
@@ -11,8 +11,6 @@ include::_ugfun_how-tos_class-structure.adoc[leveloffset=+1]
 include::_ugfun_how-tos_ui-hints.adoc[leveloffset=+1]
 include::_ugfun_how-tos_domain-services.adoc[leveloffset=+1]
 include::_ugfun_how-tos_crud.adoc[leveloffset=+1]
-include::_ugfun_how-tos_entity-relationships.adoc[leveloffset=+1]
-include::_ugfun_how-tos_contributed-members.adoc[leveloffset=+1]
 include::_ugfun_how-tos_business-rules.adoc[leveloffset=+1]
 include::_ugfun_how-tos_derived-members.adoc[leveloffset=+1]
 include::_ugfun_how-tos_drop-downs-and-defaults.adoc[leveloffset=+1]

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_contributed-members.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_contributed-members.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_contributed-members.adoc
deleted file mode 100644
index 3b48ccf..0000000
--- a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_contributed-members.adoc
+++ /dev/null
@@ -1,69 +0,0 @@
-[[_ugfun_how-tos_contributed-members]]
-= Contributed Members
-: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.
-:_basedir: ../
-:_imagesdir: images/
-
-
-
-NOTE: TODO - generalize to discussion on contributed collections/properties; update to new annotations
-
-Any n-parameter action provided by a service will automatically be contributed to the list of actions for each of its (entity) parameters. From the viewpoint of the entity the action is called a contributed action.
-
-For example, given a service:
-
-[source,java]
-----
-public interface Library {
-    public Loan borrow(Loanable l, Borrower b);
-}
-----
-
-and the entities:
-
-[source,java]
-----
-public class Book implements Loanable { ... }
-----
-
-and
-
-[source,java]
-----
-public class LibraryMember implements Borrower { ... }
-----
-
-then the `borrow(...)` action will be contributed to both `Book` and to `LibraryMember`.
-
-This is an important capability because it helps to decouple the concrete classes from the services.
-
-If necessary, though, this behaviour can be suppressed by annotating the service action with `@org.apache.isis.applib.annotations.NotContributed`.
-
-For example:
-
-[source,java]
-----
-public interface Library {
-    @NotContributed
-    public Loan borrow(Loanable l, Borrower b);
-}
-----
-
-If annotated at the interface level, then the annotation will be inherited by every concrete class. Alternatively the annotation can be applied at the implementation class level and only apply to that particular implementation.
-
-Note that an action annotated as being `@NotContributed` will still appear in the service menu for the service. If an action should neither be contributed nor appear in service menu items, then simply annotate it as `@Hidden`.
-
-
-## Contributed Action
-
-NOTE: TODO
-
-## Contributed Property
-
-NOTE: TODO
-
-## Contributed Collection
-
-NOTE: TODO
-
-

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships.adoc
deleted file mode 100644
index b03cd80..0000000
--- a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships.adoc
+++ /dev/null
@@ -1,15 +0,0 @@
-[[_ugfun_how-tos_entity-relationships]]
-= Entity 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.
-:_basedir: ../
-:_imagesdir: images/
-
-
-NOTE: TODO
-
-== Mandatory and Optional
-
-NOTE: TODO
-
-
-include::_ugfun_how-tos_entity-relationships_managed-1-to-m-bidirectional-relationships.adoc[leveloffset=+1]

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships_managed-1-to-m-bidirectional-relationships.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships_managed-1-to-m-bidirectional-relationships.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships_managed-1-to-m-bidirectional-relationships.adoc
deleted file mode 100644
index 97e2160..0000000
--- a/adocs/documentation/src/main/asciidoc/guides/_ugfun_how-tos_entity-relationships_managed-1-to-m-bidirectional-relationships.adoc
+++ /dev/null
@@ -1,59 +0,0 @@
-[[_ugfun_how-tos_entity-relationships_managed-1-to-m-bidirectional-relationships]]
-= 1-m bidir 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.
-:_basedir: ../
-:_imagesdir: images/
-
-
-
-When an object is added to a 1:m bidirectional relationship, the child object must refer to the parent and the child must be added to the parent's children collection.
-
-If there were no database involved then we would have recommended that you use the link:http://www.two-sdg.demon.co.uk/curbralan/papers/MutualRegistration.pdf[mutual registration] pattern to ensure that both the parent and child are updated correctly.  (The  xref:rgcms.adoc#_rgcms_methods_prefixes_modify[`modify...()`] and xref:rgcms.adoc#_rgcms_methods_prefixes_clear[`clear...()`] supporting methods were introduced in the framework primarily to help support implement the mutual registration pattern.
-
-However, in a relational database, these two operations in the domain object model correspond simply to updating the foreign key of the child table to reference the parent's primary key.
-
-So long as the parent's children collection is a `java.util.Set` (rather than a `Collection` or a `List`), the JDO Objectstore will automatically maintain both sides of the relationship. All that is necessary is to set the child to refer to the parent.
-
-For example, all you need write is:
-
-[source,java]
-----
-public class Department {
-    @javax.jdo.annotations.Persistent(mappedBy="department") // <1>
-    private SortedSet<Employee> employees = new TreeSet<Employee>();
-
-    public SortedSet<Employee> getEmployees() { ... }
-    public void setEmployees(SortedSet<Employee> employees) { ... }
-    ...
-}
-public class Employee {
-    private Department department;
-    public Department getDepartment() { ... }
-    public void setDepartment(Department department) { ... }
-    ...
-}
-----
-<1> it's the `mappedBy` attribute that tells DataNucleus this is a bidirectional relationship.  The value "department" refers to the Employee#department property.
-
-Moreover, when maintaining a bidirectional 1-n relationship that is automatically managed by DataNucleus, it's preferred to "add" to the parent's child collection, don't set the parent on the child.
-
-
-[WARNING]
-====
-If you don't do this then you may hit a `NullPointerException`. The above idiom fixes the issue.
-
-For more information, see http://isis.markmail.org/thread/ipu2lzqqikqdglox[this thread] on the Apache Isis users
-mailing list, including this http://markmail.org/message/hblptpw675mlw723[message] with the above recommendation.
-====
-
-
-[WARNING]
-====
-In fact, not only do you not need to manually maintain the relationship, we have noted on at least http://markmail.org/message/agnwmzocvdfht32f[one occasion] a subtle error if the code is programmatically added.
-
-The error in that case was that the same object was contained in the parents collection. This of course should not happen for a `TreeSet`. However, JDO/DataNucleus replaces the `TreeSet` with its own implementation, and (either by design or otherwise) this does not enforce `Set` semantics.
-
-The upshot is that you should NEVER programmatically add the child object to the parent's collection if using JDO Objectstore.
-====
-
-

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings.adoc
new file mode 100644
index 0000000..29eda2f
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings.adoc
@@ -0,0 +1,10 @@
+[[_ugfun_jdo-mappings]]
+= JDO Mappings
+: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.
+:_basedir: ../
+:_imagesdir: images/
+
+
+
+
+include::_ugfun_jdo-mappings_1-to-m-bidirectional-relationships.adoc[leveloffset=+1]

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings_1-to-m-bidirectional-relationships.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings_1-to-m-bidirectional-relationships.adoc b/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings_1-to-m-bidirectional-relationships.adoc
new file mode 100644
index 0000000..a28e893
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/_ugfun_jdo-mappings_1-to-m-bidirectional-relationships.adoc
@@ -0,0 +1,321 @@
+[[_ugfun_jdo-mappings_1-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.
+:_basedir: ../
+:_imagesdir: images/
+
+
+
+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 in `1.13.0-SNAPSHOT` (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 http://markmail.org/message/agnwmzocvdfht32f[a different case] 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.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/cf7b23d2/adocs/documentation/src/main/asciidoc/guides/ugfun.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun.adoc
index 70f9423..af1a7d8 100644
--- a/adocs/documentation/src/main/asciidoc/guides/ugfun.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/ugfun.adoc
@@ -50,6 +50,7 @@ include::_ugfun_core-concepts.adoc[leveloffset=+1]
 include::_ugfun_getting-started.adoc[leveloffset=+1]
 
 include::_ugfun_how-tos.adoc[leveloffset=+1]
+include::_ugfun_jdo-mappings.adoc[leveloffset=+1]
 include::_ugfun_object-layout.adoc[leveloffset=+1]
 
 include::_ugfun_faqs.adoc[leveloffset=+1]