Skip to content

Instantly share code, notes, and snippets.

@gmarti28
Created February 10, 2019 22:19
Show Gist options
  • Save gmarti28/d6b4b871d8ce886cdb0ba87ca2099bd8 to your computer and use it in GitHub Desktop.
Save gmarti28/d6b4b871d8ce886cdb0ba87ca2099bd8 to your computer and use it in GitHub Desktop.
Answer to StackOverflow
As per my current knowledge of Spring, Spring-data and Spring-data-mongodb I've ended up whith this solution:
----------
**config/MultipleMongoProperties.java**
```
package com.mycompany.myapp.config;
import lombok.Data;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "database.mongodb")
public class MultipleMongoProperties {
private MongoProperties orange = new MongoProperties();
private MongoProperties banana = new MongoProperties();
private MongoProperties peach = new MongoProperties();
}
```
**config/MultipleMongoConfig.java**
```
package com.mycompany.myapp.config;
import com.mongodb.*;
import lombok.RequiredArgsConstructor;
import org.apache.catalina.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import static com.mongodb.WriteConcern.ACKNOWLEDGED;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(MultipleMongoProperties.class)
public class MultipleMongoConfig {
private final MultipleMongoProperties mongoProperties;
private static final Logger LOGGER = LoggerFactory.getLogger(MultipleMongoConfig.class);
@Primary
@Bean(name = "orangeMongoTemplate")
public MongoTemplate orangeMongoTemplate() {
return new MongoTemplate(orangeFactory(this.mongoProperties.getOrange()));
}
@Bean(name = "bananaMongoTemplate")
public MongoTemplate bananaMongoTemplate() {
return new MongoTemplate(bananaFactory(this.mongoProperties.getBanana()));
}
@Bean(name = "peachMongoTemplate")
public MongoTemplate peachMongoTemplate() {
return new MongoTemplate(peachFactory(this.mongoProperties.getPeach()));
}
@Bean
@Primary
public MongoDbFactory orangeFactory(final MongoProperties mongo) {
return getFactory(mongo);
}
@Bean
public MongoDbFactory bananaFactory(final MongoProperties mongo) {
return getFactory(mongo);
}
@Bean
public MongoDbFactory peachFactory(final MongoProperties mongo) {
return getFactory(mongo);
}
public SimpleMongoDbFactory getFactory(MongoProperties mongo){
MongoClientOptions options = MongoClientOptions.builder()
.connectTimeout(5000)
.socketTimeout(8000)
.readPreference(ReadPreference.secondaryPreferred())
.writeConcern(ACKNOWLEDGED)
.build();
MongoCredential credential;
MongoClient client;
Optional<String> username= Optional.ofNullable(mongo.getUsername());
Optional<char[]> password = Optional.ofNullable(mongo.getPassword());
Optional<String> authDbName = Optional.ofNullable(mongo.getAuthenticationDatabase());
Optional<String> database = Optional.ofNullable(mongo.getDatabase());
Optional<String> uri = Optional.ofNullable(mongo.getUri());
Optional<String> host = Optional.ofNullable(mongo.getHost());
Optional<Integer> port = Optional.ofNullable(mongo.getPort());
if (! database.isPresent()) {
String msg = format("database parameter is required and was not specified. {%s}", mongo);
LOGGER.error(msg);
throw new RuntimeException(msg);
}
if (uri.isPresent()) {
String url = uri.get();
LOGGER.info("URI IS PRESENT. " + url);
LOGGER.info(format("URI specified [%s]. This will take precedence over host and port settings.", url));
Optional<String> authString=Optional.empty();
if (username.isPresent() && password.isPresent()) {
authString = Optional.of(format("%s:%s", username.get(), new String(password.get())));
}
StringBuilder sb = new StringBuilder();
if ( ! url.matches("^mongodb://")) sb.append("mongodb://");
if ( ! url.contains("@") && authString.isPresent()){
sb.append(authString.get()).append("@");
}
sb.append(url);
if ( ! url.contains("/") && authDbName.isPresent()){
sb.append("/").append(authDbName.get());
}
LOGGER.info(format("URI resolved to: %s",sb.toString()));
client = new MongoClient(new MongoClientURI(sb.toString()));
return new SimpleMongoDbFactory(client, database.get());
}
ServerAddress a = new ServerAddress(host.orElse("localhost"), port.orElse(27017));
LOGGER.debug(format("Address resolved to %s",a));
if ( ! username.isPresent() || ! password.isPresent()) {
String msg = format("Some of the auth parameters were not specified: {%s} will try connection without authentication", mongo);
LOGGER.info(msg);
client = new MongoClient(singletonList(a),options);
return new SimpleMongoDbFactory(client,database.get());
}
MongoCredential cred = MongoCredential.createCredential(username.get(), authDbName.get(),password.get());
client = new MongoClient(singletonList(a),cred,options);
return new SimpleMongoDbFactory(client, database.get());
}
private static int sizeOfValue(String value){
if (value == null) return 0;
return value.length();
}
private static boolean valueIsMissing(String value){
return sizeOfValue(value) == 0;
}
private static boolean valueIsPresent(String value){
return ! valueIsMissing(value);
}
private static boolean valueIsPresent(char[] charSequence){
return valueIsPresent(new String(charSequence));
}
}
```
**config/CustomerOrangeMongoConfig.java**
```
package com.mycompany.myapp.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Configuration
@EnableMongoRepositories(basePackages = "com.mycompany.myapp.model.orange",
mongoTemplateRef = "orangeMongoTemplate")
public class CustomerOrangeMongoConfig {
}
```
**config/CustomerBananaMongoConfig**
```
package com.mycompany.myapp.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Configuration
@EnableMongoRepositories(basePackages = "com.mycompany.myapp.model.banana",
mongoTemplateRef = "bananaMongoTemplate")
public class CustomerBananaMongoConfig {
}
```
**config/PeachMongoConfig**
```
package com.mycompany.myapp.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Configuration
@EnableMongoRepositories(basePackages = "com.mycompany.myapp.model.peach",
mongoTemplateRef = "peachMongoTemplate")
public class AlerterMongoConfig {
}
```
**model/Customer.java**
```
package com.mycompany.myapp.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import org.springframework.data.annotation.Id;
import java.util.List;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer {
@Id
String id;
String fiscalId;
String email;
String gender;
String birthdate;
String lastName;
String firstName;
//and so on...
}
```
**model/banana/BananaCustomer.java**
```
package com.mycompany.myapp.model.banana;
import com.mycompany.myapp.model.Customer;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "customers")
public class BananaCustomer extends Customer {
}
```
**model/banana/CustomerBananaRepository.java**
```
package com.mycompany.myapp.model.banana;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import java.util.List;
public interface CustomerBananaRepository extends MongoRepository<BananaCustomer, String> {
List<BananaCustomer> findAllByFiscalId(String fiscalId);
List<BananaCustomer> findAllByEmail(String email);
}
```
**model/orange/OrangeCustomer.java**
```
package com.mycompany.myapp.model.orange;
import com.mycompany.myapp.model.Customer;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "customers")
public class OrangeCustomer extends Customer {
}
```
**model/orange/CustomerOrangeRepo.java**
```
package com.mycompany.myapp.model.orange;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import java.util.List;
public interface CustomerOrangeRepo extends MongoRepository<OrangeCustomer, String> {
List<OrangeCustomer> findAllByFiscalId(String fiscalId);
List<OrangeCustomer> findAllByEmail(String email);
}
```
And last, but not least
**service/CustomerService.java**
```
package com.mycompany.myapp.service;
import com.mycompany.myapp.model.Customer;
import com.mycompany.myapp.model.banana.CustomerBananaRepository;
import com.mycompany.myapp.model.orange.CustomerOrangeRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class CustomerService {
private final CustomerOrangeRepo customerOrangeRepo;
private final CustomerBananaRepository customerBananaRepository;
@Autowired
public CustomerService(CustomerOrangeRepo customerOrangeRepo, CustomerBananaRepository customerBananaRepository) {
this.customerOrangeRepo = customerOrangeRepo;
this.customerBananaRepository = customerBananaRepository;
}
public List<? extends Customer> findAllByEmail(String email) {
return Stream.concat(
customerOrangeRepo.findAllByEmail(email).stream(),
customerBananaRepository.findAllByEmail(email).stream())
.collect(Collectors.toList());
}
}
```
Notice these things:
- I've renamed all references to my company and project name in compliance to all these sweet NDA's that i've signed up. Sorry for any inconsistency
- My service knows both BananaRepository and OrangeRepository and call them both to collect customers from both repos
- There are two identical types extending from customer. I need this to make them available to both mongo repositories.
- My service returns a List of ? extends Customers just because of that. Remember that java generics doesn't recognize inheritance when typing <somehing>
- The mongo **repository interface must live within the model class** (in the very same package). This is mandatory or it won't work.
- There is some magic going on with these property resolvers. So I have to define the database properties like this:
```
database.mongodb.banana.username=username
database.mongodb.orange.username=username
database.mongodb.peach.username=username
```
- The "peach" mongodb has nothing to do with my problem. It's just another mongodb config for a third mongo instance, that I used for a completely different purpose (Storing events, indeed)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment