I need to create a mongodb database user in a Spring boot application using spring data mongodb. I will be creating this user as part of application startup.
I could not find any reference for doing this using spring data mongodb.
Is that possible by using Spring data mongodb?
I had the same issue in the past and I end up by creating the user before the context load, like this:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#SuppressWarnings("resource")
public static void main(final String[] args) {
createMongoDbUser();
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
private void createMongoDbUser() {
MongoClient mongo = new MongoClient(HOST, PORT);
MongoDatabase db = mongo.getDatabase(DB);
Map<String, Object> commandArguments = new BasicDBObject();
commandArguments.put("createUser", USER_NAME);
commandArguments.put("pwd", USER_PWD);
String[] roles = { "readWrite" };
commandArguments.put("roles", roles);
BasicDBObject command = new BasicDBObject(commandArguments);
db.runCommand(command);
}
}
Spring-data-mongodb will create the db all by itself if it can't find it, when declaring your mongo-db factory.
For instance, I declare my db-factory in xml using the following:
<mongo:db-factory id="mongofactory" dbname="dbNameHere" mongo-ref="mongo" />
I did not have to create it myself, it was created by spring-data-mongodb upon firing may app the first time.
Related
I have got a problem. A simple spring boot application works fine with existing MongoDB configuration.
For integration test, I added required configuration for embeded mongodb with flapdoodle configuration. All the unit tests are getting executed properly. When I run the main Spring Boot application, it by default considers the flapdoodle embeded mongodb configuration. As a result, the embeded mongodb never exits and while running the junit test cases, it still runs. I provide below the code snippet.
Whenever I start Spring Boot main application, it still runs the embeded mongodb. I see always the following lines in the console.
Download PRODUCTION:Windows:B64 START
Download PRODUCTION:Windows:B64 DownloadSize: 231162327
Download PRODUCTION:Windows:B64 0% 1% 2% 3% 4% 5% 6% 7% 8%
I provide the code for Mongodb configuration which should be picked up when running the main Spring Boot application.
#Slf4j
#Configuration
public class NoSQLAutoConfiguration {
#Autowired
private NoSQLEnvConfigProperties configProperties;
/**
* Morphia.
*
* #return the morphia
*/
private Morphia morphia() {
final Morphia morphia = new Morphia();
morphia.mapPackage(DS_ENTITY_PKG_NAME);
return morphia;
}
#Bean
public Datastore datastore(#Autowired #Qualifier("dev") MongoClient mongoClient) {
String dbName = configProperties.getDatabase();
final Datastore datastore = morphia().createDatastore(mongoClient, dbName);
datastore.ensureIndexes();
return datastore;
}
/**
* Mongo client.
*
* #return the mongo client
*/
#Primary
#Bean(name = "dev")
public MongoClient mongoClient() {
MongoClient mongoClient = null;
String dbHost = configProperties.getHost();
int dbPort = configProperties.getPort();
String database = configProperties.getDatabase();
log.debug("MongDB Host: {} - MongoDB Port: {}", dbHost, dbPort);
List<ServerAddress> serverAddresses = new ArrayList<>();
serverAddresses.add(new ServerAddress(dbHost, dbPort));
MongoClientOptions options = getMongoOptions();
String dbUserName = configProperties.getMongodbUsername();
String encRawPwd = configProperties.getMongodbPassword();
char[] dbPwd = null;
try {
dbPwd = Util.decode(encRawPwd).toCharArray();
} catch (Exception ex) {
// Ignore exception
dbPwd = null;
}
Optional<String> userName = Optional.ofNullable(dbUserName);
Optional<char[]> password = Optional.ofNullable(dbPwd);
if (userName.isPresent() && password.isPresent()) {
MongoCredential credential = MongoCredential.createCredential(dbUserName, database, dbPwd);
List<MongoCredential> credentialList = new ArrayList<>();
credentialList.add(credential);
mongoClient = new MongoClient(serverAddresses, credentialList, options);
} else {
log.debug("Connecting to local Mongo DB");
mongoClient = new MongoClient(dbHost, dbPort);
}
return mongoClient;
}
private MongoClientOptions getMongoOptions() {
MongoClientOptions.Builder builder = MongoClientOptions.builder();
builder.maxConnectionIdleTime(configProperties.getMongodbIdleConnection());
builder.minConnectionsPerHost(configProperties.getMongodbMinConnection());
builder.connectTimeout(configProperties.getMongodbConnectionTimeout());
return builder.build();
}
}
For integration testing, I have the configuration for embeded mongodb which is part of src/test.
#TestConfiguration
public class MongoConfiguration implements InitializingBean, DisposableBean {
MongodExecutable executable;
private static final String DBNAME = "embeded";
private static final String DBHOST = "localhost";
private static final int DBPORT = 27019;
#Override
public void afterPropertiesSet() throws Exception {
IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
.net(new Net(DBHOST, DBPORT, Network.localhostIsIPv6())).build();
MongodStarter starter = MongodStarter.getDefaultInstance();
executable = starter.prepare(mongodConfig);
executable.start();
}
private Morphia morphia() {
final Morphia morphia = new Morphia();
morphia.mapPackage(DS_ENTITY_PKG_NAME);
return morphia;
}
#Bean
public Datastore datastore(#Autowired #Qualifier("test") MongoClient mongoClient) {
final Datastore datastore = morphia().createDatastore(mongoClient, DBNAME);
datastore.ensureIndexes();
return datastore;
}
#Bean(name = "test")
public MongoClient mongoClient() {
return new MongoClient(DBHOST, DBPORT);
}
#Override
public void destroy() throws Exception {
executable.stop();
}
}
Please help me how to remove this embeded mongo configuration while running Spring Boot main application in eclipse.
I also provide below my main application below.
#EnableAspectJAutoProxy
#EnableSwagger2
#SpringBootApplication(scanBasePackages = { "com.blr.app" })
public class ValidationApplication {
/**
* The main method. f
*
* #param args the arguments
*/
public static void main(String[] args) {
SpringApplication.run(ValidationApplication.class, args);
}
}
I see the code that you have not added any profile to MongoConfiguration class. During eclipse build, this class is also picked up by Spring framework. Add the below lines to this class so that while running Spring Boot test this class will be picked up and while running main Spring Boot app, the actual Mongo Configuration file will be picked up. That is why Spring comes up with concept Profile. Add the profile appropriately for different environment.
#Profile("test")
#ActiveProfiles("test")
So final code will look like this.
#Profile("test")
#ActiveProfiles("test")
#TestConfiguration
public class MongoConfiguration implements InitializingBean, DisposableBean {
...
...
}
I'm trying to write a simple function which will connect to postgres and execute a select statement.
PostgresqlConnectionFactory connectionFactory = new PostgresqlConnectionFactory(
PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.database("MyDB")
.username("username")
.password("password").build());
DatabaseClient client = DatabaseClient.create(connectionFactory);
Flux<Map<String, Object>> result = client.execute("select * from table").fetch().all();
result.map(s -> {
System.out.println(s);
return null;
});
The above piece of code isn't printing anything. There is no error as well. I can connect to DB using the same credentials. What is missing in the code to stream data from DB?
Create the configuration class which is similar to the below code to connect to the PostgreSQL database
#Configuration
#EnableR2dbcRepositories
public class DatabaseConfig extends AbstractR2dbcConfiguration {
#Override
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get("r2dbc:postgresql://localhost:5432/DATABASE_NAME");
}
}
I am trying to write a Spring Boot test that uses embedded MondoDB 4.0.2; the code to test requires Mongo ChangeStreams which requires MongoDB start as a replica set. MongoDB as a replica set at MongoDB v4 requires journaling enabled. I was not able to find a way to start with journaling enabled so posted this here looking for answers. I subsequently found out how to do it - below.
I have spring-boot 2.1.3.RELEASE. Spring-data-mongodb 2.1.5.RELEASE
This is what I'd been trying:
#RunWith(SpringRunner.class)
#DataMongoTest(properties= {
"spring.mongodb.embedded.version= 4.0.2",
"spring.mongodb.embedded.storage.repl-set-name = r_0",
"spring.mongodb.embedded.storage.journal.enabled=true"
})
public class MyStreamWatcherTest {
#SpringBootApplication
#ComponentScan(basePackages = {"my.package.with.dao.classes"})
#EnableMongoRepositories( { "my.package.with.dao.repository" })
static public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#Before
public void startup() {
MongoDatabase adminDb = mongoClient.getDatabase("admin");
Document config = new Document("_id", "rs0");
BasicDBList members = new BasicDBList();
members.add(new Document("_id", 0).append("host",
mongoClient.getConnectPoint()));
config.put("members", members);
adminDb.runCommand(new Document("replSetInitiate", config));
However, when the test starts the options used to start mongo did not include enabling journal.
The fix was to add this class:
#Configuration
public class MyEmbeddedMongoConfiguration {
private int localPort = 0;
public int getLocalPort() {
return localPort;
}
#Bean
public IMongodConfig mongodConfig(EmbeddedMongoProperties embeddedProperties) throws IOException {
MongodConfigBuilder builder = new MongodConfigBuilder()
.version(Version.V4_0_2)
.cmdOptions(new MongoCmdOptionsBuilder().useNoJournal(false).build());
// Save the local port so the replica set initializer can come get it.
this.localPort = Network.getFreeServerPort();
builder.net(new Net("127.0.0.1", this.getLocalPort(), Network.localhostIsIPv6()));
EmbeddedMongoProperties.Storage storage = embeddedProperties.getStorage();
if (storage != null) {
String databaseDir = storage.getDatabaseDir();
String replSetName = "rs0"; // Should be able to: storage.getReplSetName();
int oplogSize = (storage.getOplogSize() != null)
? (int) storage.getOplogSize().toMegabytes() : 0;
builder.replication(new Storage(databaseDir, replSetName, oplogSize));
}
return builder.build();
}
This got journal enabled and the mongod started with replica set enabled. Then I added another class to initialize the replica set:
#Configuration
public class EmbeddedMongoReplicaSetInitializer {
#Autowired
MyEmbeddedMongoConfiguration myEmbeddedMongoConfiguration;
MongoClient mongoClient;
// We don't use this MongoClient as it will try to wait for the replica set to stabilize
// before address-fetching methods will return. It is specified here to order this class's
// creation after MongoClient, so we can be sure mongod is running.
EmbeddedMongoReplicaSetInitializer(MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
#PostConstruct
public void initReplicaSet() {
//List<ServerAddress> serverAddresses = mongoClient.getServerAddressList();
MongoClient mongo = new MongoClient(new ServerAddress("127.0.0.1", myEmbeddedMongoConfiguration.getLocalPort()));
MongoDatabase adminDb = mongo.getDatabase("admin");
Document config = new Document("_id", "rs0");
BasicDBList members = new BasicDBList();
members.add(new Document("_id", 0).append("host", String.format("127.0.0.1:%d", myEmbeddedMongoConfiguration.getLocalPort())));
config.put("members", members);
adminDb.runCommand(new Document("replSetInitiate", config));
mongo.close();
}
}
That's getting the job done. If anyone has tips to make this easier, please post here.
I'm new in MongoDB and Spring Data, usually the connection between an ordinary relational DB configuration is done in the .proprietes file such as :
# EMBEDDED SERVER CONFIGURATION
server.contextPath=/api
# JPA
spring.datasource.platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.database.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=postgres
spring.datasource.password=root
But now i've migrated to MongoDB and i suceeded to insert an object in it however it is classically configured (in the main.java), and this is how it is :
public class Application {
public static final String DB_NAME = "TestDB";
public static final String COMPTE_COLLECTION = "Compte";
public static final String MONGO_HOST = "localhost";
public static final int MONGO_PORT = 27017;
public static void main(String[] args) throws UnknownHostException {
try {
MongoClient mongo = new MongoClient(MONGO_HOST, MONGO_PORT);
MongoOperations mongoOps = new MongoTemplate(mongo, DB_NAME);
Compte p = new Compte("jon", "jon");
mongoOps.insert(p, COMPTE_COLLECTION);
System.out.println(p1);
mongo.close();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
And I want to know how to move the above MongoDB configuration to the .proprieties file ? I tried to put them as they are and it doesnt work.
Thank You in advance.
As you can see it documentation:
# MONGODB (MongoProperties)
spring.data.mongodb.authentication-database= # Authentication database name.
spring.data.mongodb.database=test # Database name.
spring.data.mongodb.field-naming-strategy= # Fully qualified name of the FieldNamingStrategy to use.
spring.data.mongodb.grid-fs-database= # GridFS database name.
spring.data.mongodb.host=localhost # Mongo server host.
spring.data.mongodb.password= # Login password of the mongo server.
spring.data.mongodb.port=27017 # Mongo server port.
spring.data.mongodb.repositories.enabled=true # Enable Mongo repositories.
spring.data.mongodb.uri=mongodb://localhost/test # Mongo database URI. When set, host and port are ignored.
spring.data.mongodb.username= # Login user of the mongo server.
Also from this link connecting to mongo note that:
if you’re using the Mongo 3.0 Java driver. In such cases,
spring.data.mongodb.uri should be used to provide all of the
configuration.
I'm trying to have a H2 database setup on spring boot application startup. I have configured the database in application.properties:
spring.datasource.url = jdbc:h2:file:~/testdb
spring.datasource.username = sa
spring.datasource.password = sa
spring.datasource.driverClassName = org.h2.Driver
The Application.java file:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
CreateH2Database createH2Database = new CreateH2Database();
createH2Database.create();
}
}
CreateH2Database.java:
public class CreateH2Database {
private Logger log = Logger.getLogger(CreateH2Database.class);
#Autowired
protected JdbcTemplate jdbcTemplate;
public void create() {
log.info("Creating H2 Database");
createUsers();
}
private void createUsers() {
log.info("Creating users table");
jdbcTemplate.execute("create table if not exists users (id serial, first_name varchar(255), last_name varchar(255))");
String[] names = "John Woo;Jeff Dean;Josh Bloch;Josh Long".split(";");
for (String fullname : names) {
String[] name = fullname.split(" ");
log.info("Inserting user record for " + name[0] + " " + name[1] + "\n");
jdbcTemplate.update(
"INSERT INTO users(first_name,last_name) values(?,?)",
name[0], name[1]);
}
}
}
Once the application is started it should create the Users table if it doesn't already exist, and insert the users into the table. If the table does already exist, I don't want it to be modified.
I get a NullPointerException on jdbcTemplate.execute. How can I get the jdbcTemplate injected? All the example I've seen require the datasource to be created manually, and then the JdbcTemplate is created. However, the datasource in this example seems to be created based on the application.properties values.
Is this the correct approach to setup the database (i.e. calling a CreateH2Database after starting the SpringApplication)? Will this approach work if I want to run the application as a WAR on another application server?
Since you are using Spring Boot, you should take advantage of it's database initialization features. There is no need to roll out your own implementation.
All you have to do is have the files schema.sql and data.sql on the root of the classpath (most likely under /resources). Spring Boot will auto-detect these and run the first one in order to create the database and the second one to populate it.
Check out this part of the Spring Boot documentation
If you need to perform the initialization conditionally (perhaps only when running integration tests), you can advantage of Spring profiles. What you would do in that case is have the properties file for the test profile contain
spring.datasource.initialize=true
while the properties file for the other profiles would contain
spring.datasource.initialize=false