Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing MongoTemplate to Custom Repository implementation

Project is configured to used multiple MongoTemplate s

Mongo Ref is passed as

@EnableMongoRepositories(basePackages={"com.mypackage.one"}, mongoTemplateRef="mongoTemplateOne")

for repositories in the package com.mypackage.one

and

@EnableMongoRepositories(basePackages={"com.mypackage.two"}, mongoTemplateRef="mongoTemplateTwo")

for repositories in the package com.mypackage.two

For standard repositories it works fine. But for the scenarios, where I need custom behavior, I define say myRepoCustomImpl with my custom behavior needs.

Problem: I need to have access to the MongoTemplate which the the analogous standard Repository.

e.g. If MyRepo is extending MyRepoCustom interface as

@Repository
interface MyRepo extends MongoRepository<MyEntity, String>, MyRepoCustom{}

MyRepoCustomImpl

@Service
    public class MyRepoCustomImpl implements MyRepoCustom{
        @Autowired
        @Qualifier("mongoTemplateOne")
        MongoTemplate mongoTmpl;

        @Override
        MyEntity myCustomNeedFunc(String arg){
            // MyImplemenation goes here
        }

}

If MyRepo is in package com.mypackage.one, the mongoTemplateOne will be used by myRepo, so there should be a some way to MyRepoCustomImpl will know that it should also use mongoTemplateOne, whenever I will make change in the mongoTemplateRef for MyRepo, say as

@EnableMongoRepositories(basePackages={"com.mypackage.one"}, mongoTemplateRef="mongoTemplateThree")

now I need to make changes to @Qualifier in the MyRepoCustomImpl! There are lots of repos with custom behaviour, so its becoming tedious task.

Question: Instead isn't there any way that the MongoTemplate to be used should get automatically injected or resolved according to the Repo it is extending to?

like image 670
Arjun Avatar asked Jul 21 '17 12:07

Arjun


People also ask

Which is better MongoRepository or MongoTemplate?

MongoTemplate is a bit more lower level where you need to write your own queries. With embedded documents and denormalization it can be easier to write complex queries with MongoTemplate. For simple things I would use MongoRepository. I've seen some examples where both are used together in a hybrid approach.

What is the difference between MongoOperations and MongoTemplate?

MongoTemplate provides a simple way for you to save, update, and delete your domain objects and map those objects to documents stored in MongoDB. You can save, update and delete the object as shown below. MongoOperations is the interface that MongoTemplate implements.

Can we use MongoTemplate and MongoRepository?

We can even utilize both of them in our programming practice as per our need and for performance enhancements. Moreover, MongoTemplate can offer an easier step to write a complex query than MongoRepository. Even some people in the industry also find that MongoTemplate is a better choice to write a complex query easily.

What is use of MongoTemplate in spring boot?

MongoDB and Spring Boot interact using the MongoTemplate class and MongoRepository interface. MongoTemplate — MongoTemplate implements a set of ready-to-use APIs. A good choice for operations like update, aggregations, and others, MongoTemplate offers finer control over custom queries.


3 Answers

MongoTemplate isn't exposed by the MongoRepository interface. They could potentially expose the name of the MongoTemplate @Bean and that could provide a solution to your question. However, given the fact that they don't, I will provide an example below that may suit your needs.

First off mongoTemplateRef refers to the name of the @Bean to use, it doesn't specify the name of the MongoTemplate.

You will need to provide each MongoTemplate @Bean and then refer to it within your @EnableMongoRepositories annotation.

Since you are using spring-boot you can take advantage of the MongoDataAutoConfiguration class. Please take a look at what it does here https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java.

The simplest example is this.

package: com.xyz.repo (this implementation relies on the configuration provided by MongoDataAutoConfiguration)

@Configuration
@EnableMongoRepositories(basePackages={"com.xyz.repo"}) //mongoTemplateRef defaults to mongoTemplate
public class XyzRepoConfiguration {

}

public abstract class BaseRepo {
    @Autowired
    MongoTemplate mongoTemplate;
}

@Service
public class MyRepoCustomImpl extends BaseRepo implements MyRepoCustom {    
    @Override
    MyEntity myCustomNeedFunc(String arg){
        // access to this.mongoTemplate is present
    }
}

package: com.abc.repo

@Configuration
@EnableMongoRepositories(basePackages={"com.abc.repo"}, mongoTemplateRef=AbcRepConfiguration.TEMPLATE_NAME)
public class AbcRepoConfiguration {
    public static final String TEMPLATE_NAME = "mongoTemplateTwo";

    @Bean(name="mongoPropertiesTwo")
    @ConfigurationProperties(prefix="spring.data.mongodb2")
    public MongoProperties mongoProperties() {
        return new MongoProperties();
    }

    @Bean(name="mongoDbFactoryTwo")
    public SimpleMongoDbFactory mongoDbFactory(MongoClient mongo, @Qualifier("mongoPropertiesTwo") MongoProperties mongoProperties) throws Exception {
        String database = this.mongoProperties.getMongoClientDatabase();
        return new SimpleMongoDbFactory(mongo, database);
    }

    @Bean(name=AbcRepoConfiguration.TEMPLATE_NAME)
    public MongoTemplate mongoTemplate(@Qualifier("mongoDbFactoryTwo") MongoDbFactory mongoDbFactory, MongoConverter converter) throws UnknownHostException {
        return new MongoTemplate(mongoDbFactory, converter);
    }
}

public abstract class BaseRepo {
    @Autowired
    @Qualifier(AbcRepoConfiguration.TEMPLATE_NAME)
    MongoTemplate mongoTemplate;
}

@Service
public class MyRepoCustomImpl extends BaseRepo implements MyRepoCustom {    
    @Override
    MyEntity myCustomNeedFunc(String arg){
        // access to this.mongoTemplate is present
    }
}

