I have simple maven projects with Spring-data-mongodb project. And I need to specify (override) Database connection details. MongoDB databse runs on localhost with default settings (port=27017). I am trying to use AppConfig
#Configuration
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "TestDatabase";
}
#Override
public Mongo mongo() throws Exception {
// wrong port on purpose
return new MongoClient("127.0.0.1", 27007);
}
}
My main file looks like this
public class MongoApp {
private static final Log log = LogFactory.getLog(MongoApp.class);
public static void main(String[] args) throws Exception {
MongoOperations mongoOps = new MongoTemplate(new MongoClient(), "database");
mongoOps.insert(new Person("Joe", 34));
log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));
// mongoOps.dropCollection("person");
}
}
When I run project everything works fine but I it should not with this config.
project structure
main
-AppConfig.java
-MongoApp.java
-Person.java
I found the solution.
I used this configuration:
#Configuration
public class AppConfig {
public #Bean
MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "mydb");
}
public #Bean
MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
And main file with ApplicationContext:
public class MongoApp {
private static final Log log = LogFactory.getLog(MongoApp.class);
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MongoOperations mongoOperation = (MongoOperations)ctx.getBean("mongoTemplate");
mongoOperation.insert(new Person("Joe", 34));
log.info(mongoOperation.findOne(new Query(where("name").is("Joe")), Person.class));
//mongoOps.dropCollection("person");
}
}
Related
I'm using springboot and spring-data-jdbc.
I wrote this repository class
#Repository
#Transactional(rollbackFor = Exception.class)
public class RecordRepository {
public RecordRepository() {}
public void insert(Record record) throws Exception {
JDBCConfig jdbcConfig = new JDBCConfig();
SimpleJdbcInsert messageInsert = new SimpleJdbcInsert(jdbcConfig.postgresDataSource());
messageInsert.withTableName(record.tableName()).execute(record.content());
throw new Exception();
}
}
Then I wrote a client class that invokes the insert method
#EnableJdbcRepositories()
#Configuration
public class RecordClient {
#Autowired
private RecordRepository repository;
public void insert(Record r) throws Exception {
repository.insert(r);
}
}
I would expect that no record are insert to db when RecordClient's insert() method is invoked, because RecordRespository's insert() throws Exception. Instead the record is added however.
What am I missing?
EDIT. This is the class where I configure my Datasource
#Configuration
#EnableTransactionManagement
public class JDBCConfig {
#Bean
public DataSource postgresDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/db");
dataSource.setUsername("postgres");
dataSource.setPassword("root");
return dataSource;
}
}
You have to inject your datasource instead of creating it manually. I guess because #Transactional only works for Spring managed beans. If you create a datasource instance by calling new constructor (like this new JDBCConfig(). postgresDataSource()), you are creating it manually and it's not a Spring managed beans.
#Repository
#Transactional(rollbackFor = Exception.class)
public class RecordRepository {
#Autowired
DataSource dataSource;
public RecordRepository() {}
public void insert(Record record) throws Exception {
SimpleJdbcInsert messageInsert = new SimpleJdbcInsert(dataSource);
messageInsert.withTableName(record.tableName()).execute(record.contents());
throw new Exception();
}
}
I have done a sample application with springboot to connect with two databases using the below reference
https://dzone.com/articles/multiple-mongodb-connectors-with-spring-boot
I can access data from different databases using repository interfaces which extends MongoRepository
But i want to access data using MongoOperations as below how to differentiate between two databases
#RestController
#RequestMapping(value = "/sample")
public class SampleController {
private final PrimaryRepository primaryRepository;
private final SecondaryRepositor secondaryRepository;
#Autowired
MongoOperations mongoOps;
#Autowired
public SampleController(PrimaryRepository primaryRepository, SecondaryRepositor secondaryRepository) {
this.primaryRepository = primaryRepository;
this.secondaryRepository = secondaryRepository;
}
#RequestMapping(method = RequestMethod.GET)
public void sample() {
log.info("************************************************************");
log.info("Start printing mongo objects");
log.info("************************************************************");
primaryRepository.save(new PrimaryModel(null, "Primary database plain object"));
secondaryRepository.save(new SecondaryModel(null, "Secondary database plain object"));
List<PrimaryModel> primaries = primaryRepository.findAll();
for (PrimaryModel primary : primaries) {
log.info(primary.toString());
}
List<SecondaryModel> secondaries = secondaryRepository.findAll();
**List<SecondaryModel> second = mongoOps.findAll(SecondaryModel.class);**
log.info("RES: {}",second);
for (SecondaryModel secondary : secondaries) {
log.info(secondary.toString());
}
log.info("************************************************************");
log.info("Ended printing mongo objects");
log.info("************************************************************");
}
}```
You need to use #Qualifier to get desired MongoDB instance.
#Autowired
#Qualifier("primaryMongoTemplate")
MongoOperations primaryMongoOps;
#Autowired
#Qualifier("secondaryMongoTemplate")
MongoOperations secondaryMongoOps;
Note:
You don't need public SampleController(PrimaryRepository primaryRepository, SecondaryRepositor secondaryRepository), use instead:
#RestController
#RequestMapping(value = "/sample")
public class SampleController {
#Autowired
PrimaryRepository primaryRepository;
#Autowired
SecondaryRepositor secondaryRepository;
#Autowired
#Qualifier("primaryMongoTemplate")
MongoOperations primaryMongoOps;
#Autowired
#Qualifier("secondaryMongoTemplate")
MongoOperations secondaryMongoOps;
#RequestMapping(method = RequestMethod.GET)
public void sample() {
...
Can you try below configuration to use multiple database with MongoOperation
#Configuration
#EnableMongoRepositories(basePackages={"com.company.repo.dbOne"}, mongoTemplateRef="mongoTemplateOne")
public class AbcRepoConfiguration {
#Bean(name="mongoPropertiesOne")
#ConfigurationProperties
public MongoProperties mongoProperties() {
return new MongoProperties();
}
#Bean(name="mongoPropertiesOne")
public SimpleMongoDbFactory mongoDbFactory(MongoClient mongo, #Qualifier("mongoPropertiesOne") MongoProperties mongoProperties) throws Exception {
String database = this.mongoProperties.getMongoClientDatabase();
return new SimpleMongoDbFactory(mongo, database);
}
#Bean(name="mongoTemplateOne")
public MongoTemplate mongoTemplate(#Qualifier("mongoTemplateOne") MongoDbFactory mongoDbFactory, MongoConverter converter) throws UnknownHostException {
return new MongoTemplate(mongoDbFactory, converter);
}
}
#Configuration
#EnableMongoRepositories(basePackages={"com.company.repo.dbTwo"}, mongoTemplateRef="mongoTemplateTwo")
public class AbcRepoConfiguration {
#Bean(name="mongoPropertiesTwo")
#ConfigurationProperties
public MongoProperties mongoProperties() {
return new MongoProperties();
}
#Bean(name="mongoPropertiesTwo")
public SimpleMongoDbFactory mongoDbFactory(MongoClient mongo, #Qualifier("mongoPropertiesTwo") MongoProperties mongoProperties) throws Exception {
String database = this.mongoProperties.getMongoClientDatabase();
return new SimpleMongoDbFactory(mongo, database);
}
#Bean(name="mongoTemplateTwo")
public MongoTemplate mongoTemplate(#Qualifier("mongoTemplateTwo") MongoDbFactory mongoDbFactory, MongoConverter converter) throws UnknownHostException {
return new MongoTemplate(mongoDbFactory, converter);
}
}
You can create two MongoOperations bean. You can replace replace localhost with the DB machine IP Address. if you have sharded cluster you can change below implementation new MongoClient(new MongoClientURI("mongodb://host1:27017,host2:27017"));
#Configuration
class MongoConfiguration {
#Bean
public MongoClient mongo() {
return new MongoClient("localhost", 27017); // server address, port
}
#Bean
#Qualifier("firstDatabase")
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), "test1Db"); // test1Db is database name
}
#Bean
#Qualifier("secondDatabase")
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), "test2Db");
}
}
Now in your controller, you can inject both MongoTemplate and connect to the respective database.
#Autowired
#Qualifier("firstDatabase")
MongoOperations mongoOps1;
#Autowired
#Qualifier("secondDatabase")
MongoOperations mongoOps2;
I'm referring to this article, in which we can use the AbstractRoutingDataSource from Spring Framework to dynamically change the data source used by the application. I'm using Mybatis (3.3.0) with Spring (4.1.6.RELEASE). I want to switch to the backup database if exception occurs while getting data from main db. In this example, i have used hsql and mysql db.
RoutingDataSource:
public class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getTargetDataSource();
}
}
DataSourceContextHolder:
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceEnum> contextHolder = new ThreadLocal<DataSourceEnum>();
public static void setTargetDataSource(DataSourceEnum targetDataSource) {
contextHolder.set(targetDataSource);
}
public static DataSourceEnum getTargetDataSource() {
return (DataSourceEnum) contextHolder.get();
}
public static void resetDefaultDataSource() {
contextHolder.remove();
}
}
ApplicationDataConfig:
#Configuration
#MapperScan(basePackages = "com.sample.mapper")
#ComponentScan("com.sample.config")
#PropertySource(value = {"classpath:app.properties"},
ignoreResourceNotFound = true)
public class ApplicationDataConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer =
new PropertySourcesPlaceholderConfigurer();
return configurer;
}
#Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setDefaultTargetDataSource(dataSource1());
Map<Object, Object> targetDataSource = new HashMap<Object, Object>();
targetDataSource.put(DataSourceEnum.HSQL, dataSource1());
targetDataSource.put(DataSourceEnum.BACKUP, dataSource2());
routingDataSource.setTargetDataSources(targetDataSource);
sessionFactory.setDataSource(routingDataSource);
sessionFactory.setTypeAliasesPackage("com.sample.common.domain");
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath*:com/sample/mapper/**/*.xml"));
return sessionFactory;
}
#Bean
public DataSource dataSource1() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript(
"classpath:database/app-hsqldb-schema.sql").addScript(
"classpath:database/app-hsqldb-datascript.sql").build();
}
#Bean
public DataSource dataSource2() {
PooledDataSourceFactory pooledDataSourceFactory = new PooledDataSourceFactory();
pooledDataSourceFactory.setProperties(jdbcProperties());
return pooledDataSourceFactory.getDataSource();
}
#Bean
protected Properties jdbcProperties() {
//Get the data from properties file
Properties jdbcProperties = new Properties();
jdbcProperties.setProperty("url", datasourceUrl);
jdbcProperties.setProperty("driver", datasourceDriver);
jdbcProperties.setProperty("username", datasourceUsername);
jdbcProperties.setProperty("password", datasourcePassword);
jdbcProperties.setProperty("poolMaximumIdleConnections", maxConnectionPoolSize);
jdbcProperties.setProperty("poolMaximumActiveConnections", minConnectionPoolSize);
return jdbcProperties;
}
}
Client:
#Autowired
private ApplicationMapper appMapper;
public MyObject getObjectById(String Id) {
MyObject myObj = null;
try{
DataSourceContextHolder.setTargetDataSource(DataSourceEnum.HSQL);
myObj = appMapper.getObjectById(Id);
}catch(Throwable e){
DataSourceContextHolder.setTargetDataSource(DataSourceEnum.BACKUP);
myObj = appMapper.getObjectById(Id);
}finally{
DataSourceContextHolder.resetDefaultDataSource();
}
return getObjectDetails(myObj);
}
I'm getting the following exception
### Error querying database. Cause: java.lang.IllegalArgumentException: DataSource router not initialized
However i'm able to get things working if i'm using only one db at a time, this means there is no issue with data source configuration.
Can you try this last line once (in same order) :-
targetDataSource.put(DataSourceEnum.HSQL, dataSource1());
targetDataSource.put(DataSourceEnum.BACKUP, dataSource2());
routingDataSource.setTargetDataSources(targetDataSource);
routingDataSource.afterPropertiesSet();
I got the same issue and found a solution using the SchemaExport class of hibernate.
For each DataSourceEnum you can manually initialize the datasource.
here is my detailed answer to my own issue discription
I would like to use application.yml and profiles approach for externalising db and other infrastructure dependencies.
I currently have neo4j url hard coded in the main application.
#Configuration
#EnableNeo4jRepositories("com.xxx.repository")
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application extends Neo4jConfiguration {
public Application() {
setBasePackage("com.xxx.entity");
}
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new SpringRestGraphDatabase("http://127.0.0.1:7474/db/data");
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
UPDATED:
#Value("${neo4jurl}")
private String neoUrl;
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new SpringRestGraphDatabase(neoUrl);
}
src/main/resources/application.yml
neo4jurl: http://127.0.0.1:7474/db/data
How to write this part of annotation based configuration in XML?
#Bean
public EventRepository eventRepository() throws Exception {
return new SolrRepositoryFactory(eventTemplate())
.getRepository(EventRepository.class, new EventRepositoryImpl(eventTemplate()));
}
Full code of this config:
#Configuration
public class SolrContext {
#Bean
public SolrServerFactory solrServerFactory() {
return new MulticoreSolrServerFactory(new HttpSolrServer("solr.host"));
}
#Bean
public SolrTemplate eventTemplate() throws Exception {
SolrTemplate solrTemplate = new SolrTemplate(solrServerFactory());
solrTemplate.setSolrCore("events");
return solrTemplate;
}
#Bean
public EventRepository eventRepository() throws Exception {
return new SolrRepositoryFactory(eventTemplate())
.getRepository(EventRepository.class, new EventRepositoryImpl(eventTemplate()));
}
}
I got this example from that answer.