Spring: 2 Repositories out of a single Entity - spring

What I need is 2 Repositories created out of a single entity:
interface TopicRepository implements ReactiveCrudRepository<Topic, String>
interface BackupTopicRepository implements ReactiveCrudRepository<Topic, String>
How is that possible? Right now only one is created.

This is how you would do it.
#Configuration
#ConfigurationProperties(prefix = "mongodb.topic")
#EnableMongoRepositories(basePackages = "abc.def.repository.topic", mongoTemplateRef = "topicMongoTemplate")
#Setter
class TopicMongoConfig {
private String host;
private int port;
private String database;
#Primary
#Bean(name = "topicMongoTemplate")
public MongoTemplate topicMongoTemplate() throws Exception {
final Mongo mongoClient = createMongoClient(new ServerAddress(host, port));
return new MongoTemplate(mongoClient, database);
}
private Mongo createMongoClient(ServerAddress serverAddress) {
return new MongoClient(serverAddress);
}
}
Another configuration
#Configuration
#ConfigurationProperties(prefix = "mongodb.backuptopic")
#EnableMongoRepositories(basePackages = "abc.def.repository.backuptopic", mongoTemplateRef = "backupTopicMongoTemplate")
#Setter
class BackupTopicMongoConfig {
private String host;
private int port;
private String database;
#Primary
#Bean(name = "backupTopicMongoTemplate")
public MongoTemplate backupTopicMongoTemplate() throws Exception {
final Mongo mongoClient = createMongoClient(new ServerAddress(host, port));
return new MongoTemplate(mongoClient, database);
}
private Mongo createMongoClient(ServerAddress serverAddress) {
return new MongoClient(serverAddress);
}
}
Your TopicRepository and BackuoTopicRepository should reside in abc.def.repository.topic and abc.def.repository.backuptopic respectively.
And also you need to have these properties defined in your properties or yml file
mongodb:
topic:
host:
database:
port:
backuptopic:
host:
database:
port:
Lastly, disable springboot autoconfiguration for mongo.
#SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})

Related

Field properties in xx.xxx.configuration.ElasticSearchConfiguration required a single bean, but 2 were found:

As part of spring-framework up-gradation from 1.5.6.RELEASE to 2.4.0 i am facing one issue
I am using 2 different ElasticSearch Host so i create 2 different classes one for ESConfigForMasterData and Other for ElasticSearchConfiguration while running spring boot application it throws an error
APPLICATION FAILED TO START
**Description:
Field properties in xxx.xxx.configuration.ElasticSearchConfiguration required a single bean, but 2 were found:
- integrationGlobalProperties: defined in null
- systemProperties: a programmatically registered singleton
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed**
#Configuration
#PropertySource("classpath:application.properties")
public class ElasticSearchConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ElasticSearchConfiguration.class);
#Autowired
private Properties properties;
#Value("${elasticsearch.cluster-nodes}")
private String clusterNodes;
#Value("${elasticsearch.cluster-name}")
private String clusterName;
#Value("${elasticsearch.host}")
private String elasticHost;
#Value("${elasticsearch.port}")
private String elasticPort;
#Value("${elasticsearch.protocol}")
private String elasticProtocol;
private Header[] headers = { new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json")};
#Bean(name = "restClient")
public RestClient restClient(){
return RestClient.builder(new HttpHost(elasticHost, Integer.parseInt(elasticPort), elasticProtocol)).setDefaultHeaders(headers).build();
}
#Bean(name = "restHighLevelClient")
public RestHighLevelClient restHighLevelClient(){
logger.info(" elastic port = " + elasticPort + " host = " + elasticHost);
return new RestHighLevelClient(RestClient.builder(new HttpHost(elasticHost, Integer.parseInt(elasticPort), elasticProtocol)));
}
}
#Configuration
#PropertySource("classpath:application.properties")
public class
ESConfigForMasterData {
private static final Logger logger = LoggerFactory.getLogger(ElasticSearchConfiguration.class);
#Autowired
private Properties properties;
#Value("${elasticsearch.masterdata.cluster-nodes}")
private String clusterNodes;
#Value("${elasticsearch.masterdata.cluster-name}")
private String clusterName;
#Value("${elasticsearch.masterdata.host}")
private String elasticHost;
#Value("${elasticsearch.masterdata.port}")
private String elasticPort;
#Value("${elasticsearch.masterdata.protocol}")
private String protocol;
private Header[] headers = { new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json")};
#Bean(name = "restHighLevelClientMasterData")
public RestHighLevelClient restHighLevelClientMasterData(){
logger.info(" elastic port = " + elasticPort + " host = " + elasticHost);
return new RestHighLevelClient(RestClient.builder(new HttpHost(elasticHost, Integer.parseInt(elasticPort), protocol)));
}
#Bean(name = "restClientMasterData")
public RestClient restClientMasterData(){
RestClientBuilder builder = RestClient.builder(new HttpHost(elasticHost, Integer.parseInt(elasticPort), protocol))
.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
#Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
return requestConfigBuilder.setConnectTimeout(60000000)
.setSocketTimeout(60000000);
}
});
//TODO: As of now we are ignoring it we need to find alternative method for this setMaxRetryTimeoutMillis
// .setMaxRetryTimeoutMillis(60000000);
//return RestClient.builder(new HttpHost(elasticHost, Integer.parseInt(elasticPort), protocol)).setDefaultHeaders(headers).build();
return builder.build();
}
}
#Repository
public class SearchDao {
private static final Logger logger = LoggerFactory.getLogger(SearchDao.class);
#Autowired
#Qualifier("restHighLevelClientMasterData")
private RestHighLevelClient restHighLevelClientMasterData;
#Autowired
#Qualifier("restClientMasterData")
private RestClient restClientMasterData;
}
I have tryed keeping #Primary & #Qualifier annotation but it doesn't work for me
please help me in sorting this issue

