How to load data in specified database by Spring Data Mongodb? - java

I have configured Spring Mongodb project to load data in database named "warehouse". Here is how my config class looks like
#Configuration
public class SpringMongoConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "warehouse";
}
public #Bean Mongo mongo() throws Exception {
return new Mongo("localhost");
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), getDatabaseName());
}
}
But Spring is always using the default database "test" to store and retrieve the collections. I have tried different approaches to point it to "warehouse" db. But it doesnt seem to work. What am doing wrong? Any leads are appreciated.

Assuming you have a standard mongo install (e.g., the database is at a default such as /data/db or C:\data\db), your configuration class looks correct. How are you using it? Can you try:
SpringMongoConfig config = new SpringMongoConfig();
MongoTemplate template = config.mongoTemplate();
template.createCollection("someCollection");
From a shell, if you then log into mongo and enter show dbs, do you not see a warehouse"?

Related

How to configure multiple couchbase data source using springboot-data-couchbase?

I am trying configuring multiple couchbase data source using springboot-data-couchbase.
This is a way I tried to attach two couchbase sources with 2 repositories.
#Configuration
#EnableCouchbaseRepositories("com.xyz.abc")
public class AbcDatasource extends AbstractCouchbaseConfiguration {
#Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("ip_address_of_couchbase");
}
//bucket_name
#Override
protected String getBucketName() {
return "bucket_name";
}
//password
#Override
protected String getBucketPassword() {
return "user_password";
}
#Override
#Bean(destroyMethod = "disconnect", name = "COUCHBASE_CLUSTER_2")
public Cluster couchbaseCluster() throws Exception {
return CouchbaseCluster.create(couchbaseEnvironment(), "ip_address_of_couchbase");
}
#Bean( name = "BUCKET2")
public Bucket bucket2() throws Exception {
return this.couchbaseCluster().openBucket("bucket2", "somepassword");
}
#Bean( name = "BUCKET2_TEMPLATE")
public CouchbaseTemplate newTemplateForBucket2() throws Exception {
CouchbaseTemplate template = new CouchbaseTemplate(
couchbaseClusterInfo(), //reuse the default bean
bucket2(), //the bucket is non-default
mappingCouchbaseConverter(), translationService()
);
template.setDefaultConsistency(getDefaultConsistency());
return template;
}
#Override
public void configureRepositoryOperationsMapping(RepositoryOperationsMapping baseMapping) {
baseMapping
.mapEntity(SomeDAOUsedInSomeRepository.class, newTemplateForBucket2());
}
}
similarly:
#Configuration
#EnableCouchbaseRepositories("com.xyz.mln")
public class MlnDatasource extends AbstractCouchbaseConfiguration {...}
Now the problem is there is no straight forward way to specify namespace based datasource by attaching different beans to these configurations like in springdata-jpa as springdata-jpa support this feature do using entity-manager-factory-ref and transaction-manager-ref.
Due to which only one configuration is being picked whoever comes first.
Any suggestion is greatly appreciated.
Related question: Use Spring Data Couchbase to connect to different Couchbase clusters
#anshul you are almost there.
Make one of the Data Source as #primary which will be used as by default bucket.
Wherever you want to use the other bucket .Just use specific bean in your service class with the qualifier below is the example:
#Qualifier(value = "BUCKET1_TEMPLATE")
#Autowired
CouchbaseTemplate couchbaseTemplate;
Now you can use this template to perform all couch related operations on the desired bucket.

Setting up Mongo Extension for Axon Framework on spring boot

So at first I added a properties file with:
spring.data.mongodb.uri=mongodb://axon:axon#aurl:27017/axonframework
which works but I was forced to use axonframework as db name because it is what was created in my mongo db.
Now controlling the db name and other details isn't an option in this case, so I went and checked around and found the following:
#configuration
public class AxonConfiguration {
#Value("${mongo.host:127.0.0.1}")
private String mongoHost;
#Value("${mongo.port:27017}")
private int mongoPort;
#Value("${mongo.db:test}")
private String mongoDB;
#Bean
public MongoSagaStore sagaStore() {
return new MongoSagaStore(axonMongoTemplate());
}
#Bean
public TokenStore tokenStore(Serializer serializer) {
return new MongoTokenStore(axonMongoTemplate(), serializer);
}
#Bean
public EventStorageEngine eventStorageEngine(Serializer serializer) {
return new MongoEventStorageEngine(serializer, null, axonMongoTemplate(), new DocumentPerEventStorageStrategy());
}
#Bean
public MongoTemplate axonMongoTemplate() {
return new DefaultMongoTemplate(mongo(), mongoDB);
}
#Bean
public MongoClient mongo() {
MongoFactory mongoFactory = new MongoFactory();
mongoFactory.setMongoAddresses(Collections.singletonList(new ServerAddress(mongoHost, mongoPort)));
return mongoFactory.createMongo();
}
}
Now apparently this worked for people but what I'm not being able to get right is how am I supposed to set the username and password?
I'm using axon 4.1, axonframework.extensions.mongo 4.1
The snippet of code you share does not correspond with Axon Framework release 4.x or Axon Mongo Extension release 4.x. The shift from version 3 to 4 has replaced almost all constructors of the infrastructure components in favor of the Builder pattern.
As such, you should not be able to do new MongoEventStorageEngine(...), but instead should do:
MongoEventStorageEngine.builder().mongoTemplate(axonMongoTemplate).build()
If you're still able to use the constructor, I assume you still have Axon 3 somewhere on the class path!
Regarding the Mongo specifics, I'd trust #PolishCivil's statement by the way.
Hope this helps!
This issue is not really related to the axon itself but more likely to the spring configuration of the mongo client instance since the usage of mongo is just an extension over the axon framework.
AFAIK it's
spring.data.mongodb.password
and
spring.data.mongodb.username
Also theres one thing in the code you should consider changing
return new DefaultMongoTemplate(mongo(), mongoDB);
You call the method that is specified as a bean, so instead in spring you should just wire it to your method parameter like so :
public MongoTemplate axonMongoTemplate(MongoClient client) {
return new DefaultMongoTemplate(client, mongoDB);
}

List users in memory in Spring Security?

