SpringBoot Bean dependency injection with annotation #Primary and #Secondary - spring-boot

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?

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 = cacheRepo.findById(id);
if(myResponse.isPresent()){
return myResponse;
} else {
myResponse = dbRepo.findById(id);
if(myResponse.isPresent){
// Update your cache here
}
return myResponse;
}
}
}

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.

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

Related

How to use unit of work in rest controller properly?

public interface CourseRepo extends CrudRepository<Course, Long> {
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class UnitOfWork {
CourseRepo courses;
StudentRepository students;
StudyProgramRepository studyPrograms;
StudySchemeRepo studySchemes;
FeeStructureRepository feeStructures;
}
#RestController
public class TestController {
#Autowired
UnitOfWork uow;
#GetMapping("/addcr")
public String addCourse() {
Course cr = new Course();
cr.setTitle("DingDong course");
uow.getCourses().save(cr);
return "course Added..!!" ;
}
APPLICATION FAILED TO START
***************************
Description:
Field uow in com.srs.TestController required a bean of type 'com.srs.uow.UnitOfWork' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.srs.uow.UnitOfWork' in your configuration.
if i remove autowired and add a bean
#RestController
public class TestController {
#Bean
public UnitOfWork uow() {
return new UnitOfWork();
}
#GetMapping("/addcr")
public String addCourse() {
Course cr = new Course();
cr.setTitle("DingDong course");
uow().getCourses().save(cr);
return "course Added..!!" ;
}
java.lang.NullPointerException: Cannot invoke "com.srs.jpa.CourseRepo.save(Object)"
because the return value of "com.srs.uow.UnitOfWork.getCourses()" is null
i tried both autowired and in this case how can i use autowired or bean properly ?
Your class need to be annotated with #Component to be used with DI provider by #Autowired annotation
For the same reason each repository of your class need to be annotated with #Autowired
The Error Message gives the answer.
Field uow in com.srs.TestController required a bean of type 'com.srs.uow.UnitOfWork' that could not be found.
spring is searching for a bean from type UnitOfWork. You have to add this class to the application context from spring boot. To accomplish this you have to annotate the class UnitOfWork with #bean or #Data if you use lombok.
After this the spring application can find the Class UnitOfWork and auto wire it.
Since UnitOfWork (a somewhat misleading name in the JPA context) autowires data repositories, it has to be a Spring Bean itself.
The easiest and most common way is to annotate the class with one of the annotations #Service, #Component or #Bean, depending on the semantic of the class. There are also other ways, like the #Bean on method-level as you used.
To use the fully initialized bean you need to autowire it where you want to use it, not calling the create method. E.g. calling uow() as in your sample, bypasses the Spring Bean mechanism and creates a new instance, which hasn't been fully initialized (thus the NullPointerException).
Usually, the beans are autowired as fields, sometimes they are autowired in mehtod parameters (especially when working with #Bean on method-level in the same class).
E.g.
#Component
#Getter
#RequiredArgsConstructor
public class UnitOfWork {
private final CourseRepo courses;
private final StudentRepository students;
private final StudyProgramRepository studyPrograms;
private final StudySchemeRepo studySchemes;
private final FeeStructureRepository feeStructures;
}

Autowiring of Service and Service Implementation class

Following are my code
#RestController
public class EmployeeController {
#Autowired
EmployeeService empService;
public EmployeeController (EmployeeService Impl empServiceImpl) {
super();
this.empService = empServiceImpl;
}
}
#Service
public interface EmployeeService {
public List<EmployeeDTO> getAllEmployeeDetails()
}
public class EmployeeServiceImpl {
public List<EmployeeDTO> getAllEmployeeDetails(){
//methods business logic and repo call goes here
}
}
When I start my server I am getting below error.
Parameter 1 of constructor in
com.app.in.controller.EmployeeController required a bean of type
'com.app.in.service.EmployeeServiceImpl' that could not be found
My understanding might be wrong. If I annotate the EmployeeSeriveImpl class also with #Service then it working.Is that is the correct way to do it ? My question is the service interface is annotated with #Service still why its implementation is also required to annotation. Please let me know if I miss something in that ? What is the standard method to solve this issue ?
You can get your dependency injected using a constructor. And #Autowired is optional in this case.
This is your example, but with a few corrections:
#RestController
public class EmployeeController {
// private final is a good practice. no need in #Autowire
private final EmployeeService empService;
// this constructor will be used to inject your dependency
// #Autowired is optional in this case, but you can put it here
public EmployeeController (EmployeeService empServiceImpl) {
this.empService = empServiceImpl;
}
}
I assume you have an interface EmployeeService and class EmployeeServiceImpl which implements that interface and is Spring Bean.
Something like this:
#Service
public class EmployeeServiceImpl implements EmployeeService {}
Why this #Service is needed? When you put this annotation on your class, Spring knows this is a bean that Spring should manage for you (container will create an instance of it and inject it wherever it is needed).
Check Spring docs to get more details about Dependency Injection.
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null.

How to autowire SimpleJpaRepository in a spring boot application?

I'm working on a project that uses SpringBoot 2.0.5 version, Spring Data JPA to persists and retrieve records using JPA. I autowired SimpleJpaRepository in the service layer. But while starting my application, it failed with
"NoSuchBeanDefinitionException"- No qualifying bean of type
'org.springframework.data.jpa.repository.support.SimpleJpaRepository<?, ?>'
available: expected at least 1 bean which qualifies as autowire candidate.
My controller, service and DAO are like below
Controller class:
#Controller
public class MyController{
#Autowired
private MyService<Person,PersonPK> service;
Service layer as
public interface MyService<V,K>{
methods defined
}
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private SimpleJpaRepository<V,K> repository; // This dependency is failing
}
Application as :
#SpringBootApplication (exclude = {SecurityAutoConfiguration.class})
#EnableJpaRepositories
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Is my approach incorrect? Is that not the correct way of autowiring the SimpleJpaRepository.
There is no need for me to extend SimpleJpaRepository as Spring provided JPARepository is good for me, for now.
Thanks
You still need to create a repository interface that extends JpaRepisitory, or the spring repository type of your choice.
To quote the spring data documentation:
1.2.1 Defining repository interfaces
As a first step you define a domain class-specific repository interface. The interface must extend
Repository and be typed to the domain class and an ID type. If you
want to expose CRUD methods for that domain type, extend
CrudRepository instead of Repository.
Once you do create a new repository type, you will autowire by that type rather than the SimpleJpaRepository.
One way to get an implementation of the SimpleJpaRepository is by using a Configuration class to create an instance as a bean that you will use inside your service.
#Configuration
public class PersistanceConfiguration {
#PersistenceContext
private EntityManager entityManager;
#Bean
public SimpleJpaRepository<YourEntity, Long> getYourEntitySimpleRepository() {
return new SimpleJpaRepository<>(YourEntity.class, entityManager);
}
}
And inject it to your service as you would do with a JpaRepository, for example:
#Service
public class YourEntityServiceImpl<YourEntity, Long> implements YourEntityService {
private JpaRepository<YourEntity, K> repository;
private SimpleJpaRepository<YourEntity, K> simpleRepository;
#Autowired
public YourEntityServiceImpl(YourEntityRepository repository, SimpleJpaRepository<YourEntity, Long> simpleRepository) {
this.repository = repository;
this.simpleRepository = simpleRepository;
}
}
You should create a repository interface that extending JpaRepisitory.
#Repository
public interface MyRepository extends JpaRepisitory<T, ID> {
//
}
And the you should Autowired in your service class.
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private MyRepository myRepository;
}

Spring Boot detects 2 identical repository beans

I am using Spring Boot with Spring Data JPA, there is only one #SpringBootApplication. And I have also a repository classes, for example:
package com.so;
public interface SORepository {
//methods
}
And impl
#Repository("qualifier")
#Transactional(readOnly = true)
public class SORepositoryImpl implements SORepository {
//methods
}
The proplem is, when I start the application, I get following error:
Parameter 0 of constructor in com.so.SomeComponent required a single bean, but 2 were found:
- qualifier: defined in file [path\to\SORepositoryImpl.class]
- soRepositoryImpl: defined in file [path\to\SORepositoryImpl.class]
So, as you see, somehow 2 beans of one repository class are created. How can I fix this?
You can use Spring Data JPA methods having created Proxy element and than inject it into public class SORepositoryImpl:
public interface Proxy() extends JpaRepository<Element, Long>{
Element saveElement (Element element); //ore other methods if you want}
And than:
#Repository
#Transactional(readOnly = true)
public class SORepositoryImpl implements SORepository {
#Autowired
private Proxy proxy;
//end realisation of methods from interface SORepository
}
Try taking the #Repository annotation off the SORepositoryImpl class
e.g.
#Transactional(readOnly = true)
public class SORepositoryImpl implements SORepository {
//methods
}
The error message is implying you have two beans, one named "qualifier" and one named "soRepositoryImpl", which is probably in a Config class.
I guess you should share your SomeComponent class supposing you have no extra configuration class/xml. My take is that you are injecting as 'soRepositoryImpl' there where you have defined as 'qualifier'. Having two options them. I would say to just remove the annotation parameter 'qualifier' and it should work.
Moreover, unless you want do specify an custom DAO implementation you can avoid #Repository at all (That's an annotation you use to make it injectable for your services). You can just create an interface extending Spring interface and define methods for queries.
For example:
public interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
Then you can just inject it in your services/controller directly.
private final PersonRepository personRepository;
public PersonController(final PersonRepository personRepository) {
this.personRepository = personRepository;
}
check samples:
https://spring.io/guides/gs/accessing-data-jpa/
http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
OK, I've found the issue.
I just couldn't understand, how Spring creates the second bean (soRepositoryImpl), because I've never told it, neither explicitly nor in config classes. But I figured out that the second bean us created during the instantiation of my another SORepository (which is in the different package com.another and which extends JpaRepository).
So, when Spring tries to resolve all dependencies of com.another.SORepository it somehow finds my com.so.SORepositoryImpl (which has nothing familiar with com.another.SORepository - not extending\implementing, not jpa stuff, only similar names!).
Well it seems like a Spring bug to me, because it doesn't check the real inheritance of dependent classes of repositories, only name + Impl (even in different package) suits for him.
The only thing that I should do is to rename `com.so.SORepositoryImpl and that it, no 2 beans anymore.
Thanks everyone for answers!

Could not autowire field when bean implements some interface with Spring

I am using Spring in my Java Application, all the #Autowired annotations working until now.
Simplified example would be:
#Component
public class MyBean implements MyInterface {
...
}
#Component
public class MyOtherBean {
#Autowired
private MyBean myBean;
...
}
Once I try to start the Application, I get:
java.lang.IllegalArgumentException: Can not set MyBean field MyOtherBean.myBean to $ProxyXX
The interface contains just two public simple methods and the class implements them.
Both classes are public and have public default constructor. (I even tried to instantiate them in tests.
Once I remove the implements section, everything works correctly.
What can be wrong with the implementation of the interface? What is $ProxyXX?
I suspect the issue is that Spring is injecting an AOP proxy which implements MyInterface - possibly for the purposes of transaction management or caching. Are any of MyBean's methods annotated #Transactional or annotated with any other annotation?
Ideally you'd probably want to reference MyBean by it's interface type - which should resolve the issue.
#Component
public class MyOtherBean {
#Autowired
private MyInterface myBean;
...
}
If you have more than one bean implementing MyInterface then you an always qualify your bean by name.
#Component
public class MyOtherBean {
#Autowired
#Qualifier("myBean")
private MyInterface myBean;
...
}
By default, Spring uses Java dynamic proxies to implement AOP when the bean implements an interface. The easiest and cleanest way to solve your problem is to make program on interfaces, and inject theinterface insted of the concrete class:
#Component
public class MyOtherBean {
#Autowired
private MyInterface myBean;
...
}
See http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/htmlsingle/#aop-proxying for how to force Spring to always use CGLib.

Resources