Skip to content

Commit d220494

Browse files
committed
Improve documentation on exact behaviour of many-to-many deletion operations
1 parent c235901 commit d220494

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

docs/en/reference/association-mapping.rst

+18
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,15 @@ Generated MySQL Schema:
881881
replaced by one-to-many/many-to-one associations between the 3
882882
participating classes.
883883

884+
.. note::
885+
886+
For many-to-many associations, the ORM takes care of managing rows
887+
in the join table connecting both sides. Due to the way it deals
888+
with entity removals, database-level constraints may not work the
889+
way one might intuitively assume. Thus, be sure not to miss the section
890+
on :ref:`join table management <remove_object_many_to_many_join_tables>`.
891+
892+
884893
Many-To-Many, Bidirectional
885894
---------------------------
886895

@@ -1019,6 +1028,15 @@ one is bidirectional.
10191028
The MySQL schema is exactly the same as for the Many-To-Many
10201029
uni-directional case above.
10211030

1031+
.. note::
1032+
1033+
For many-to-many associations, the ORM takes care of managing rows
1034+
in the join table connecting both sides. Due to the way it deals
1035+
with entity removals, database-level constraints may not work the
1036+
way one might intuitively assume. Thus, be sure not to miss the section
1037+
on :ref:`join table management <remove_object_many_to_many_join_tables>`.
1038+
1039+
10221040
Owning and Inverse Side on a ManyToMany Association
10231041
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10241042

docs/en/reference/working-with-objects.rst

+43-7
Original file line numberDiff line numberDiff line change
@@ -286,17 +286,53 @@ as follows:
286286
After an entity has been removed, its in-memory state is the same as
287287
before the removal, except for generated identifiers.
288288

289-
Removing an entity will also automatically delete any existing
290-
records in many-to-many join tables that link this entity. The
291-
action taken depends on the value of the ``@joinColumn`` mapping
292-
attribute "onDelete". Either Doctrine issues a dedicated ``DELETE``
293-
statement for records of each join table or it depends on the
294-
foreign key semantics of onDelete="CASCADE".
289+
During the ``EntityManager#flush()`` operation, the removed entity
290+
will also be removed from all collections in entities currently
291+
loaded into memory.
292+
293+
.. _remove_object_many_to_many_join_tables:
294+
295+
Join-table management when removing from many-to-many collections
296+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
297+
298+
Regarding existing rows in many-to-many join tables that refer to
299+
an entity being removed, the following applies.
300+
301+
When the entity being removed does not declare the many-to-many association
302+
itself (that is, the many-to-many association is unidirectional and
303+
the entity is on the inverse side), the ORM has no reasonable way to
304+
detect associations targeting the entity's class. Thus, no ORM-level handling
305+
of join-table rows is attempted and database-level constraints apply.
306+
In case of database-level ``ON DELETE RESTRICT`` constraints, the
307+
``EntityManager#flush()`` operation may abort and a ``ConstraintViolationException``
308+
may be thrown. No in-memory collections will be modified in this case.
309+
With ``ON DELETE CASCADE``, the RDBMS will take care of removing rows
310+
from join tables.
311+
312+
When the entity being removed is part of bi-directional many-to-many
313+
association, either as the owning or inverse side, the ORM will
314+
delete rows from join tables before removing the entity itself. That means
315+
database-level ``ON DELETE RESTRICT`` constraints on join tables are not
316+
effective, since the join table rows are removed first. Removal of join table
317+
rows happens through specialized methods in entity and collection persister
318+
classes and take one query per entity and join table. In case the association
319+
uses a ``@JoinColumn`` configuration with ``onDelete="CASCADE"``, instead
320+
of using a dedicated ``DELETE`` query the database-level operation will be
321+
relied upon.
322+
323+
.. note::
324+
325+
In case you rely on database-level ``ON DELETE RESTRICT`` constraints,
326+
be aware that by making many-to-many associations bidirectional the
327+
assumed protection may be lost.
328+
329+
330+
Performance of different deletion strategies
331+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
295332

296333
Deleting an object with all its associated objects can be achieved
297334
in multiple ways with very different performance impacts.
298335

299-
300336
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
301337
will fetch this association. If its a Single association it will
302338
pass this entity to

0 commit comments

Comments
 (0)