Due to the lack of support from Spring (as far as I was able to find), to a "taggable cache" which is inspired in
Drupal's cache core. I have decided to implement my own one... For now is
First of all add to your pom the dependency
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
....
<dependency>
<groupId>com.github.KevinGuancheDarias</groupId>
<artifactId>taggable-cache</artifactId>
<version>v0.2.0</version>
</dependency>
</dependencies>
In any of your Spring Configurations beans Import the default Configuration, for Example:
@SpringBootApplication()
@Import(TaggableCacheDefaultConfiguration.class)
public class Application {
}
After that you should be able to use the annotations @TaggableCacheable
, @TaggableCacheEvictByKey
and @TaggableCacheEvictByTag
Example usage of the annotations:
@Service
public class UserService {
...
@TaggableCacheEvictByTag(tags = "user:#id")
public User saveUser(int id) {
...
}
}
@Service
public class UserMassiveOperations {
@TaggableCacheable(tags = {"user:#userId", "world_conquer:list"})
public void heavyThing(int userId) {
serviceA.doHeavy();
worldConquerService.saveNew(...);
...
}
}
Example usage of the programmatic way
@Service
@AllArgsConstructor
public class UserService {
public static final TagHelper TAG_HELPER = new TagHelper("user");
private final TaggableCacheManager taggableCacheManager;
public User saveUser(int id) {
...
taggableCacheManager.evictByCacheTag(TAG_HELPER.plainPart(id));
}
}
@Service
public class UserMassiveOperations {
private final TaggableCacheManager taggableCacheManager;
public void heavyThing(int userId) {
var cacheKey = "heavyThing_user_" + userId;
if (!taggableCacheManager.keyExists(cacheKey)) {
serviceA.doHeavy();
worldConquerService.saveNew(...);
...
taggableCacheManager.saveEntry(
cacheKey,
null,
List.of(UserService.TAG_HELPER.plainPart(userId), WorldConquerService.TAG_HELPER.list())
);
}
}
}
Just see how the current works TaggableCacheDefaultConfiguration
You can switch the used annotation PlaceholderResolver
, default is DefaultPlaceholderResolver
(which translate using
hash replacement) for example "user:#id"
would be translate to user:6
, for SpringSpelPlaceholderResolver
this
would do the same translation "\"user:\" + #id"
... While Spring one is way superior (supports method invocation,
statics, and more) is not the default as most times the cache is going to be entity:id
Just as Drupal has the cache contexts, would be something interesting there, so a cached entry may depend on something, for example the logged in user @TaggableCacheable(contexts = "user"), this is something that I have to give a better think on how to implement that
A question to StackOverflow was added regarding this pattern in Spring, link the user John Blum added a nice answer, of how to extend the current Spring Cache ecosystem for my use case... A future rewrite may delegate the caching in the Spring's CacheManager instead of implementing, owns way, as in some years I may need to use Redis for caching a distributed backend... an instead of implementing my own RedisCacheManager, I can use the Spring's one... For now the project will continue to use its own CacheManager, not to worry about the rewrite as the new cache managers delegating to Spring will implement the existing TaggableCacheManager.