Mongo Template querying the wrong collection

I have a mongodb springboot application that is connected to 2 different databases, that have the same collection names and database names but different uris.
Here is my application.properties
spring.data.mongodb.uri = uri
spring.data.mongodb.secondDB.uri = uri
spring.data.mongodb.database = database_name
spring.data.mongodb.secondDB.database = database_name
My AppConfiguration file
#Configuration
public class MultipleMongoConfig {
#Primary
#Bean(name = "newdb1Properties")
#ConfigurationProperties(prefix = "spring.data.mongodb")
public MongoProperties getNewDb1Props() throws Exception {
return new MongoProperties();
}
#Bean(name = "newdb2Properties")
#ConfigurationProperties(prefix = "spring.data.mongodb.secondDB")
public MongoProperties getNewDb2Props() throws Exception {
return new MongoProperties();
}
#Primary
#Bean(name = "newdb1MongoTemplate")
public MongoTemplate newdb1MongoTemplate() throws Exception {
return new MongoTemplate(newdb1MongoDatabaseFactory(getNewDb1Props()));
}
#Bean(name ="newdb2MongoTemplate")
public MongoTemplate newdb2MongoTemplate() throws Exception {
return new MongoTemplate(newdb2MongoDatabaseFactory(getNewDb2Props()));
}
#Primary
#Bean
public MongoDatabaseFactory newdb1MongoDatabaseFactory(MongoProperties mongo) throws Exception {
return new SimpleMongoClientDatabaseFactory(
mongo.getUri()
);
}
#Bean
public MongoDatabaseFactory newdb2MongoDatabaseFactory(MongoProperties mongo) throws Exception {
return new SimpleMongoClientDatabaseFactory(
mongo.getUri()
);
}
Then I set up config files for each data source
#Configuration
#EnableMongoRepositories(basePackages = {"com.example.app.firstDatabse.Repository"},
mongoTemplateRef = "newdb1MongoTemplate"
)
public class NewDb1Config {
}
and
#Configuration
#EnableMongoRepositories(basePackages = {"com.example.app.secondDatabse.Repository"},
mongoTemplateRef = "newdb2MongoTemplate"
)
public class NewDb1Config {
}
For Model I have the following
#AllArgsConstructor
#NoArgsConstructor
#ToSting
#Document(collection = "coll")
public class FirstModel{
#Id
public String id;
#Field("f_name")
public String firstName;
#Field("l_name")
public String lastName;
#Field("age")
public int age;
#Field("gender")
public String gender;
}
and my second Model is the same
#AllArgsConstructor
#NoArgsConstructor
#ToSting
#Document(collection = "coll")
public class SecondModel{
#Id
public String id;
#Field("f_name")
public String firstName;
#Field("l_name")
public String lastName;
#Field("age")
public int age;
#Field("gender")
public String gender;
}
My controller
#ResController
#RequestMapping("/controller")
public class Controller{
#Autowired
private FirstDataabseRepository repo;
#Autowired
private SecondDataabseRepository repo;
#Resource
private MongoTemplate mongoTemplate;
#RequestMapping("/findByName")
public List<SecondModel> findByName(){
Criteria criteria = new Criteria();
criteria = Criteria.where("f_name").is("John");
Query q = new Query(criteria);
List<SecondModel> results = mongoTemplate.find(q,SecondModel.class);
return results;
}
}
So the results show the results of the first collection not the second one. What do I need to do for mongoTemplate to query the second collection not the first one.
Solution
I have to add a qualifier and have a mongoTemplate for each collection
#ResController
#RequestMapping("/controller")
public class Controller{
#Autowired
private FirstDataabseRepository repo;
#Autowired
private SecondDataabseRepository repo;
#Resource
#Qualifier(value="newdb1MongoTemplate")
private MongoTemplate mongoTemplate;
#Resource
#Qualifier(value="newdb2MongoTemplate")
private MongoTemplate mTemplate;
#RequestMapping("/findByName")
public List<SecondModel> findByName(){
Criteria criteria = new Criteria();
criteria = Criteria.where("f_name").is("John");
Query q = new Query(criteria);
List<SecondModel> results = mTemplate.find(q,SecondModel.class);
return results;
}
}

'Couldn't find PersistentEntity for type class' exception in Spring boot MongoRepository

In here I have configured two databases in mongodb. As described in this tutorial (link). So basically I override the MongoDataAutoConfiguration and MongoProperties implementations.
The property yml file :
spring:
autoconfigure:
exclude:
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
mongodb:
primary:
host: 127.0.0.1
port: 27017
database: db_admin_crm
secondary:
host: 127.0.0.1
port: 27017
database: lead_forms
MultipleMongoProperties class :
#Data
#ConfigurationProperties(prefix = "mongodb")
public class MultipleMongoProperties {
private MongoProperties primary = new MongoProperties();
private MongoProperties secondary = new MongoProperties();
//getters and setters
}
MultipleMongoConfig class :
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(MultipleMongoProperties.class)
public class MultipleMongoConfig {
#Autowired
private final MultipleMongoProperties mongoProperties;
public MultipleMongoConfig() {
mongoProperties = null;
}
#Primary
#Bean(name = "primaryMongoTemplate")
public MongoTemplate primaryMongoTemplate() throws Exception {
return new MongoTemplate(primaryFactory(this.mongoProperties.getPrimary()));
}
#Bean(name = "secondaryMongoTemplate")
public MongoTemplate secondaryMongoTemplate() throws Exception {
return new MongoTemplate(secondaryFactory(this.mongoProperties.getSecondary()));
}
#Bean
#Primary
public MongoDbFactory primaryFactory(final MongoProperties mongo) throws Exception {
return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
mongo.getDatabase());
}
#Bean
public MongoDbFactory secondaryFactory(final MongoProperties mongo) throws Exception {
return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
mongo.getDatabase());
}
}
PrimaryMongoConfig :
#Configuration
#EnableMongoRepositories(basePackages = "io.crm.service.repositories",
mongoTemplateRef = "primaryMongoTemplate")
public class PrimaryMongoConfig{
}
SecondaryMongoConfig :
#Configuration
#EnableMongoRepositories(basePackages = "io.crm.service.repositories.report.repositories",
mongoTemplateRef = "secondaryMongoTemplate")
public class SecondaryMongoConfig {
}
The Repository class :
#RepositoryRestResource(collectionResourceRel = "users",path = "users",excerptProjection = UserProjection.class)
public interface UserRepository extends MongoRepository<User, String> {
}
User model class :
#Id
private String id;
private String email;
private String name;
private String businessName;
private String phone;
private String address;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private Date createdTime;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private Date updatedTime;
#Field("bookletSignUps")
#DBRef
private List<BookletSignUp> bookletSignUps;
#Field("eventSignUps")
#DBRef
private List<EventSignUp> eventSignUps;
#Field("infoSignUps")
#DBRef
private List<InfoSignUp> infoSignUps;
#Field("webinarSignUps")
#DBRef
private List<WebinarSignUp> webinarSignUps;
The projection :
#Projection(name = "userExcerpt", types = User.class)
public interface UserProjection {
String getId();
String getName();
String getEmail();
String getBusinessName();
String getPhone();
String getAddress();
Date getCreatedTime();
Date getUpdatedTime();
List<BookletSignUp> getBookletSignUps();
List<EventSignUp> getEventSignUps();
List<InfoSignUp> getInfoSignUps();
List<WebinarSignUp> getWebinarSignUps();
}
But when im trying to do a GET request to the REST endpoint http://localhost:9090/users/ im getting java.lang.IllegalArgumentException: Couldn't find PersistentEntity for type class io.crm.service.models.User! exception here. What could be gone wrong here? Ideas will be very much appreciated. Thanks in advance.

