-
Notifications
You must be signed in to change notification settings - Fork 38k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@CacheEvict beforeInvocation with transaction does not work #23192
Comments
Good catch!
According to its class-level Javadoc, I would say that
However, there is definitely a conflict here with the documentation for
Unfortunately, it does not appear that it would be easy to make that work within a transaction given the current API for @snicoll, what are your thoughts on the matter? |
I tried to resolved follow, record Evict beforeInvocation to ThreadLocal : EvictCacheThreadLocalContext.java package org.springframework.cache;
public class EvictCacheThreadLocalContext {
private static final ThreadLocal<Boolean> evictBeforeInvocation = ThreadLocal.withInitial(() -> false);
public static boolean isEvictBeforeInvocation() {
return evictBeforeInvocation.get();
}
public static void setEvictBeforeInvocation(boolean beforeInvocation) {
evictBeforeInvocation.set(beforeInvocation);
}
public static void remove() {
evictBeforeInvocation.remove();
}
} CacheAspectSupport.java private void performCacheEvict(
CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {
Object key = null;
for (Cache cache : context.getCaches()) {
if (operation.isCacheWide()) {
logInvalidating(context, operation, null);
doClear(cache);
} else {
if (key == null) {
key = generateKey(context, result);
}
logInvalidating(context, operation, key);
try {
EvictCacheThreadLocalContext.setEvictBeforeInvocation(operation.isBeforeInvocation());
doEvict(cache, key);
} finally {
EvictCacheThreadLocalContext.remove();
}
}
}
} TransactionAwareCacheDecorator.java @Override
public void evict(final Object key) {
if (EvictCacheThreadLocalContext.isEvictBeforeInvocation()) {
this.targetCache.evict(key);
return;
}
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
TransactionAwareCacheDecorator.this.targetCache.evict(key);
}
});
} else {
this.targetCache.evict(key);
}
} |
Thanks for the example workaround, @wusthuke. A We will think it over. |
After some back and forth, I've introduced a new In case of |
@jhoeller I don't see any commit linked to this issue and it is still open. Am I missing something? |
Juergen hasn't committed that change yet. During the team call yesterday, we discussed that the proposal likely would not cover the case when |
@sbrannen I verified 5.2.0.RELEASE, and found that this implementation has a little problem. when cache is empty and database exists, after AbstractCacheInvoker my modify as if (immediate) {
cache.evictIfPresent(key);
cache.evict(key);
} |
Spring Version: 5.1.7.RELEASE
When Spring transactions are enabled, the
Cache
operation is proxied byTransactionAwareCacheDecorator
. Only theget()
operation is invoked directly. Theput()
,evict()
, andclear()
operations will be delayed until after the transaction has been committed (via a customTransactionSynchronizationAdapter.afterCommit()
callback).When
username='zhangsan'
is cached, theupdateAndRefresh()
method will return the old cached value becauseuserRepository.getByUsername()
is executed before the transaction is committed.Even if
beforeInvocation=true
,CacheAspectSupport.processCacheEvicts()
also only sets cache operation toThreadLocal
.Is there is a bug for
@CacheEvict(beforeInvocation = true, ...)
when executing within a transaction?CacheAspectSupport.java
TransactionAwareCacheDecorator.java
The text was updated successfully, but these errors were encountered: