4

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

Hana
  • 824
  • 4
  • 14
  • 30
  • Could you please provide the configuration file – Prashant Nov 08 '17 at 05:04
  • I just updated it. Thanks – Hana Nov 08 '17 at 05:08
  • 1
    As I can guess, the container is not able to detect service. Can you please add `@ComponentScan()` in your configuration class to allow the `Service` package to be detected. Also add `@MongoRepositories()` to scan mongo repositories. – Prashant Nov 08 '17 at 05:21
  • Thank you for your suggestion. I updated the annotaion for SpringMongoConfig to @ComponentScan,@EnableMongoRepositories but it show the same error. – Hana Nov 08 '17 at 05:47
  • Can you please send in the complete stack trace – Prashant Nov 08 '17 at 05:57
  • Yes , here is it. Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ups.mongo.service.ReconInputService' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:348) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101) at ups.mongodb.App.main(App.java:48) – Hana Nov 08 '17 at 06:09
  • Could you provide your pom.xml please? – Danylo Zatorsky Nov 08 '17 at 08:52
  • @DanyloZatorsky, updated already. – Hana Nov 08 '17 at 08:57

5 Answers5

4

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
Danylo Zatorsky
  • 5,856
  • 2
  • 25
  • 49
2

Since you have annotated @Service on ReconInputServiceImpl so, please add ReconInputServiceImpl.class in main class

ReconInputService reconInputService = ctx.getBean(ReconInputServiceImpl.class);
Dipen Chawla
  • 331
  • 2
  • 10
0

You can annotate your ReconInputServiceImpl class as @Service("reconInputService").

You can then access it as follows.

ReconInputService reconInputService = ctx.getBean("reconInputService",ReconInputService.class);
amdg
  • 2,211
  • 1
  • 14
  • 25
  • After edit, it show new error: Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'reconInputService' available So I think i'm missing SCAN component as @Prashant mention as above. – Hana Nov 08 '17 at 07:14
  • he has correctly pointed out. But, the interface injection is not supported in spring. You will have to specify some service name – amdg Nov 08 '17 at 07:22
  • The point "the interface injection is not supported in spring". I don't think so, Spring acually support Dependency Injection throught Invertion of Control. I implemented it in others project that connect to MySQL but using XML configuration. – Hana Nov 08 '17 at 07:28
  • It seem I confused the point "the interface injection is not supported in spring" :) Thanks – Hana Nov 08 '17 at 07:44
  • If there are more than one classes which are implementing `ReconInputService` interface, spring won't be able to identify the bean that should be injected. In that case, some qualifier name should be given so that spring can identify the correct implementing bean. – amdg Nov 08 '17 at 08:24
  • In this case, I just implement one time for ReconInputService interface only. – Hana Nov 08 '17 at 08:28
0
    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)
Tran Thien Chien
  • 643
  • 2
  • 6
  • 17
0

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>
Tenzin Chemi
  • 5,101
  • 2
  • 27
  • 33