https://github.com/qaware/collection-cacheable-for-spring
Spring cache extension for putting a whole collection of entities as single cache items
https://github.com/qaware/collection-cacheable-for-spring
Last synced: 4 months ago
JSON representation
Spring cache extension for putting a whole collection of entities as single cache items
- Host: GitHub
- URL: https://github.com/qaware/collection-cacheable-for-spring
- Owner: qaware
- License: apache-2.0
- Created: 2020-12-30T11:42:47.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2024-07-01T07:49:59.000Z (over 1 year ago)
- Last Synced: 2024-12-25T17:06:48.814Z (about 1 year ago)
- Language: Java
- Size: 109 KB
- Stars: 60
- Watchers: 8
- Forks: 9
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Collection Cacheable for Spring
[](https://github.com/qaware/collection-cacheable-for-spring/actions?query=workflow%3A%22build%22)
[](https://sonarcloud.io/dashboard?id=qaware_collection-cacheable-for-spring)
[](https://sonarcloud.io/dashboard?id=qaware_collection-cacheable-for-spring)
[](https://mvnrepository.com/artifact/de.qaware.tools.collection-cacheable-for-spring/collection-cacheable-for-spring-starter)
This library provides the `@CollectionCacheable` annotation extending Spring's caching mechanism, in
particular `@Cacheable`.
`@CollectionCacheable` supports putting a whole collection of entities as single cache items, thus enabling an efficient
integration of batch retrieval of entities. See the example usage below for a detailed explanation.
## Getting started
Inside your Spring Boot application, add the following (maven) dependency:
```
de.qaware.tools.collection-cacheable-for-spring
collection-cacheable-for-spring-starter
1.3.0
```
You may also use the separate artifact `collection-cacheable-for-spring-api`
providing the annotation only with minimal dependencies.
## Example usage
Suppose your entity looks as follows,
```java
class MyEntity {
long id;
String value;
}
```
and you have defined a method to retrieve one and many values by their unique `id` as follows:
```java
class MyRepository {
@Nullable
MyEntity findById(long id) {
// retrieve one MyEntity from persistence layer (if existing)
}
// map key is MyEntity.id
Map findByIds(Collection ids) {
// do efficient batch retrieve of many MyEntity's and build result map
}
}
```
Now, to efficiently implement a cache on `MyRepository`, you want the following to happen:
* Whenever a call to `findById` occurs, either a cache hit should be returned, or the method is called putting the
result in the cache unless it is `null`.
* Whenever a call to `findByIds` occurs, the method should be called with the non-empty subset of the given `ids`
parameter which are **not** in the cache. Otherwise, the cache hits are simply returned.
To illustrate the above behavior further, consider the following call sequence:
1. `findById(1)` retrieves an entity from the persistence layer and fills the cache for id `1`
1. `findByIds({1, 2})` finds the cache hit for id `1` and only calls `findByIds({2})` as a delegate. It then fills the
cache for id `2`.
1. `findById(2)` just retrieves the cache hit for id `2`.
1. `findByIds({1, 2})` will not call anything and just returns a map built from the cache hits for id `1` and `2`.
In order to implement exactly this behavior, this library is used as follows:
```java
class MyRepository {
@Nullable
@Cacheable(cacheNames = "myCache", unless = "#result == null")
MyEntity findById(long id) {
// retrieve one MyEntity from persistence layer (if existing)
}
@CollectionCacheable(cacheNames = "myCache")
Map findByIds(Collection ids) {
// do efficient batch retrieve of many MyEntity's and build result map
}
}
```
See [this test repository](collection-cacheable-for-spring-starter/src/test/java/de/qaware/tools/collectioncacheableforspring/CollectionCacheableTestRepository.java)
for a completely worked out example.
## Advanced usage
### Additional "findAll" method
If your repository also provides a "findAll"-like method without any arguments, you can integrate this as well into your
cache as follows:
```java
class MyRepository {
@CollectionCacheable(cacheNames = "myCache")
// map key is MyEntity.id
Map findAll() {
// do efficient batch retrieve of many MyEntity's and build result map
}
}
```
The library assumes that such methods do not have any arguments. Note that the return value must still be a `Map`,
otherwise the library is unable to determine the cache id.
### Also consider `null` as cache hit
Under some circumstances, it is also desirable to cache also `null` results. This must be explicitly enabled via
the `putNull` flag on batch retrieval as follows:
```java
class MyRepository {
@CollectionCacheable(cacheNames = "myCache", putNull = true)
Map findByIds(Collection ids) {
// do efficient batch retrieve of many MyEntity's and build result map
}
}
```
### Using Set or List as method argument
The methods annotated with `@CollectionCacheable` use the base interface `Collection` in the above examples, but
also `List<>` and `Set<>` interfaces are supported. Note though that this may change the actual passed implementation
to `LinkedList` or `HashSet`, respectively (see `DefaultCollectionCreator` and `SetCollectionCreator` implementations).
You can add support for more collection-like types by providing beans deriving from `CollectionCreator`, or even
override the given creators thanks to Spring Boot autoconfiguration.
## Contributing
Please report [issues or feature requests](https://github.com/qaware/collection-cacheable-for-spring/issues).
Also see this [Spring issue here](https://github.com/spring-projects/spring-framework/issues/23221). As of now,
integration into Spring is not ideal, so this library might break with future Spring releases.