Spring Boot multiple MongoDB configuration

I have looked everywhere for this and it seems like I cannot find a solution that works. I am using spring boot 1.5.10-RELEASE. I am trying to configure two different mongodb instances in the same application. Here is my code:
Main Application:
#SpringBootApplication(exclude = {MongoAutoConfiguration.class})
#ComponentScan("com.reef.reports")
public class MainApplication
{
public static void main(String[] args)
{
SpringApplication.run(MainApplication.class, args);
}
}
1st Instance
#Configuration
#EnableMongoRepositories(basePackages = {"com.reef.repository.mongousa"} , mongoTemplateRef = "USAMongo")
public class MongoUsaConfig
{
#Value("${usa.mongodb.host}")
private String host;
#Value("${usa.mongodb.database:reef}")
private String database;
#Value("${usa.mongodb.port:27017}")
private int port;
#Value("${usa.mongodb.username:}")
private String username;
#Value("${usa.mongodb.password:}")
private String password;
#Value("${usa.mongodb.authdb:}")
private String authdb;
private final List<MongoCredential> credentials = new ArrayList<>();
private final List<ServerAddress> hosts = new ArrayList<>();
/**
* Method that creates MongoDbFactory
* Common to both of the MongoDb connections
*/
public MongoDbFactory mongoDbFactory()
{
return new SimpleMongoDbFactory(getMongoClient(), database);
}
/**
* Method that creates MongoClient
*/
#Bean(name = "USAClient")
public MongoClient getMongoClient()
{
if ((null != username)&&(!username.isEmpty()))
{
hosts.add(new ServerAddress(host, port));
credentials.add(MongoCredential.createMongoCRCredential(username, authdb, password.toCharArray()));
return new MongoClient(hosts, credentials);
}
else
{
return new MongoClient(host, port);
}
}
#Primary
#Bean(name = "USAMongo")
public MongoTemplate getMongoTemplate()
{
return new MongoTemplate(mongoDbFactory());
}
}
2nd Instance
#Configuration
#EnableMongoRepositories(basePackages = {"com.reef.repository.mongocan"} , mongoTemplateRef = "CANMongo")
public class MongoCanConfig
{
#Value("${can.mongodb.host}")
private String host;
#Value("${can.mongodb.database:reef}")
private String database;
#Value("${can.mongodb.port:27017}")
private int port;
#Value("${can.mongodb.username:}")
private String username;
#Value("${can.mongodb.password:}")
private String password;
#Value("${can.mongodb.authdb:}")
private String authdb;
private final List<MongoCredential> credentials = new ArrayList<>();
private final List<ServerAddress> hosts = new ArrayList<>();
/**
* Method that creates MongoDbFactory
* Common to both of the MongoDb connections
*/
public MongoDbFactory mongoDbFactory()
{
return new SimpleMongoDbFactory(getMongoClient(), database);
}
/**
* Method that creates MongoClient
*/
#Bean(name = "CANClient")
public MongoClient getMongoClient()
{
if ((null != username)&&(!username.isEmpty()))
{
hosts.add(new ServerAddress(host, port));
credentials.add(MongoCredential.createMongoCRCredential(username, authdb, password.toCharArray()));
return new MongoClient(hosts, credentials);
}
else
{
return new MongoClient(host, port);
}
}
#Bean(name = "CANMongo")
public MongoTemplate getMongoTemplate()
{
return new MongoTemplate(mongoDbFactory());
}
}
When I run the application, it will run the configuration for the first instance. However, it will not pick up the second instance. I have put in breakpoints to debug and it never hits the breakpoint in the configuration. The repositories in this package get loaded correctly:
com.reef.repository.mongousa
The errors happen with the repositories in this package:
com.reef.repository.mongocan
Please let me know what I am missing. Why does one config work and the other does not?
Refer to this blog post https://medium.com/#joeclever/using-multiple-datasources-with-spring-boot-and-spring-data-6430b00c02e7
I tried it out for two MySQL DataBases and it is working fine.

Spring Boot - Same repository and same entity for different databases

I have a Spring Boot project with one entity and one repository associated to this entity. In the repository there is one method with a custom query and in the project controller this repository is used to return data from different postgresql databases. These databases have same tables with same columns (so the referred entity is the same), the only difference among these databases is the year (..., DB2015, DB2016, DB2017).
My questions are: How can i return data in the project controller that belong to "different" databases? Is possible to use the same query to select data initially from the first database, then from the second and so on?
In other questions i've read that i need different datasources, is this correct?
This is the entity:
#Entity(name = "REQUEST")
public class Request implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name="IDREQUEST", nullable=false)
private BigDecimal idrequest;
#Column(name="PAYLOAD")
private String payload;
#Column(name="MITTENTE")
private String mittente;
#Column(name="SERVIZIO")
private String servizio;
#Column(name="DATARICEZIONE")
private BigDecimal dataricezione;
public BigDecimal getIdrequest() {
return idrequest;
}
public void setIdrequest(BigDecimal idrequest) {
this.idrequest = idrequest;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
public String getMittente() {
return mittente;
}
public void setMittente(String mittente) {
this.mittente = mittente;
}
public String getServizio() {
return servizio;
}
public void setServizio(String servizio) {
this.servizio = servizio;
}
public BigDecimal getDataricezione() {
return dataricezione;
}
public void setDataricezione(BigDecimal dataricezione) {
this.dataricezione = dataricezione;
}
}
This is the repository:
#Repository
public interface RequestRepository extends PagingAndSortingRepository<Request, BigDecimal> {
#Query(nativeQuery=true, value="SELECT * FROM \"REQUEST\" WHERE strpos(\"PAYLOAD\",\'?1\') > 0")
List<Request> findByCodiceFiscale(String codiceFiscale);
}
This is the controller
#RequestMapping(value="/ricercaadesioni/{codicefiscale}", method=RequestMethod.GET)
public ResponseEntity<List<Request>> ricercaAdesioniByCodiceFIscale(#PathVariable("codicefiscale") String codicefiscale) {
List<Request> listAdesioni = requestRepo.findByCodiceFiscale(codicefiscale);
return new ResponseEntity<List<Request>>(listAdesioni, HttpStatus.OK);
}
This is application.properties (in this case the datasource is referred to one db only):
spring.datasource.url=jdbc:postgresql://localhost:5432/DB2017_test
spring.datasource.username=xxx
spring.datasource.password=xxx
Hope everything is clear
Create 2 config files with different datasource and these 2 config files will have different specifications for 2 different jpa repository class.but can have same domain class.
step1>
In your properties file have 2 datasource details.
spring.datasource.url=jdbc:postgresql://localhost:5432/DB2017_test
spring.datasource.username=xxx
spring.datasource.password=xxx
# DB2018 DB - ""
spring.datasource2.url=jdbc:postgresql://localhost:5432/DB2018_test
spring.datasource2.username=xxx
spring.datasource2.password=xxx
step2>Then create config file for first dataSource
package com.package1;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
basePackages = { "com.package1.repo" }
)
public class DB2017Config {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("com.domain")
.persistenceUnit("foo")
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
step3> Similary create another config file for other dataSource,
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
basePackages = { "com.package2.repo" }
And change prefix
#ConfigurationProperties(prefix = "spring.datasource2")
Now you will have 2 similar RequestRepository1 and RequestRepository2 in package1 and package2 respectiverly as mentioned above (basePackages = { "com.package1.repo" }).
step4>All set autowire 2 different repo .
#Autowired
private final RequestRepository1 repo1;
#Autowired
private final RequestRepository2 repo2;
Then use them.
List<Request> listAdesioni = repo1.findByCodiceFiscale(codicefiscale);
List<Request> listAdesioni = repo2.findByCodiceFiscale(codicefiscale);

Resources