JPA : What is the behaviour of merge with lazy initialized collection?
Here are the sequences leading to the question :
- I have a Team record, and 3 Player records in the database. Team entity has a List that is using FetchType.LAZY, CascadeType.ALL
- The search button on the webui is clicked
- Query in the server side using JPA query is invoked, find开发者_开发知识库ing all the Team records, in this case, only 1 record of the team entity returned from the query (which has a proxy of the list of player entities)
- Map this teamEntity to a DTO, and return this DTO to the webui, skipping the mapping of the list of player entities
- Webui renders the DTO in a html form, ready to receive modifications from the user
- User modifies the team's properties, like the date of when it's founded
- The save button on the webui is clicked
- Converting the DTO to the team entity, to be used to update the already existing team record
- But in this case, if i were to use the em.merge(teamEntity), the team record will be updated, but what will happen to the list of players ? Because when converting from DTO to the team entity, the teamEntity has an empty list of players entities. And after merging, i notice that the teamEntity has 0 size of the details. But after refreshing that entity, em.refresh(teamEntity), it will return 3 of the detail size.
Im confused on :
- Why is it the size is 0 after merged ? It's like not representing the record anymore
- Before doing the test, i was thinking that the details will be removed since im merging a teamEntity with an empty detail.
Please enlighten me :)
Thank you !
JPA Specification says:
The semantics of the merge operation applied to an entity X are as follows:
If X is a detached entity, the state of X is copied onto a pre-existing managed entity instance X' of the same identity or a new managed copy X' of X is created.
If X is a new entity instance, a new managed entity instance X' is created and the state of X is copied into the new managed entity instance X'.
If X is a removed entity instance, an
IllegalArgumentException
will be thrown by the merge operation (or the transaction commit will fail).If X is a managed entity, it is ignored by the merge operation, however, the merge operation is cascaded to entities referenced by relationships from X if these relationships have been annotated with the cascade element value
cascade=MERGE
orcascade=ALL
annotation.For all entities Y referenced by relationships from X having the cascade element value
cascade=MERGE
orcascade=ALL
, Y is merged recursively as Y'. For all such Y referenced by X, X' is set to reference Y'. (Note that if X is managed then X is the same object as X'.)If X is an entity merged to X', with a reference to another entity Y, where
cascade=MERGE
orcascade=ALL
is not specified, then navigation of the same association from X' yields a reference to a managed object Y' with the same persistent identity as Y.
As you can see, there is no magic here. The state of detached instance is copied into the newly created managed instance. Since your detached instance has an empty list, managed instance would have it too.
Further behaviour depends on ownership of relationship, since representation in the database reflects the owning side of relationship:
- If
Team
is the owning side, relationships betweenTeam
andPlayer
s will be destroyed during flush (butPlayer
itself would survive unless you haveorphanRemoval = true
on your relationship). - Otherwise having the empty list in
Team
doesn't affect the database.
If you refresh the Team
before flushing the context, all properties of Team
are rewritten by values from the database, therefore list of Player
s is restored (since the empty list of players wasn't flushed yet).
If you call flush()
before calling refresh()
, and Team
is the owning side, list will be empty, since destruction of relationships was propagated to the database during flush()
.
精彩评论