Spring Data MongoDB Configure Database bean - spring

How to I configure the Mongo database bean? The bean is initialized by the MongoDB driver somehow with somethings pre-configured. I can't get the bean using mongoClient.getDatabase() because the database name either comes from the connection uri or just the standard property. And I can't define a new bean to update the old one like this public MongoDatabase mongoDatabase(MongoDatabase database) because that causes a ban definition cycle loop. What do I do?
Not entirely relevant to the question, but here's my intention:
What I trying to do is add custom Conventions introduced in MongoDB Driver version 3.6.
http://mongodb.github.io/mongo-java-driver/3.6/bson/pojos/
I know that spring data mongodb uses an older version, so I just replaced it with the newer one.

You need to take care of couple of things if you wish to configure MongoTemplate Or MongoDatabase yourself.
You need to disable spring boot's Mongo Auto Configuration. You can achieve this as show below.
#SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
You have to create MongoTemplate or MongoDatabase bean and register it with Spring application context(using #Bean).
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
ServerAddress server = new ServerAddress(host,port);
MongoClientOptions.Builder builder = MongoClientOptions.builder();
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new XYZCodec(..)),
MongoClient.getDefaultCodecRegistry());
builder.codecRegistry(codecRegistry).build();
MongoClientOptions options = builder.build();
MongoClient mongoClient = new MongoClient(server,options);
return new SimpleMongoDbFactory(mongoClient, mongoDataBase);
}
Replace XYZCodec with the codec you want.

Related

Spring boot application.properties naming

I am learning about springboot and trying to connect to a DB2 database. I got that working just fine.
Below are my working DB2 properties:
spring.datasource.url=jdbc:db2://server:port/database:currentSchema=schema-name;
spring.datasource.username=user1
spring.datasource.password=password1
But I renamed them to start with "db2" instead "spring" like:
db2.datasource.url=jdbc:db2://server:port/database:currentSchema=schema-name;
db2.datasource.username=user1
db2.datasource.password=password1
My app still runs, hHowever, when I do that, my controllers no longer return results as they did before the rename.
The reason I ask this is that if I add 2nd data source in the future, I could distinguish easily properties by their data sources if I name them like this.
UPDATE:
Thanks to #Kosta Tenasis answer below and this article (https://www.javadevjournal.com/spring-boot/multiple-data-sources-with-spring-boot/), I was able to resolve and figure this out.
Then going back to my specific question, once you have the configuration for data source in place, you can then modify application.properties to have:
db2.datasource.url=...
instead of having:
spring.datasource.url=...
NOTE1: if you are using Springboot 2.0, they changed to use Hikari and Hikari does not have url property but instead uses jdbc-url, so just change above to:
db2.datasource.jdbc-url=...
NOTE2: In your datasource that you had to create when adding multiple datasources to your project, you will have annotation #ConfigurationProperties. This annotation needs to point to your updated application.properties for datasource (the db2.datasource.url).
By default Spring looks for spring.datasource.** for the properties of the DataSource to connect to.
So you might be getting wrong results because you are not connecting to the database. If you want to configure a DataSource with different,from default, properties you can do like so
#Configuration
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="db2.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create()
.build();
}
And let's say a day comes along and you want a second DataSource you can modify the previous class to something like:
#Configuration
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="db2.datasource")
public DataSource d2Datasource() {
return DataSourceBuilder.create()
.build();
}
#Bean
#ConfigurationProperties(prefix="db3.datasource")
public DataSource db3Datasource() { //pun intented
return DataSourceBuilder.create()
.build();
}
}
and after that in each Class that you want a DataSource you can specify which of the beans you like:
public class DB3DependedClass{
private final DataSource dataSource;
public DB3DependedClass(#Qualifier("db3Datasource") DataSource dataSource){
this.dataSource = dataSource;
}
}
So by default spring will look for
spring.datasource.url (or spring.datasource.jdbc-url)
spring.datasource.username
spring.datasource.password
If you specify another DataSource of your own, those values are not needed.
So in the above example where we specified let's say db3.datasource spring will look for
db3.datasource.url
db3.datasource.username
db3.datasource.password
Important thing here is that the spring IS NOT inferred meaning the complete path is indeed: db3.datasource.url
and NOT
spring.db3.datasource.url
Finally to wrap this up you do have the flexibility to make it start with spring if you want so by declaring a prefix like spring.any.path.ilike.datasouce and of course under that the related values. Spring will pick up either path as long as you specify it.
NOTE: This answer is written solely in the text box provided here and was not tested in an IDE for compilation errors. The logic still holds though

what is the Difference between spring.profiles.active=production vs spring.profiles.active=local

When I try to run an spring boot application.
There is a property available inapplication.properties file where it has the property spring.profiles.active=production.
While search the details about this property in web I got to know that spring.profiles.active=local.
Can anyone kindly explain these details?
Certain environment-specific choices made for development aren’t appropriate or won’t work when the application transitions from development to production.
Consider database configuration, for instance. In a development environment,
you’re likely to use an embedded database preloaded with test data like this:
#Bean(destroyMethod="shutdown")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
In a production setting,
you may want to retrieve a DataSource from your container using JNDI:
#Bean
public DataSource dataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
Starting with Spring 3.1 you can use profiles. Method-leve #Profile annotation works starting from Spring 3.2. In Spring 3.1 it's only class-level.
#Configuration
public class DataSourceConfig {
#Bean(destroyMethod="shutdown")
#Profile("development")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
#Bean
#Profile("production")
public DataSource jndiDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
Each of the DataSource beans is in a profile and will only be created if the prescribed profile is active. Any bean that isn’t given a profile will always be created, regardless of what profile is active.
You can give any logical names to your profiles.
You can use this property to let Spring know which profiles should be active ( to be used while starting application). For example, if you give it in application.properties or via argument -Dspring.profiles.active=prod; you tell Spring, to run under prod profile. Which means - Spring will look for "application-prod.yml" or "application-prod.properties"file and will load all properties under it.
You can also annotate bean (method or class) by #Profile("PROFILE_NAME") - this ensures, the bean is mapped to certain profile.
You can pass multiple profiles to spring.profiles.active.
More information in docs - https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

Runtime resolution of destinations within Spring and JMS

I am trying to dynamically resolve;
#JmsListener(destinations = "${some.key}")
Now I know we can resolve this using properties using such thing as PropertySourcesPlaceholderConfigurer.
I have found the following spring ticket, https://jira.spring.io/browse/SPR-12289. Which gives me some indication that this can be done without the use of properties files using a DestinationResolver. I am hoping that this is a resolver that is not property based but I am not 100% sure. Could some give me an example of this without using xml based spring.
See the documentation which shows how to configure a custom destination resolver in the listener container factory that generates the listener for the annotation:
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setConcurrency("3-10");
return factory;
}

Spring + multiple H2 instances in memory

Two different H2 instances to be created in-memory. To make sure this happens, I initialized both instances with same shema and different data. So that when I query using DAO different set of data picked from different DataSource. However this is not happening. What am I doing wrong? How to name the instance of H2 correct?
#Bean(name = "DS1")
#Primary
public EmbeddedDatabase dataSource1() {
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
setName("DB1").
addScript("schema.sql").
addScript("data-1.sql").
build();
}
#Bean(name = "DS2")
public EmbeddedDatabase dataSource2() {
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
setName("DB2").
addScript("schema.sql").
addScript("data-2.sql").
build();
}
You have created two DataSources and have marked one as #Primary -- this is the one which will be used when your EntityManagerFactories and Repositories are autoconfigured. That's why both DAO's are accessing the same database.
In order to get around this, you need to declare two separate EntityManagerFactories, as described in the Spring Boot documentation:
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-use-two-entity-managers
After that, you'll need to declare two separate repositories and tell each repository which of the EntityManagerFactory to use. To do this, in your #EnableJpaRepositories annotation you'll have to specify the correct EntityMangerFactory. This article describes very nicely how to do that:
http://scattercode.co.uk/2013/11/18/spring-data-multiple-databases/
It would be nice if Spring Boot supported autoconfiguration with two DataSources, but I don't think it's going to happen soon:
https://github.com/spring-projects/spring-boot/issues/808
UPDATE
The author of the above article has published an updated approach:
https://scattercode.co.uk/2016/01/05/multiple-databases-with-spring-boot-and-spring-data-jpa/
The issue was not with multiple instances of H2; but with DataSource injection.
I solved it by passing the Qualifier in method argument.
#Autowired
#Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(#Qualifier("myDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

spring boot multiple mongodb datasource

We are using spring boot and we have multiple mongodbs within the system. We are able to configure "one" mongodb in application.properties files, as per the spring boot documents. Now we have a need to write to multiple mongodbs. How can we configure this?
Hope someone can help and any code examples would be helpful.
Thanks
GM
Use multiple #Bean methods, where you create and configure your datasources, and specify the bean name to distinguish them.
Example:
#Bean("primary")
public Mongo primaryMongo() throws UnknownHostException {
Mongo mongo = new Mongo();
// configure the client ...
return mongo;
}
#Bean("secondary")
public Mongo secondaryMongo() throws UnknownHostException {
Mongo mongo = new Mongo();
// configure the client ...
return mongo;
}
When you want to access the datasource, use the #Qualifier annotation on the field to specify the datasource with the bean name:
#Autowired
#Qualifier("primary")
private Mongo mongo;

Resources