com.xyz.repo will rely on spring.data.mongodb properties within application.properties com.abc.repo will rely on spring.data.mongodb2 properties within application.properties

I haven't used the AbcRepoConfiguration.TEMPLATE_NAME approach before, but it was compiling within my IDE.

Please let me know if you need any clarification.

like image 160
ryan2049 Avatar answered Oct 21 '22 23:10

ryan2049


MongoTemplate is not injected on your repository class but deeper in spring-data-mongodb so you cannot get it from your repository. Look at the code you will learn a lot of stuff.

So no you can't inject a bean according to the repo extend except if you disable spring-boot auto-configuration and component discovery and configure it by your self but it will be much longer than just changing @Qualifier name. Your IDE call help you easily and you might regret disable auto-configuration.

Sorry for the disappointment.

like image 20
Martin Choraine Avatar answered Oct 22 '22 00:10

Martin Choraine


You can use following example.

1)

package com.johnathanmarksmith.mongodb.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * Date:   6/28/13 / 10:40 AM
 * Author: Johnathan Mark Smith
 * Email:  [email protected]
 * <p/>
 * Comments:
 *  This main really does not have to be here but I just wanted to add something for the demo..
 *
 */


public class MongoDBApp {

    static final Logger logger = LoggerFactory.getLogger(MongoDBApp.class);

    public static void main(String[] args) {
        logger.info("Fongo Demo application");

        ApplicationContext context = new AnnotationConfigApplicationContext(MongoConfiguration.class);




        logger.info("Fongo Demo application");
    }
}

2)

package com.johnathanmarksmith.mongodb.example;

import com.mongodb.Mongo;
import com.mongodb.ServerAddress;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

import java.util.ArrayList;


/**
 * Date:   5/24/13 / 8:05 AM
 * Author: Johnathan Mark Smith
 * Email:  [email protected]
 * <p/>
 * Comments:
 * <p/>
 * This is a example on how to setup a database with Spring's Java Configuration (JavaConfig) style.
 * <p/>
 * As you can see from the code below this is easy and a lot better then using the old style of XML files.
 * <p/>
 * T
 */

@Configuration
@EnableMongoRepositories
@ComponentScan(basePackageClasses = {MongoDBApp.class})
@PropertySource("classpath:application.properties")
public class MongoConfiguration extends AbstractMongoConfiguration {


    @Override
    protected String getDatabaseName() {
        return "demo";
    }



    @Override
    public Mongo mongo() throws Exception {
        /**
         *
         * this is for a single db
         */

        // return new Mongo();


        /**
         *
         * This is for a relset of db's
         */

        return new Mongo(new ArrayList<ServerAddress>() {{ add(new ServerAddress("127.0.0.1", 27017)); add(new ServerAddress("127.0.0.1", 27027)); add(new ServerAddress("127.0.0.1", 27037)); }});

    }

    @Override
    protected String getMappingBasePackage() {
        return "com.johnathanmarksmith.mongodb.example.domain";
    }

}

3)

package com.johnathanmarksmith.mongodb.example.repository;


import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;

import com.johnathanmarksmith.mongodb.example.domain.Person;


/**
 * Date:   6/26/13 / 1:22 PM
 * Author: Johnathan Mark Smith
 * Email:  [email protected]
 * <p/>
 * Comments:
 * <p/>
 * This is my Person Repository
 */


@Repository
public class PersonRepository {

    static final Logger logger = LoggerFactory.getLogger(PersonRepository.class);

    @Autowired
    MongoTemplate mongoTemplate;

    public long countUnderAge() {
        List<Person> results = null;

        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria = criteria.and("age").lte(21);

        query.addCriteria(criteria);
        //results = mongoTemplate.find(query, Person.class);
        long count = this.mongoTemplate.count(query, Person.class);

        logger.info("Total number of under age in database: {}", count);
        return count;
    }

    /**
     * This will count how many Person Objects I have
     */
    public long countAllPersons() {
        // findAll().size() approach is very inefficient, since it returns the whole documents
        // List<Person> results = mongoTemplate.findAll(Person.class);

        long total = this.mongoTemplate.count(null, Person.class);
        logger.info("Total number in database: {}", total);

        return total;
    }

    /**
     * This will install a new Person object with my
     * name and random age
     */
    public void insertPersonWithNameJohnathan(double age) {
        Person p = new Person("Johnathan", (int) age);

        mongoTemplate.insert(p);
    }

    /**
     * this will create a {@link Person} collection if the collection does not already exists
     */
    public void createPersonCollection() {
        if (!mongoTemplate.collectionExists(Person.class)) {
            mongoTemplate.createCollection(Person.class);
        }
    }

    /**
     * this will drop the {@link Person} collection if the collection does already exists
     */
    public void dropPersonCollection() {
        if (mongoTemplate.collectionExists(Person.class)) {
            mongoTemplate.dropCollection(Person.class);
        }
    }
}

4)

package com.johnathanmarksmith.mongodb.example.domain;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
 * Date:   6/26/13 / 1:21 PM
 * Author: Johnathan Mark Smith
 * Email:  [email protected]
 * <p/>
 * Comments:
 * <p/>
 * This is a Person object that I am going to be using for my demo
 */


@Document
public class Person {

    @Id
    private String personId;

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getPersonId() {
        return personId;
    }

    public void setPersonId(final String personId) {
        this.personId = personId;
    }

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(final int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [id=" + personId + ", name=" + name
                + ", age=" + age + "]";
    }

}

https://github.com/JohnathanMarkSmith/spring-fongo-demo

like image 1
vaquar khan Avatar answered Oct 22 '22 01:10

vaquar khan