From 400be2a3e68b6360ecdba04ec14cfc634bce4571 Mon Sep 17 00:00:00 2001 From: eXtreme Date: Wed, 7 Jul 2010 19:10:48 +0800 Subject: [PATCH 1/3] fixing codeblocks --- manual/en/dql-doctrine-query-language.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manual/en/dql-doctrine-query-language.txt b/manual/en/dql-doctrine-query-language.txt index 3e0a5cab5..e2d9e6cdb 100644 --- a/manual/en/dql-doctrine-query-language.txt +++ b/manual/en/dql-doctrine-query-language.txt @@ -188,11 +188,12 @@ With Nested Conditions in WHERE Clause: With COUNT DISTINCT: [php] - $query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser + $query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser'); $users = $query->getResult(); // array of ForumUser objects With Arithmetic Expression in WHERE clause: + [php] $query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000'); $users = $query->getResult(); // array of ForumUser objects From 0428774ec875e5cb34d5cb88a623ca40db318cf2 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Thu, 8 Jul 2010 00:26:02 +0200 Subject: [PATCH 2/3] Added more information about flushing semantics. --- manual/en/working-with-objects.txt | 159 ++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 51 deletions(-) diff --git a/manual/en/working-with-objects.txt b/manual/en/working-with-objects.txt index e69adacdb..3955abfbb 100644 --- a/manual/en/working-with-objects.txt +++ b/manual/en/working-with-objects.txt @@ -1,4 +1,3 @@ -++ Understanding In this chapter we will help you understand the `EntityManager` and the `UnitOfWork`. A Unit of Work is similar to an object-level transaction. A new Unit of Work is @@ -8,49 +7,7 @@ implicity started when an EntityManager is initially created or after A Unit of Work can be manually closed by calling EntityManager#close(). Any changes to objects within this Unit of Work that have not yet been persisted -are lost. - -+++ The size of a Unit of Work - -The size of a Unit of Work mainly refers to the number of managed entities at -a particular point in time. - -+++ The cost of flush() - -How costly a flush operation is in terms of performance mainly depends on 2 factors: - -* The size of your current Unit of Work -* The configured change tracking policies - -You can get the size of your Unit of Work as follows: - - [php] - $uowSize = $em->getUnitOfWork()->size(); - -The size represents the number of managed entities in the Unit of Work. This -size affects the performance of flush() operations due to change tracking -(see "Change Tracking Policies") and, of course, memory consumption, so you -may want to check it from time to time during development. - -> **CAUTION** -> Do not invoke `flush` after every change to an entity or every single invocation of -> persist/remove/merge/... This is an anti-pattern and unnecessarily reduces the -> performance of your application. Instead, form units of work that operate on your objects -> and call `flush` when you are done. While serving a single HTTP request there should -> be usually no need for invoking `flush` more than 0-2 times. - -+++ Direct access to a Unit of Work - -You can get direct access to the Unit of Work by calling `EntityManager#getUnitOfWork()`. -This will return the UnitOfWork instance the EntityManager is currently using. - - [php] - $uow = $em->getUnitOfWork(); - -> **NOTE** -> Directly manipulating a UnitOfWork is not recommended. When working directly with the -> UnitOfWork API, respect methods marked as INTERNAL by not using them and carefully read -> the API documentation. +are lost. ++ Persisting entities @@ -79,7 +36,7 @@ Example: > **CAUTION** > Generated entity identifiers / primary keys are guaranteed to be available after the > next successful flush operation that involves the entity in question. -> You can not reply on a generated identifier to be available directly after invoking `persist`. +> You can not rely on a generated identifier to be available directly after invoking `persist`. > The inverse is also true. You can not rely on a generated identifier being not available > after a failed flush operation. @@ -88,15 +45,12 @@ The semantics of the persist operation, applied on an entity X, are as follows: * If X is a new entity, it becomes managed. The entity X will be entered into the database as a result of the flush operation. * If X is a preexisting managed entity, it is ignored by the persist operation. However, the persist operation is cascaded to entities referenced by X, if the relationships from X to these other entities are mapped with cascade=PERSIST or cascade=ALL (see "Transitive Persistence"). * If X is a removed entity, it becomes managed. -* If X is a detached entity, the behavior is undefined. - -> **CAUTION** -> Do not pass detached entities to the persist operation. +* If X is a detached entity, an exception will be thrown on flush. ++ Removing entities -An entity can be removed from persistent storage by passing it to the `EntityManager#remove($entity)` method. By applying the `remove` operation on some entity, that entity becomes REMOVED, which means that its persistent state will be deleted once `EntityManager#flush()` is invoked. The in-memory state of an entity is unaffected by the `remove` operation. +An entity can be removed from persistent storage by passing it to the `EntityManager#remove($entity)` method. By applying the `remove` operation on some entity, that entity becomes REMOVED, which means that its persistent state will be deleted once `EntityManager#flush()` is invoked. > **CAUTION** > Just like `persist`, invoking `remove` on an entity does NOT cause an immediate SQL @@ -115,7 +69,9 @@ The semantics of the remove operation, applied to an entity X are as follows: * If X is a managed entity, the remove operation causes it to become removed. The remove operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=REMOVE or cascade=ALL (see "Transitive Persistence"). * If X is a detached entity, an InvalidArgumentException will be thrown. * If X is a removed entity, it is ignored by the remove operation. -* A removed entity X will be removed from the database as a result of the flush operation. +* A removed entity X will be removed from the database as a result of the flush operation. + +After an entity has been removed its in-memory state is the same as before the removal, except for generated identifiers. ++ Detaching entities @@ -340,6 +296,107 @@ Do not blindly apply cascade=all to all associations as it will unnecessarily degrade the performance of your application. +++ Synchronization with the Database + +The state of persistent entities is synchronized with the database on flush of an `EntityManager` +which commits the underlying `UnitOfWork`. The synchronization involves writing any updates to +persistent entities and their relationships to the database. Thereby bidirectional relationships +are persisted based on the references held by the owning side of the relationship as explained +in the Association Mapping chapter. + +The flush operation applies to a managed entity with the following semantics: + +* The entity itself is synchronized to the database, if it has changed. + +For all (initialized) relationships of the entity the following semantics apply to each +associated entity X: + +* If X is new and persist operations are configured to cascade on the relationship, + X will be persisted. +* If X is new and no persist operations are configured to cascade on the relationship, + an exception will be thrown as this indicates a programming error. +* If X is removed and persist operations are configured to cascade on the relationship, + an exception will be thrown as this indicates a programming error (X would be re-persisted by the cascade). +* If X is detached and persist operations are configured to cascade on the relationship, + an exception will be thrown (This is semantically the same as passing X to persist()). + +The flush operation applies to a removed entity by deleting its persistent state from the database. +No cascade options are relevant for removed entities on flush. + ++++ The size of a Unit of Work + +The size of a Unit of Work mainly refers to the number of managed entities at +a particular point in time. + ++++ The cost of flushing + +How costly a flush operation is, mainly depends on two factors: + +* The size of the EntityManager's current UnitOfWork. +* The configured change tracking policies + +You can get the size of a UnitOfWork as follows: + + [php] + $uowSize = $em->getUnitOfWork()->size(); + +The size represents the number of managed entities in the Unit of Work. This +size affects the performance of flush() operations due to change tracking +(see "Change Tracking Policies") and, of course, memory consumption, so you +may want to check it from time to time during development. + +> **CAUTION** +> Do not invoke `flush` after every change to an entity or every single invocation of +> persist/remove/merge/... This is an anti-pattern and unnecessarily reduces the +> performance of your application. Instead, form units of work that operate on your objects +> and call `flush` when you are done. While serving a single HTTP request there should +> be usually no need for invoking `flush` more than 0-2 times. + ++++ Direct access to a Unit of Work + +You can get direct access to the Unit of Work by calling `EntityManager#getUnitOfWork()`. +This will return the UnitOfWork instance the EntityManager is currently using. + + [php] + $uow = $em->getUnitOfWork(); + +> **NOTE** +> Directly manipulating a UnitOfWork is not recommended. When working directly with the +> UnitOfWork API, respect methods marked as INTERNAL by not using them and carefully read +> the API documentation. + ++++ Entity State + +As outlined in the architecture overview an entity can be in one of four possible states: +NEW, MANAGED, REMOVED, DETACHED. If you explicitly need to find out what the current state +of an entity is in the context of a certain `EntityManager` you can ask the underlying +`UnitOfWork`: + + [php] + switch ($em->getUnitOfWork()->getEntityState($entity)) { + case UnitOfWork::MANAGED: + ... + case UnitOfWork::REMOVED: + ... + case UnitOfWork::DETACHED: + ... + case UnitOfWork::NEW: + ... + } + +An entity is in MANAGED state if it is associated with an `EntityManager` and it is not REMOVED. + +An entity is in REMOVED state after it has been passed to `EntityManager#remove()` until the +next flush operation of the same EntityManager. A REMOVED entity is still associated with an +`EntityManager` until the next flush operation. + +An entity is in DETACHED state if it has persistent state and identity but is currently not +associated with an `EntityManager`. + +An entity is in NEW state if has no persistent state and identity and is not associated with an +`EntityManager` (for example those just created via the "new" operator). + + ++ Querying Doctrine 2 provides the following ways, in increasing level of power and flexibility, to query for persistent objects. You should always start with the simplest one that suits your needs. From 1017e2c6c7ceacf73924dbad0ed76505f2ccf6c8 Mon Sep 17 00:00:00 2001 From: beberlei Date: Sat, 10 Jul 2010 08:33:12 +0200 Subject: [PATCH 3/3] Extended details on how to remove entities and their associations --- manual/en/working-with-objects.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/manual/en/working-with-objects.txt b/manual/en/working-with-objects.txt index 588082e11..29f9cfab5 100644 --- a/manual/en/working-with-objects.txt +++ b/manual/en/working-with-objects.txt @@ -117,6 +117,30 @@ The semantics of the remove operation, applied to an entity X are as follows: * If X is a removed entity, it is ignored by the remove operation. * A removed entity X will be removed from the database as a result of the flush operation. +Removing an entity will also automatically delete any exisiting records in many-to-many +join tables that link this entity. The action taken depends on the value of the `@joinColumn` +mapping attribute "onDelete". Either Doctrine issues a dedicated `DELETE` statement +for records of each join table or it depends on the foreign key semantics of +onDelete="CASCADE". + +Deleting an object with all its associated objects can be achieved in multiple +ways with very different performance impacts. + +1. If an association is marked as `CASCADE=REMOVE` Doctrine 2 will fetch this + association. If its a Single association it will pass this entity to + ´EntityManager#remove()`. If the association is a collection, Doctrine will loop over all + its elements and pass them to `EntityManager#remove()`. In both cases the + cascade remove semantics are applied recursively. For large object graphs + this removal strategy can be very costly. +2. Using a DQL `DELETE` statement allows you to delete multiple entities of a + type with a single command and without hydrating these entities. This + can be very efficient to delete large object graphs from the database. +3. Using foreign key semantics `onDelete="CASCADE"` can force the database + to remove all associated objects internally. This strategy is a bit + tricky to get right but can be very powerful and fast. You should be aware + however that using strategy 1 (`CASCADE=REMOVE`) completly by-passes + any foreign key `onDelete=CASCADE` option, because Doctrine will fetch and remove + all associated entities explicitly nevertheless. ++ Detaching entities