Comments (14)
@schauder
I had thought that a detached entity transitions to a persistent state by the time find()
is called based on the id field, but I realize now that this is incorrect. Therefore, just because the condition existing != null
is satisfied, it doesn't mean that the entity is in a persistent state by the time contains()
method is invoked. As you mentioned, it can return a different entity instance.
So, calling the contains()
method ultimately serves as a kind of defensive strategy, doesn't it?
from spring-data-jpa.
I think this somewhat convoluted process is necessary for cases where entity
was modified, before the call to delete, and/or to make sure optimistic locking works as intended.
Consider this:
entity
gets loaded, and detached.- the same row in the database gets modified in the database by some other process, updating the version attribute.
delete(entity)
gets called.
In this case the delete should fail, but if we use entityManager.remove(entityManger.find(..))
instead, it will actually succeed.
On the other hand, if we don't check if it exists first, calling delete
on a new entity will actually persist it, which probably isn't what was intended.
from spring-data-jpa.
@schauder How about this improvement?
from spring-data-jpa.
I think this somewhat convoluted process is necessary for cases where
entity
was modified, before the call to delete, and/or to make sure optimistic locking works as intended.Consider this:
entity
gets loaded, and detached.- the same row in the database gets modified in the database by some other process, updating the version attribute.
delete(entity)
gets called.In this case the delete should fail, but if we use `entityManager.remove(entityManger.find(..)) instead, it will actually succeed.
On the other hand, if we don't check if it exists first, calling
delete
on a new entity will actually persist it, which probably isn't what was intended.
it still cannot prevent throwing OptimisticLockingFailureException
.
merge(entity)
gets called.- the same row in the database gets modified in the database by some other process, updating the version attribute.
delete(entity)
gets called will throwOptimisticLockingFailureException
.
OptimisticLockingFailureException
should be thrown for safety if optimistic lock failed.
from spring-data-jpa.
OptimisticLockingFailureException should be thrown for safety if optimistic lock failed.
That's the point: If we don't use the entity passed into the method we might end up doing a delete which should fail with an optimistic locking exception.
from spring-data-jpa.
That's the point: If we don't use the entity passed into the method we might end up doing a delete which should fail with an optimistic locking exception.
OptimisticLockingFailureException
may be thrown no matter merge()
is called or not.
from spring-data-jpa.
If you want to use the positive result of the find
call as an indication that the entity in question is managed, you'd have to use existing
as an argument for remove
which would not throw an optimistic locking exception in the scenario I out lined.
from spring-data-jpa.
If you want to use the positive result of the
find
call as an indication that the entity in question is managed, you'd have to useexisting
as an argument forremove
which would not throw an optimistic locking exception in the scenario I out lined.
Using existing
instead of entity
may throw OptimisticLockingFailureException
also if the database row is updated between find()
and remove()
.
from spring-data-jpa.
Yes, but it won't throw an exception when the change happend before the find
from spring-data-jpa.
So is it worthy to perform find()
and merge()
then remove()
since the exception can not be avoided completely?
from spring-data-jpa.
I find out that merge
gets called to avoid InvalidDataAccessApiUsageException
not OptimisticLockingFailureException
, because there is no way to reattach a stale detached entity in JPA, see https://stackoverflow.com/a/4438358.
Here is failed tests:
[ERROR] Errors:
[ERROR] JavaConfigUserRepositoryTests>UserRepositoryTests.removeDetachedObject:629 » InvalidDataAccessApiUsage Removing a detached instance org.springframework.data.jpa.domain.sample.User#756
[ERROR] NamespaceUserRepositoryTests>UserRepositoryTests.removeDetachedObject:629 » InvalidDataAccessApiUsage Removing a detached instance org.springframework.data.jpa.domain.sample.User#756
[ERROR] UserRepositoryTests.removeDetachedObject:629 » InvalidDataAccessApiUsage Removing a detached instance org.springframework.data.jpa.domain.sample.User#757
[ERROR] JpaRepositoryTests.testCrudOperationsForCompoundKeyEntity:76 » IllegalArgument Removing a detached instance org.springframework.data.jpa.domain.sample.SampleEntity#org.springframework.data.jpa.domain.sample.SampleEntityPK@5e1258
The fix is very simple, I've updated the PR.
from spring-data-jpa.
Nobody said that merge
gets called to avoid OptimisticLockingFailureException
. The opposite is true. In the situation described in my first comment we must have an OptimisticLockingFailureException
!
from spring-data-jpa.
Issue title revised for clarity as it seemed unclear and caused confusion regarding the topic I intended to inquire about.
I agree with your explanation. @schauder
Probably, your initial response regarding the necessity of the merge()
method was in response to my inquiry. There seems to have been some confusion there. What I was curious about was why the contains()
method is called again at the point of invoking the remove()
method, even though the entity is already considered to be in a persistent state when the entityManager.find()
method is called.
Now, let's consider another scenario: Threads A and B are running independently in separate transactions. Entity@v1 is already loaded into Thread A's persistence context, and Thread A is currently modifying it. At this point, Thread B attempts to delete the same entity.
- Thread A: Loads and modifies the entity (Entity@v1 -> Entity@v2, stored in the persistence context).
- Thread B: Attempts to delete the same entity by calling the
delete()
method. - Thread B: Inside the
delete()
method,entityManager.find()
is used to retrieve the entity. Since Thread A's changes have not been committed, the entity's version in the database remains at v1. Therefore, existing is assigned Entity@v1 retrieved from the database.
In this case, although existing != null
within the delete()
method in Thread B, at the point where entityManager.contains()
is called internally to check the persistent state, the entity has already been retrieved via the find()
method into Thread B's persistence context or directly from the database.
Therefore, at the time of the remove()
method call, the entity is indeed in a persistent state.
If so, isn't it unnecessary to call contains()
?
Is there a scenario where the return value of the contains()
method is false even when the find()
method has been called and existing != null
?
from spring-data-jpa.
Yes, when entity
is in detached state. find
will return a different instance and entity
is still detached, i.e. contains(entity)
will return false
.
from spring-data-jpa.
Related Issues (20)
- @Query("delete from xxxxx") SQLGrammarException: could not execute statement HOT 9
- @Query("delete from xxxxx") SQLGrammarException: could not execute statement HOT 1
- Wrong handling of positional INOUT parameters when extracting output parameters HOT 9
- entity basePackages in third-party jars HOT 2
- Add `camelCase` to `snake_case` fallback for native query projections HOT 6
- 3.2.x Auditing behavior fails with composite keys using @IdClass but succeeds with @Embeddable HOT 3
- Unexpected Quoting on Column Name after Spring Boot 3 Migration HOT 3
- findById ignores @SQLRestriction annotation when performed in the same transaction as the save HOT 8
- The getId method was not called HOT 2
- Upgrade to Hibernate 6.5.1 HOT 1
- Remove duplicate plugin setup from parent pom.
- Invalid value for NanoOfSecond between 12am and 1am HOT 2
- Sorting of Pageable.unpaged(sort) is ignored by JPA Repository HOT 2
- Different `property` is used in `Sort.Order` method HOT 2
- Upgrade to Hibernate 6.2.25.Final
- Upgrade to Hibernate 6.4.8.Final
- Release 3.2.7 (2023.1.7)
- Release 3.3.1 (2024.0.1)
- Release 3.4 M1 (2024.1.0)
- 3.2.5 - Issue with dropping unexisting tables with ddl-auto=create HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from spring-data-jpa.