Or more specifically, is it possible?
We currently have our users in memory using XML configuration. We know of InMemoryUserDetailsManager, but unfortunately it's not possible to get all users and users map inside InMemoryUserDetailsManager is private.
Yes you can actually access that using a bit a jugglery with Java reflection where you can access the properties which are not exposed via public API.
I have used below the RelectionUtils from Spring which should be available if you are using Spring ( which you are since it's Spring security).
The key is to get hold of AuthenticationManager via Autowiring and then drill down to the required Map containing the user info.
Just to demonstrate I have tested this on Spring Boot App but there should not be any issue if your are not using it.
#SpringBootApplication
public class SpringBootSecurityInMemoryApplication implements CommandLineRunner {
#Autowired AuthenticationManager authenticationManager;
................................
...............................
public static void main(String[] args) {
SpringApplication.run(SpringBootSecurityInMemoryApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
introspectBean(authenticationManager);
}
public void printUsersMap(Object bean){
Field field = ReflectionUtils.findField(org.springframework.security.authentication.ProviderManager.class, "providers");
ReflectionUtils.makeAccessible(field);
List listOfProviders = (List)ReflectionUtils.getField(field, bean);
DaoAuthenticationProvider dao = (DaoAuthenticationProvider)listOfProviders.get(0);
Field fieldUserDetailService = ReflectionUtils.findField(DaoAuthenticationProvider.class, "userDetailsService");
ReflectionUtils.makeAccessible(fieldUserDetailService);
InMemoryUserDetailsManager userDet = (InMemoryUserDetailsManager)(ReflectionUtils.getField(fieldUserDetailService, dao));
Field usersMapField = ReflectionUtils.findField(InMemoryUserDetailsManager.class, "users");
ReflectionUtils.makeAccessible(usersMapField);
Map map = (Map)ReflectionUtils.getField(usersMapField, userDet);
System.out.println(map);
}
I have 2 users configured - shailendra and admin. You can see the output of program below. You can get the required info from this map.
{shailendra=org.springframework.security.provisioning.MutableUser#245a060f, admin=org.springframework.security.provisioning.MutableUser#6edaa77a}

How to set MongoDB ReadPreference in Spring MVC's contextConfigLocation

I am connecting to a MongoDB sharding server via mongodb java driver in Spring MVC. I am using the following versions:
spring-webmvc-3.2.1.RELEASE
mongo-java-driver/2.10.0/mongo-java-driver-2.10.0
spring-data-mongodb-1.2.0.RELEASE
My Mongo options are set in the contextConfigLocation file mvc-dispatcher-servlet.xml
<mongo:mongo host="mongo.sample.com" port="30000">
<mongo:options auto-connect-retry="true"
slave-ok="true"/>
</mongo:mongo>
It works pretty well, but the slave-ok is deprecated by come.MongoDB.ReadPreference. I just wonder if there is any way to set the readPreference for Spring MVC in the contextConfiLocation file.
Declare the following bean
<bean id="readPreferenceSecondary" class="com.mongodb.TaggableReadPreference.SecondaryReadPreference">
</bean>
and
you inject this in your mongotemplate
<bean id="mongoTemplateProdDb" class="org.springframework.data.mongodb.core.MongoTemplate" >
<property name="readPreference" ref="readPreferenceSecondary"></property>
</bean>
Expanding #Trisha's response in to an answer: "Do it in MongoTemplate programmatically" by setting the ReadPreference to SECONDARY.
MongoTemplate template = new MongoTemplate(...);
template.setReadPreference(com.mongodb.ReadPreference.SECONDARY);
In case you are using spring-data-mongodb and have some requirement to use multiple Read Preferences based on find query, you can create multiple Mongo Templates and/or Repositories like
#EnableMongoRepositories(basePackages = {
"com.you.repo.package" }, mongoTemplateRef = "mongoTemplateOne")
#Configuration
public class MongoConfig {
#Bean(name="mongoTemplateOne")
public MongoTemplate getMongoTemplateOne() throws UnknownHostException {
MongoTemplate templateOne = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI("YOUR_MONGO_URL")));
templateOne.setReadPreference(ReadPreference.secondaryPreferred());
//setting WriteConcern but not relevant for this thread
templateOne.setWriteConcernResolver(yourWriteConcernResolver());
return templateOne;
}
#Bean(name = "mongoTemplateTwo")
public MongoTemplate getMongoTemplateTwo() throws UnknownHostException {
MongoTemplate templateTwo = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI("YOUR_MONGO_URL")));
templateTwo.setReadPreference(ReadPreference.secondaryPreferred());
return templateTwo;
}
private WriteConcernResolver yourWriteConcernResolver() {
return action -> {
if (action.getCollectionName()
.equals("your_collecton")
&& (action.getMongoActionOperation() == MongoActionOperation.SAVE
|| action.getMongoActionOperation() == MongoActionOperation.UPDATE)) {
return WriteConcern.MAJORITY;
}
return action.getDefaultWriteConcern();
};
}
In case you have more than one secondary (replica-set) you can be more specific and tell the mongo driver explicitly which of the secondaries you want to read from, using tags.
On the mongo side you run this command:
db.getMongo().setReadPref('secondaryPreferred',
[{"tagName":"TagVal1"},
{"tagName":"TagVal2"},
{}])
In the code it looks like this:
MongoTemplate template = new MongoTemplate(...)
template.setReadPreference(ReadPreference.secondaryPreferred("your DBObject that reflect your mongo tag names");
Hope it helps.
Here is one more way to do this using Mongo Repositories
#Configuration
#EnableMongoRepositories
class ApplicationConfig extends AbstractMongoClientConfiguration {
#Autowired
private Environment env;
#Value("${spring.data.mongodb.uri}")
public String mongoUri;
#Override
protected String getDatabaseName() {
return env.getProperty("spring.data.mongodb.database");
}
#Override
protected void configureClientSettings(MongoClientSettings.Builder builder) {
builder.applyConnectionString(new ConnectionString(mongoUri)).readPreference(ReadPreference.secondary());
}
}
sample app available #
https://github.com/prashanthmadi/cosmosdb-mongodb-readpreference-springboot-java/blob/main/src/main/java/azure/cosmosdb/mongodb/spring/cosmosdbmongodb/ApplicationConfig.java
If there is a need to mix between primary and secondary for reads depending on the collection, you can set the ReadPreference on the DBCollection object. This helps to avoid complex multiple MongoTemplate configuration. Instead, set collection level preference like below once in the application lifetime. All the reads for that specific collection will go to secondary, while for other collections it goes to primary.
DBCollection dbCollection = mongoTemplate.getCollection(mongoTemplate.getCollectionName(collection));
dbCollection.setReadPreference(ReadPreference.secondaryPreferred());
If you want to know different options to achieve it, please check Spring data mongodb secondary reads
As of spring-mongo-2.0.xsd, slave-ok has been entirely removed, but support has been added for XML config of ReadPreference. Here's the XML from the original question translated for the current XSD:
<mongo:mongo-client host="mongo.sample.com" port="30000">
<mongo:client-options read-preference="SECONDARY_PREFERRED" />
</mongo:mongo-client>

JUnit + Derby + Spring: drop in-memory db after every test

In my unit tests I autowired some DataSources, which use URLs like
jdbc:derby:memory:mydb;create=true
to create an in-memory DBs.
To drop an in-memory Derby db you have to connect with:
jdbc:derby:memory:mydb;drop=true
I would like this to happen after every test and start with a fresh db. How can I do this using Spring?
How to shutdown Derby in-memory database Properly
gave me a hint to a solution:
mydb.drop.url = jdbc:derby:memory:mydb;drop=true
...
<bean id="mydbDropUrl" class="java.lang.String">
<constructor-arg value="${mydb.drop.url}" />
</bean>
...
#Resource
private String mydbDropUrl;
#After
public void tearDown() {
try {
DriverManager.getConnection(mydbDropUrl);
} catch (SQLException e) {
// ignore
}
}
A downside is the use of the String constructor which accepts a String (an immutable String object around an immutable String object). I read that there is a #Value annotation in Spring 3, which might help here, but I'm using Spring 2.5.
Please let me know if you have a nicer solution.
There is a database-agnostic way to do this if you are using Spring together with Hibernate.
Make sure the application context will be created / destroyed before / after every test method:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"classpath*:application-context-test.xml"})
#TestExecutionListeners({DirtiesContextTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public abstract class AbstractTest {
}
Instruct Hibernate to auto create the schema on startup and to drop the schema on shutdown:
hibernate.hbm2ddl.auto = create-drop
Now before every test
the application context is created and the required spring beans are injected (spring)
the database structures are created (hibernate)
the import.sql is executed if present (hibernate)
and after every test
the application context is destroyed (spring)
the database schema is dropped (hibernate).
If you are using transactions, you may want to add the TransactionalTestExecutionListener.
After spring test 3, you can use annotations to inject configurations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/spring-test.xml")
public class MyTest {
}
Just do something like:
public class DatabaseTest implements ApplicationContextAware {
private ApplicationContext context;
private DataSource source;
public void setApplicationContext(ApplicationContext applicationContext) {
this.context = applicationContext;
}
#Before
public void before() {
source = (DataSource) dataSource.getBean("dataSource", DataSource.class);
}
#After
public void after() {
source = null;
}
}
Make your bean have a scope of prototype (scope="prototype"). This will get a new instance of the data source before every test.
If you use the spring-test.jar library, you can do something like this:
public class MyDataSourceSpringTest extends
AbstractTransactionalDataSourceSpringContextTests {
#Override
protected String[] getConfigLocations() {
return new String[]{"classpath:test-context.xml"};
}
#Override
protected void onSetUpInTransaction() throws Exception {
super.deleteFromTables(new String[]{"myTable"});
super.executeSqlScript("file:db/load_data.sql", true);
}
}
And an updated version based on latest comment, that drops db and recreates tables before every test:
public class MyDataSourceSpringTest extends
AbstractTransactionalDataSourceSpringContextTests {
#Override
protected String[] getConfigLocations() {
return new String[]{"classpath:test-context.xml"};
}
#Override
protected void onSetUpInTransaction() throws Exception {
super.executeSqlScript("file:db/recreate_tables.sql", true);
}
}
This is what we do at the start of every test.
Drop all Previous Objects.
Create all tables mentioned in the create_table.sql
Insert values onto the created tables based on what you want to test.
#Before
public void initialInMemoryDatabase() throws IOException, FileNotFoundException {
inMemoryDerbyDatabase.dropAllObjects();
inMemoryDerbyDatabase.executeSqlFile("/create_table_policy_version_manager.sql");
inMemoryDerbyDatabase.executeSqlFile("/insert_table_policy_version_manager.sql");
}
Works like a charm!

Categories

Resources