Cannot find the Repository bean in Spring boot / Spring Data MongoDB - java

I have created a spring project which has a controller and all of its logic is written in Service Interface, which is implemented by ServiceImpl class. I have a repository with which has a model.
//Service Interface
public interface Service{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class ServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me an empty list.
}
}
//KPIRepository
#Repository
public inerface KPIRepository extends MongoRepository<KPIModel, String>
{
}
//Another Service Interface in another package
public interface AnotherService{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class AnotherServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me list of values, which are inside the repo(master data).
}
}
Both of them are pointing to same repo, but in AnotherService class i am able to get values inside the repository, whereas i am not able to get any values inside Service, on doing this.kpiRepository.findAll().

Do you have spring-boot-starter-data-mongodb dependency on classpath? If yes, then is KPIRepository in the same package as your main class? If not then in your main class put this annotation #EnableMongoRepositories(basePackageClasses=KPIRepository.class) to safely tell Spring Data MongoDB to scan a different root package by type if your project layout has multiple projects and its not finding your repositories. Or you can use #EnableMongoRepositories(basePackages = "com.acme.repositories.mongo") to specify the package that contains all of your repositories.
The presense of spring-boot-starter-data-mongodb will automatically enable #EnableMongoRepositories. And Spring will automatically create proxies of all classes implementing Repository<T,ID> (your class implements MongoRepository, which itself implements Repository) and create beans of them and make them available for injection. And when your repository is in a different package then it is unable to create proxies of your repository, hence fails to create a bean of it. And since there is no bean, it cannot inject it, hence you see the error.

Did you use the #EnableMongoRepositories annotation? Take a look to this link: https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html. Review the "6.2 usage" point.
Regards

Related

Spring Boot: Why does #Autowired not work for this case?

I have a Spring Boot Java application and am trying to inject a #Service into a class. I'm using #Autowired but it's not doing it. I successfully do so in other classes, but not this one.
The service class:
#Service
#Transactional
public class TaskService {
...
}
The class where it works is a Vaadin "view" annotated with a Vaadin #Route annotation. I'm assuming there is something going on behind the scenes with the #Route annotation that allows this to work.
#Route("main")
public class TaskListView extends HorizontalLayout {
private final TaskService taskService;
public TaskListView(#Autowired TaskService taskService) {
this.taskService = taskService;
}
...
}
The class where it does not work is also a Vaadin "view", but does not have a #Route annotation, because it is not intended to be navigable to, but rather used as a sub-component of a view (i.e. would be directly instantiated inside a parent view).
public class EditNotesForm extends VerticalLayout {
#Autowired
private TaskService taskService;
...
}
The first class uses constructor injection, while the second uses property injection. I can't see why this should make a difference, as I have used both techniques successfully in other applications.
From the Vaadin website:
The only difference between using the router in a standard application and a Spring application is that, in Spring, you can use dependency injection in components annotated with #Route. These components are instantiated by Spring and become Spring-initialized beans. In particular, this means you can autowire other Spring-managed beans.
One way to access Spring components from your regular Vaadin components is by creating static get methods to retrieve your Spring components. Here is one way that can be done... but not the only.
#Component
public class StaticHelper {
private static StaticHelper instance;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> class) {
return instance.applicationContext.getBean(class);
}
}
So in your example...
private TaskService taskService = StaticHelper.getBean(TaskService.class);
Since EditNotesFormdoesn't have the Route annotation, spring can't autodiscover it and therefore it can't inject any dependency.
Since you want to instantiate it manually, you'll need to provide yourself all dependencies.
But if you still want to benefit from automatic dependency injection, take a look at https://www.baeldung.com/spring-beanfactory

Creating objects that implement JpaRepo interfaces via Autowiring in JUnit?

I'm tasked with writing JUnit classes. To do this, I need to create an object that implements an interface that extends org.springframework.data.jpa.repository.JpaRepository, (called "KanjiRepo") yet no classes in this application actually implement KanjiRepo.
Rather than with new, all objects that implement KanjiRepo seem to be created via #Autowiring.
The KanjiRepo interface is a member of a package that has only interfaces, and they all extend org.springframework.data.jpa.repository.JpaRepository.
[question] I don't think I understand the problem. Can someone tell me if what I just said sounds correct. In my junit, do I also use #Autowire to create objects that implement the interfaces in the same package as KanjiRepo?Maintaining 2 separate Application Contexts will be needed as a last step?
I think Autowiring KanjiRepo is enough in your test class unless you annotate your repository as #Repository.
#Repository
public interface KanjiRepo extends JpaRepository....
If you have #Repository annotation on top of KanjiRepo and if you annototated your main class or configuration class to auto scan beans; spring would handle creating and autowiring beans succesfully.
Please check for component scan
#SpringBootApplication
// or #ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And then, you can create your test class with the following code;
#Autowired
KanjiRepo repo;
#Test
public void testFindById() {
Kanji instance = repo.findById(0L);
assert.....
}
should be fine.
Can the application and JUnit share the same configuration files?
Actually, it is. But, if you do component scan, you don't have to worry that. Spring will handle where your bean is and it should autowire succesfully.
do I also use #Autowire to create objects that implement the interfaces in the same package as KanjiRepo?
Spring will do component scan and will find where your repository is, so, you don't have to worry that where you locate your repository.

