I have a repository interface and two classes implementing it, one is cache repository and the other is MongoDB repository.
public interface Repository {}
@Primary
@Component
public class CacheRepo implement Repository {}
@Component
public class MongoDBRepo implement Repository {}
The ideal process for fetching an item would be to check if it exists in the cache using cache repo and if not, go with MongoDB repo, I have a @Primary
on my CacheRepo
class, and dependency inject Repository interface in my service, but how could I still use the same injected instance as MongoDBRepo
if item not found in Cache? Is there something like @Secondary
annotation?
CodePudding user response:
I would suggest you take a look at @Qualifier
. With it, you can specify which Bean you want to inject. For that, the best option is to define a component name for each bean as follows:
public interface Repository {}
@Component("cache")
public class CacheRepo implement Repository {}
@Component("mongodb")
public class MongoDBRepo implement Repository {}
Then, in another Spring-managed bean you can specify the implementation as follows:
@Autowired
@Qualifier("mongodb")
private Repository mongoRepo;
You can read more about it at https://www.baeldung.com/spring-qualifier-annotation.
CodePudding user response:
I think you misunderstood the purpose of @Primary
.
As the documentation says:
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
You may use @Cacheble
annotation on the MongoDBRepo
's methods to do what you want and define a custom cache manager to use your CacheRepo
Here is a useful introduction to @Cacheble
with some examples https://www.baeldung.com/spring-cache-tutorial
CodePudding user response:
What you are trying to implement is Repository Pattern
Here is a simple way to implement it
public interface MyRepository {
Optional<MyClass> findById(Long id);
}
Then you will have 3 implementations. This is where the logic lies.
@Repository
@Qualifier("db")
public interface MyDbRepository extends MyRepository, CrudRepository<MyClass, Long>{
}
@Component
@Qualifier("cache")
public class MyCacheRepository implements MyRepository {
public Optional<MyClass> findById(Long id){
return Optional.empty();
}
}
// This is the key
@Component
@Primary
public class MyDataSource implements MyRepository {
@Autowire
@Qualifier("db")
private MyRepository dbRepo;
@Autowire
@Qualifier("cache")
private MyRepository cacheRepo;
public Optional<MyClass> findById(Long id){
Optional<MyClass> myResponse = myCache.findById(id);
if(myResponse.isPresent()){
return myResponse;
} else {
myResponse = dbRepo.findById(id);
if(myResponse.isPresent){
// Update your cache here
}
return myResponse;
}
}
}