I have some project trying to build a microservices architecture. Some of the projects are used producing jar files (I refer these projects as "library projects".) for the callable microservices. One of these microservices are responsible for user/customer registration and lookup. All the data is persisted in a Cassandra cluster. One of the library projects are responsible for providing various Cassandra based services/value objects, etc...
In the microservice pom file, I include the jar produced by the library
project. It compiles, but when I start the customer service the classes from that jar are not found.
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.besztercekk.tao.customer.handler.PostUserHandler required a bean of type 'com.besztercekk.tao.cassandra.repository.ReactiveUserRepository' 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.besztercekk.tao.cassandra.repository.ReactiveUserRepository' in your configuration.
Here are some code snippets, but I can attach anything else you need.
This is the pom file for the microservice. The class what is not found is in this dependency: tao-elszamolas-cassandra.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tao-elszamolas-customer-service</artifactId>
<packaging>jar</packaging>
<name>tao-elszamolas-customer-service</name>
<description>TAO elszamolas customer backend service</description>
<parent>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<!-- Spring Cloud Eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring Cloud config client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<!-- Webflux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Reactive cassandra -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
<!-- TAO elszamolas dependencies -->
<dependency>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-cassandra</artifactId>
</dependency>
<dependency>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-jwt</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
This is the pom file for the project what contains the missing class:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tao-elszamolas-cassandra</artifactId>
<packaging>jar</packaging>
<name>tao-elszamolas-cassandra</name>
<description>TAO elszámolás Spring alapú több projektben használt komponensek gyűjtő projektje</description>
<parent>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<!-- Reactive cassandra -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
</dependencies>
</project>
Here the class where the error is coming from:
#Component
#Validator(UserDataValidator.class)
public class PostUserHandler extends AbstractValidationHandler<User, ChainableValidator> {
#Autowired
private ReactiveUserRepository repository;
#Autowired
private PasswordEncoder passwordEncoder;
public PostUserHandler() {
super(User.class);
}
#Override
protected Mono<ServerResponse> processRequest(User body, ServerRequest request) {
return repository.save(enrichUser(body, UUIDs.random().toString()))
.flatMap(updatedUser -> {
sendRegistrationCode();
return ok(updatedUser);
})
.onErrorResume(IllegalArgumentException.class, e -> ServerResponse.badRequest().build())
.switchIfEmpty(ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
}
#Override
protected Mono<User> extractContent(ServerRequest request) {
return request.bodyToMono(User.class);
}
private void sendRegistrationCode() {
//TODO: Send Kafka message to email component
}
private Mono<ServerResponse> ok(User c) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromPublisher(Mono.just(c), User.class));
}
private User enrichUser(User user, String activationCode) {
return User.builder()
.authorities(user.getAuthorities())
.email(user.getEmail())
.enabled(user.isEnabled())
.firstName(user.getFirstName())
.id(UUIDs.timeBased())
.lastName(user.getLastName())
.middleName(user.getMiddleName())
.password(passwordEncoder.encode(user.getPassword()))
.registrationTime(LocalDateTime.now())
.passwordExpiryTime(LocalDateTime.now())
.roles(user.getRoles())
.username(user.getUsername())
.accountExpired(false)
.accountLocked(false)
.activationCode(activationCode)
.build();
}
}
Any help would be appreciated!
UPDATE (SOME EXTRA INFO ADDED):
Here is the main class containing the component scan.
#SpringBootApplication
#EnableEurekaClient
#ComponentScan(basePackages = {"com.besztercekk.tao.customer", "com.besztercekk.tao.cassandra"})
public class CustomerBackendApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerBackendApplication.class, args);
}
}
Here is the repository class:
package com.besztercekk.tao.cassandra.repository;
import java.util.UUID;
import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;
import org.springframework.stereotype.Component;
import com.besztercekk.tao.cassandra.model.User;
import reactor.core.publisher.Mono;
/**
* This interface provides reactive {#link User} services.
*/
public interface ReactiveUserRepository extends ReactiveCassandraRepository<User, UUID> {
Mono<User> findByUsername(String username);
Mono<User> findByEmail(String email);
}
Also tried to add #Component and #Repository annotations to this interface, just to see its effect. :-)
My other idea was add the bean programatically to the context after restart. But this is just an interface, so not sure if it works out at all.
The third idea was wrap this interface into a service class and that is easy to add programatically.
But I really want it to be added automatically, without writing code.
Try adding the following annotation to any of the #Configuration classes:
#EnableReactiveCassandraRepositories
#Configuration
class MyConfiguration() {
//code
}
The above code enables spring to automatically scan and create the default implementation bean for your interface ReactiveUserRepository .
Also annotate the interface like :
#Repository
public interface ReactiveUserRepository extends ReactiveCassandraRepository<User, UUID> {
Mono<User> findByUsername(String username);
Mono<User> findByEmail(String email);
}
For more details read :
https://dzone.com/articles/reactive-streams-with-spring-data-cassandra
Also, if you would like more details on the annotations read a similar post :
how annotation #Repository in java spring work?
Related
So I was running a Springboot application then I got this error
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'sample.ArticledDao' that could not be found.
Action:
Consider defining a bean of type 'sample.ArticledDao' in your configuration.
Then I checked how SpringBoot scans the files and I saw some of the files are ignored
This is how the path looks ( It's not ideal I know) :
This is the code of my Main class :
#SpringBootApplication(scanBasePackages="sample")
#EnableJpaRepositories("sample")
public class Main {
public static void main(String[] args) throws Exception {
SpringApplication.run(Main.class, args);
}
}
This is my ArticlesDao class:
public interface ArticledDao {
List<Articles> findAll();
public void deleteArticles(Articles articles);
}
ArticleSercviceImp class
#Component
public class ArticleSericeImp implements ArticlesServices
{
#Autowired
ArticledDao articleDao;
#Override
public List<Articles> findAll() {
return articleDao.findAll();
}
#Override
public void deleteEmployee(Articles art) {
articleDao.deleteArticles(art);
}
ArticleRepository:
#Repository
public class ArticlesRepository implements ArticledDao{
public ArticlesRepository(NamedParameterJdbcTemplate template) {
this.template = template;
}
NamedParameterJdbcTemplate template;
#Override
public List<Articles> findAll() {
return template.query("select * from article", new ArticlesRowMapper());
}
#Override
public void deleteArticles(Articles articles) {
final String sql = "delete from article where articleid=:articleid";
Map<String,Object> map=new HashMap<String,Object>();
map.put("articleid", articles.getId());
template.execute(sql,map,new PreparedStatementCallback<Object>() {
#Override
public Object doInPreparedStatement(PreparedStatement ps)
throws SQLException, DataAccessException {
return ps.executeUpdate();
}
});
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sprinboot_project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Sprinboot_Project</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.29.Final</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
I'm new to SpringBoot so I would be very grateful for your help.
I have added the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
and removed the spring-data dependency as well (this will bring in all needed transitive dependencies like hibernate etc.).
Then you have excluded the datasource autoconfiguration in your application.properties:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
However this auto configuration is needed to autoconfigure the JdbcTemplate, which you use in your ArticleRepository class. So you need to remove that line. With those changes i can get at least to the point where it tries to connect to Postgres (which fails in my case because i don't have a running instance).
The chances are that you're trying to inject the interface into some component, probably some service implementation that looks like this:
#Service
public class ArticleServiceImpl implements ArticeService {
#Autowired
private ArticleDao dao;
....
}
// or alternatively if you're using constructor injection:
#Service
public class ArticleServiceImpl implements ArticeService {
private final ArticleDao dao;
public ArticleServiceImpl(ArticleDao dao) {
this.dao = dao;
}
}
In order to instantiate this service bean, spring needs to resolve its dependencies, which is, in this case, a single dependency of DAO expressed as an interface. So there has to be some real instance that implements that interface ArticleDao.
It seems like you haven't even created such a class, let alone you have to specify it as a bean as well. So spring merely can't find a proper match for injection, hence an error.
To solve this error you should have something like this:
#Repository
public class ArticleDaoImpl implements ArticleDao {
// implement the methods of the interface
}
You might also be using spring data , but its a different story, I assume, the question is about the general spring injection principles...
A component required a bean of type 'sample.ArticledDao' that could not be found.
Spring IoC is trying to make sense of your Bean injections, but Spring cannot find a Bean of type matching sample.ArticledDao. Notice that in your code,
#Repository
public class ArticlesRepository implements ArticledDao
Based on your definition, the ArticledDao bean is not of the ArticledDao type, so it throws an error on
#Component
public class ArticleSericeImp implements ArticlesServices
{
#Autowired
ArticledDao articleDao;
// wrong.
In fact, the ArticledDao bean is of the ArticlesRepository type. Thus, in the consumers, you can autowire either
1.
#Component
public class ArticleSericeImp implements ArticlesServices
{
#Autowired
ArticledDao articlesRepository;
// ...
or,
2.
#Component
public class ArticleSericeImp implements ArticlesServices
{
#Autowired
ArticlesRepository articlesRepository;
// ...
*Ehcache3 not working with spring boot - I tried out with approach given below. Spring boot never caches the value mentioned in the component.It is getting called n - no of times no matter the cache is enable or not. In the logs it shows cache is added to cache manager but thats not the case here
ehcache.xml
<ehcache:config>
<ehcache:cache-template name="myDefaultTemplate">
<ehcache:expiry>
<ehcache:none/>
</ehcache:expiry>
</ehcache:cache-template>
<ehcache:cache alias="customer" uses-template="myDefaultTemplate">
<ehcache:key-type>java.lang.Long</ehcache:key-type>
<ehcache:value-type>com.controller.Customer</ehcache:value-type>
<ehcache:expiry>
<ehcache:tti unit="seconds">30</ehcache:tti>
</ehcache:expiry>
<ehcache:heap unit="entries">200</ehcache:heap>
</ehcache:cache>
</ehcache:config>
In my pom.xml i have the following configurations -
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.6.2</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application.java which starts spring boot app
#SpringBootApplication
#ComponentScan(basePackages = {"com.service"})
#EnableCaching
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Component class for caching -
#Component
public class CustomerService {
#Cacheable(cacheNames = "customer",key="#id")
public Customer getCustomer(final Long id){
System.out.println("Returning customer information for customer id
{}
"+id);
Customer customer = new Customer();
customer.setCustomerId(id);
customer.setFirstName("Test");
customer.setEmail("contact-us#test.com");
return customer;
}
}
I tried with couple of approaches by adding component scan in the application
but didn't worked out.
Spring boot starts and it shows cache has been added to cache manager.
I got it working by changing from #Component to #Service. I don't understand why caching is not working under component and works in service layer
If you want to add Caching annotation at the Repository layer then just remove #Repository Annotation, If you want to add Caching at Service Layer then use #Service Annotation instead of #Component Annotation.
I'm going through the JPA starter tutorial for spring boot am struggling.
I know the question has been asked sometimes here ('Field required a bean of type that could not be found.' error spring restful API using mongodb)
But those problems are a bit different from what I have.
Structure
java
|
helloWorld
|
web/ -- HelloWorldController
Application
Customer
CustomerRepository
ServletInitializer
As you can see all my packages related to JPA are on the same level as my Application file . According to the tutorial (https://spring.io/guides/gs/accessing-data-jpa/) this should work
My Application class
package helloWorld;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
CustomerRepository customerRepository;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
CustomerRepository
package helloWorld;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
When trying to use #Autowired I receive
***************************
APPLICATION FAILED TO START
***************************
Description:
Field customerRepository in helloWorld.Application required a bean of type 'helloWorld.CustomerRepository' that could not be found.
Action:
Consider defining a bean of type 'helloWorld.CustomerRepository' in your configuration.
Also, adding scanBasePackages={"helloWorld"}) to #SpringBootApplication does not help and from what I read it should also not be needed.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>helloWorld.com.example</groupId>
<artifactId>helloWorld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>fireCommerce</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-webapp-maven-plugin</artifactId>
<version>1.1.0</version>
<configuration>
<resourceGroup>maven-projects</resourceGroup>
<appName>${project.artifactId}-${maven.build.timestamp}</appName>
<region>westus</region>
<javaVersion>1.8</javaVersion>
<deploymentType>war</deploymentType>
</configuration>
</plugin>
</plugins>
</build>
</project>
link to the github project
You are excluding the autoconfiguration of JPA repositories. Remove the line from application.properties to let Spring make CustomerRepository a bean and configure it.
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
In my case, I just forgot to add #Component to Impl class of interface!
These errors may be due to absence of one or more stereotype annotations.
I changed this Class Level Annotation from my Application Class.
From:
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
To:
#SpringBootApplication
Try Adding the #ComponentScan("package.path.to.your.repository") annotation above Application class.
- Just make sure your repository is in the the same package path that is written in #ComponentScan
#SpringBootApplication
#ComponentScan("helloworld")
public class Application extends SpringBootServletInitializer {
//your stuff
}
I had the same issue and everything was good except adding the below dependecies.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.4.2</version>
And, It worked for me
you need to add #Repository in repository
I'm trying to structure a project to connect to MongoDB using Spring Data as below:
SpringMongoConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import com.mongodb.MongoClient;
#Configuration
public class SpringMongoConfig {
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient("127.0.0.1"), "ReconInput");
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
ReconInputRepository.java:
#Repository
public interface ReconInputRepository extends MongoRepository<ReconInput, String> {
public List<ReconInput> findByReportingDate(String reportingDate);
}
ReconInputService.java
public interface ReconInputService {
public List<ReconInput> getInputByReportingDate(String reportingDate);
}
ReconInputServiceImpl.java
#Service
public class ReconInputServiceImpl implements ReconInputService {
#Autowired
private ReconInputRepository reconInputRepository;
public List<ReconInput> getInputByReportingDate(String reportingDate) {
return reconInputRepository.findByReportingDate(reportingDate);
}
}
App.java
public class App
{
public static void main( String[] args )
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
ReconInputService reconInputService = ctx.getBean(ReconInputService.class);
List<ReconInput> inputData = reconInputService.getInputByReportingDate("2017 Nov 20");
System.out.println(inputData.get(0).getReportId());
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ups.mongodb</groupId>
<artifactId>MongoConnection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MongoConnection</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.0.1.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-mongodb -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
</dependencies>
</project>
When I run the project, it throw an exception:
No qualifying bean of type 'ups.mongo.service.ReconInputService'
available.
Please help me any suggestion for this error. Thank you !
Update 1
Added #ComponentScan(basePackages = "ups.mongo") to SpringMongoConfig.java.
Then I got new issue:
Error creating bean with name 'ReconInputService': Unsatisfied dependency expressed through field 'reconInputRepository';
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ups.mongo.repository.ReconInputRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Update 2
Instead of using Spring Data MongoRepository. I replaced ReconInputRepository.java that extends from Spring data by implement it by myself as below :
ReconInputRepository.java
public interface ReconInputRepository {
public List<ReconInput> findByReportingDate(String reportingDate);
}
ReconInputRepositoryImpl.java
#Repository
public class ReconInputRepositoryImpl implements ReconInputRepository {
#Autowired
MongoTemplate mongoTemplate;
public List<ReconInput> findByReportingDate(String reportingDate) {
List<ReconInput> reconInputList = null;
Query searchUserQuery = new Query(Criteria.where("reportingDate").is(reportingDate));
reconInputList = mongoTemplate.find(searchUserQuery, ReconInput.class);
return reconInputList;
}
}
Then it work correctly.
My Summary
The issue may come from Spring does not support inject interface - as #amdg suggest (but work in spring boot - I have no idea why, if someone know that please leave me some comment).
Reference: Spring interface injection example
Update 3
At last, I found the most simple way to make it correctly.
All I need to do is adding #EnableMongoRepositories({ "ups.mongo.repository" }) to the SpringMongoConfig.java
As I suspected, you mix plain old Spring with Spring Boot and want to get Spring Boot effect.
In order to use Spring Boot you should update your dependencies to use Spring Boot Starters.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ups.mongodb</groupId>
<artifactId>MongoConnection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MongoConnection</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Then just add your config in the root package of your app (presumably it's ups.mongo):
#SpringBootApplication
#EnableMongoRepositories
public class App
{
public static void main( String[] args )
{
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
ReconInputService reconInputService = ctx.getBean(ReconInputService.class);
List<ReconInput> inputData = reconInputService.getInputByReportingDate("2017 Nov 20");
System.out.println(inputData.get(0).getReportId());
}
}
In this case, you do not even need SpringMongoConfig.class. Instead, add the following config in your application.properties:
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.database=demo
Since you have annotated #Service on ReconInputServiceImpl so,
please add ReconInputServiceImpl.class in main class
ReconInputService reconInputService = ctx.getBean(ReconInputServiceImpl.class);
You can annotate your ReconInputServiceImpl class as #Service("reconInputService").
You can then access it as follows.
ReconInputService reconInputService = ctx.getBean("reconInputService",ReconInputService.class);
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
If you use above constructor, DI container will only load bean definitions from SpringMongoConfig class.
If You want DI container to scan all bean definitions recursively for a particular base package please use this constructor instead
AnnotationConfigApplicationContext(java.lang.String... basePackages)
I have previously added only spring-data-mongodb package which kept me busying debugging for "No qualifying bean for some repository".
After adding spring-boot-starter-data-mongodb, everything is handled by spring.
application.yaml
spring:
data:
mongodb:
uri: mongodb://username:password#localhost:27017
database: test
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
I am learning Spring Framework and now I am trying to make a simple Spring Boot application that would list all entries from a database (using Hibernate).
First I had a problem when SessionFactory would not be defined, but I managed to define it in Config Class. However, when I try to run the app now, I get the following error:
Description:
The dependencies of some of the beans in the application context form a cycle:
indexController (field private com.prvi.dao.CustomerDAO com.prvi.controllers.IndexController.customerDAO)
↓
customerDAOImpl (field private org.hibernate.SessionFactory com.prvi.dao.CustomerDAOImpl.sessionFactory)
┌─────┐
| sessionFactory defined in class path resource [com/prvi/ConfigPrvi.class]
└─────┘
Basically, I have IndexController, who gets GET / request, then it calls customerDAO to get list of customers and customerDAO uses sessionFactory to get Session and performs a query on DB. (I have omitted Service layer from the app for sake of simplicity)
Now, I have read that this error happens when a bean is dependent on a bean that is dependent on a first bean, making cyclical dependency. However, I do not understand where I made this cycle and how to fix it. Also, other answers on this topic have not provided me enough information to correct the error. They were mostly oversimplifies where cycle is clear, which is not the case here.
Here is what I have tried so far:
PrviApplication.java - entry point for Spring Boot
#SpringBootApplication
public class PrviApplication {
public static void main(String[] args) {
SpringApplication.run(PrviApplication.class, args);
}
}
ConfigPrvi.java - My Configuration file
#Configuration
#EnableAutoConfiguration
public class ConfigPrvi {
#Bean
public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory emf){
HibernateJpaSessionFactoryBean factory = new HibernateJpaSessionFactoryBean();
factory.setEntityManagerFactory(emf);
return factory;
}
}
IndexController.java - my Controller class, which handles GET /
#Controller
#RequestMapping("/")
public class IndexController {
#Autowired
private CustomerDAO customerDAO;
#GetMapping("/")
public String listCustomers(Model model){
model.addAttribute("customers", customerDAO.getAllCustomers());
return "index";
}
}
CustomerDAO.java - just an interface
public interface CustomerDAO {
public List<Customer> getAllCustomers();
}
CustomerDAOImpl.java - implementation, retrieves data from database
#Repository
public class CustomerDAOImpl implements CustomerDAO {
#Autowired
private SessionFactory sessionFactory;
#Transactional
public List<Customer> getAllCustomers(){
Session session = sessionFactory.getCurrentSession();
Query<Customer> query = session.createQuery("from Customer order by lastName", Customer.class);
List<Customer> customers = query.getResultList();
return customers;
}
}
Is it perhaps that cycle was made here:
main --> controller --> dao --> sessionFactory --> config --> main
If so, how can I rewrite the code so that I get rid of it?
EDIT: Added pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.prvi</groupId>
<artifactId>prvi</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>prvi</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>