How to create a JPA implementation with Spring that is not added to the Spring context?

I'd like to add a cache using Spring and annotations to a repository that uses Spring Data but I'd prefer not to add the cache annotations to the same interface that extends the JpaRepository interface to separate concerns.
I'd like to create a wrapper around the Spring JPA repository defined as follows:
#NoRepositoryBean
public interface MyRepository extends JpaRepository<MyEntity, Long>{
MyEntity findOneByMyAttribute(String myAttribute);
}
public interface MyRepositorySpring extends MyRepository {}
Spring by default creates an implementation and adds it to the beans context even without an #Repository annotation present.
My problem is that I would like to have full control of the beans in the context. I'd like to create a wrapper like this:
public class MyCachedRepository extends MyRepository {
private final MyRepository wrappedRepository;
#Cacheable
MyEntity findOneByMyAttribute(String myAttribute){
wrappedRepository.get(myAttribute);
}
}
where wrappedRepository is the implementation created by Spring Data and then create the configuration as follows:
#Configuration
public class MyConf {
#Bean
public MyRepository myRepository() {
return new MyCachedRepository(myJpaRepository());
}
private MyRepository myJpaRepository() {
//here I would have the code that generates the implementation
}
}
Like this I could always have my code injecting a myRepository bean and let the configuration decide whether the bean in the context is the wrapped repository or the jpa one as there would be only one at all times.
Is there any way to do this?

Spring boot - #Service class calling another #Service class

Is it fine to have a #Service annotated class calling another #Service annotated class? Or it is a bad practice?
Eg.:
#Service
public class MyService {
// ...
#Autowired
private MyOtherService myOtherService;
// ...
}
It is not any restriction calling a service from another one. Unless you make circular dependency between services.
Circular dependency : https://en.wikipedia.org/wiki/Circular_dependency
Circular dependency in spring : https://www.baeldung.com/circular-dependencies-in-spring
Its good practice since utility class are being ignored these days, approach getting motivated by horizontal scaling... Surely services got to interact with other.
No need to worry, its like one service manager needs services of another manager.
just only one should be dependent on other, not both.
#Service
public class MyService {
// ...
#Autowired
private MyOtherService myOtherService = new MyOtherService();
// ...}
Try this. It is worked for me. You can use the methods of a service in many sevice.

Spring Boot program structure for access to data objects

I have an spring boot app that has a mongo repository and is using spring data to connect to it. This means there's an "entity" class and then a "repository" class.
However, to actually use the repository I need spring to #Autowire my repo to a variable inside another class (we'll call it X) that might want to use it. But if the X class is itself not a spring bean, it's just a regular pojo created by "new X" somewhere then it can't make use of Autowiring and therefore cant use the repo.
This seems like a show stopper... No one could ever make use of a repo outside of some very specific situations like calling the repo directly from your RestController or whatever without any intervening logic. Yet I am sure people are using this.
So my question is how to structure code so that it can be used? Do I need to do a bunch of processing and then return back up to the controller to interact with the databases? Is there a way to create some kind of other "intermediate helper bean" to mediate the connection? What should that look like?
disclaimer: I am fairly new to spring
Perhaps I can post some excerpts that can clarify your situation.
//nothing spring specific
public class MyPojo {
//properties
}
in a different package:
#Repository
public class MyRepositoryImpl implements MyRepository {
//CRUD implementation or whatever
}
in a different package:
#Service
public class MyServiceImpl implements MyService {
#Autowired //constructor-injection
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
private final MyRepository myRepository;
public void myBusinessLogic() {
MyPojo pojo = new MyPojo(); //not dependent on Spring
myRepository.doSomething();
//place calls to X here as needed
}
}
And finally:
#Controller
public class MyController {
#Autowired
public MyController(MyService myService) {
this.myService = myService;
}
private final MyService myService;
#GetMapping("/myPage")
public String doIt() {
myService.myBusinessLogic();
return "myPage";
}
}
Where MyRepository and MyService are interfaces that would contain the contract for their respective implementations.
Single Responsibility Principle
A major point to note is that your POJO isn't going to "use the repo" as you mention in your question. It represents an entity and shouldn't care about any specific repository. And this seems related to your issue - a POJO shouldn't be making calls to a repository (your "X" class in this case). Seems like the design should be revisited if that is the case.
As you say, you can only autowire fields in objects that themselves are autowired. This is inherent to bean injection. You should annotate X with for instance #Component and inject it where you need it.

Categories

Resources