Created
February 10, 2019 22:19
-
-
Save gmarti28/d6b4b871d8ce886cdb0ba87ca2099bd8 to your computer and use it in GitHub Desktop.
Answer to StackOverflow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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