Code Monkey home page Code Monkey logo

collection-cacheable-for-spring's Introduction

Collection Cacheable for Spring

Build Status Quality Gate Status Code Coverage Maven Central

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:

<dependency>
    <groupId>de.qaware.tools.collection-cacheable-for-spring</groupId>
    <artifactId>collection-cacheable-for-spring-starter</artifactId>
    <version>1.3.0</version>
</dependency>

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,

class MyEntity {
    long id;
    String value;
}

and you have defined a method to retrieve one and many values by their unique id as follows:

class MyRepository {
    @Nullable
    MyEntity findById(long id) {
        // retrieve one MyEntity from persistence layer (if existing)
    }

    // map key is MyEntity.id
    Map<Long, MyEntity> findByIds(Collection<Long> 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
  2. 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.
  3. findById(2) just retrieves the cache hit for id 2.
  4. 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:

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<Long, MyEntity> findByIds(Collection<Long> ids) {
        // do efficient batch retrieve of many MyEntity's and build result map
    }
} 

See this test repository 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:

class MyRepository {

    @CollectionCacheable(cacheNames = "myCache")
        // map key is MyEntity.id
    Map<Long, MyEntity> 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:

class MyRepository {
    @CollectionCacheable(cacheNames = "myCache", putNull = true)
    Map<Long, MyEntity> findByIds(Collection<Long> 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.

Also see this Spring issue here. As of now, integration into Spring is not ideal, so this library might break with future Spring releases.

collection-cacheable-for-spring's People

Contributors

dependabot[bot] avatar neiser avatar prokop7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

collection-cacheable-for-spring's Issues

putUncachedResultToCache is actually putting all items to cache.

version 1.3.0.in CollectionCacheableCacheInterceptor line 89

 ReturnValueConverter.MapLikeReturnValue returnValue = operation.getReturnValueConverter().convert(uncachedResult, cacheResult);
        if (context.canPutToCache(uncachedResult)) {
            putUncachedResultToCache(returnValue, context);
            if (operation.isPutNull()) {
                putNullToCache(returnValue, idsArgument, context);
            }
        }

putUncachedResultToCache(returnValue, context); this line. should use uncachedResult value instead of returnValue.

Memory leakage in integration tests

Hello again!

After running several integration tests in a springboot application using the cachecollection annotation, i get OutOfMemory exceptions. If I run the tests one by one they all pass so I guess there is something accumulating with each test.

Do you know of any possible memory leakage of the library or how tests deal with caches. I get the error even when "disabling" caching for tests!

Thank you very much!

Did not find zero or one Collection argument.

    @CollectionCacheable(value = CacheConstant.CACHE_ACCOUNT)
    @Override
    public Map<Long, LoginAccountDO> queryLoginAccounts() {
        return lambdaQuery().gt(LoginAccountDO::getConnectedStatus, OFFLINE).list().stream()
                .collect(Collectors.toMap(LoginAccountDO::getMpAccountId, e -> e));
    }

    @CollectionCacheable(value = CacheConstant.CACHE_ACCOUNT)
    @Override
    public Map<Long, LoginAccountDO> queryLoginByAccounts(Set<Long> idSet) {
        //noinspection unchecked
        return idSet.size() > 0 ? lambdaQuery().in(LoginAccountDO::getMpAccountId, idSet)
                .orderByDesc(LoginAccountDO::getLoginTime).list().stream()
                .collect(Collectors.toMap(LoginAccountDO::getMpAccountId, e -> e)) :
                Collections.emptyMap();
    }

    @Cacheable(value = CacheConstant.CACHE_ACCOUNT, key = "#mpAccountId", unless = "#result == null ")
    @Override
    public Optional<LoginAccountDO> queryLoginByAccount(Long mpAccountId) {
        return lambdaQuery().eq(LoginAccountDO::getMpAccountId, mpAccountId)
                .gt(LoginAccountDO::getConnectedStatus, OFFLINE).oneOpt();
    }

Isn't I useful, why do you report such an exception?

@CollectionCacheable completely ignored

Hey there,

I just try to get it run with your annotation, but it seems like this annotation gets completely ignored. I have @EnableCaching and also my @Cacheable annotations work like expected, but not the CollectionCacheable.

Im using Spring 2.7.5 and Kotlin, but also tried makeing a class with java where it also did not work. I see, that the CollectionCacheableCacheInterceptor gets called in the debug mode, but never checks my annotation.

Also I have the question as I where not able to test it, if it is intended that multiple calls to en empty findAll() Method would use the cache. So that first call sees its empty, so Im putting all with their key inside and second call just gets all the entries from a cache and returns it.

Suggest for getting cache value from redis

I review the code and found that CollectionCacheableCacheInterceptor#findInCaches this method get value by key from redis one by one using RedisConnection#get.
If the number of keys is large, A lot of time will be wasted on network requests.
Do u consider change the way of getting cache value from RedisConnection#get to RedisConnection#mget

debug logs

By adding dependency collection-cacheable-for-spring-starter into my Spring Boot app it stops logging Spring cache debug logs. Log were enabled by adding

logging:
level:
org.springframework.cache: TRACE

into application.yaml. With CollectionCacheable cache debug logs are malfunctioning.

findAll() include spring pageable

Im getting list(org.springframework.data.domain.Pageable)'. Did not find exactly one Collection-like argument.

Could we include the org.springframework.data.domain.Pageable to have the same functionality as findAll().

thanks

CollectionCacheable returning a list of entities

Hey @neiser ! First of all thank you for this project, it's really nice.

My question here is if there is any way to make it work in functions like this?

@CollectionCacheable(cacheNames = "myCache")
public List<MyEntity> findByIds(Collection<Long> ids)

If not, is there any way I could contribute to make this possible? It would be really useful to be able to do so.

Bug when id params have same id, returnValue will occur same result if cache expired in loop

we get idsArgument from CollectionCacheableCacheInterceptor#injectCollectionArgument.
but when args have the same id params, we will get repeated id in idsArgument.

Then when the method run into CollectionCacheableCacheInterceptor#findIdsInCache

because idsArgument is run in loop,if have two or more same id occur. The first id hit cache will put into cacheResult.

At that time, if cache expired. The next the id will not match cache and then get result from DB, then put the result into uncachedResult.

when merge uncachedResult and cacheResult. The returnValue will occur two or more same id result when use ListReturnValueConverter

Specify parameter cache key

Hi there,
I wonder why, there is no way that I make method that, has two or more parameter.

I think that it can be easily done by making field annotation.

So can u develop it?
If not, I want to know that reason.

I think you can provide Hash-based Cache storage

Thank you for providing this extension, which helps me solve a lot of problems.
If I use Redis, in fact using Redis to make a cache is a very common operation, but I don't want all the default String, but use Hash storage, which is more in line with the scene, I think it can be used as an open to developer selection

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.