TaskConfigurer does not considering schema name for datasource - java

I have 2 data source. For one data source, I want to use custom schema name. For this reason, I am setting my data source url like
spring.datasource.url=jdbc:postgresql://192.168.33.10/analytics?currentSchema=bahmni_mart_scdf.
But it's creating all the tables in public schema, instead of bahmni_mart_scdf schema.
I already override TaskRepositoryInitializer bean and implements TaskConfigurer.
Here are my implementations
DatabaseConfiguration.class
#Configuration
public class DatabaseConfiguration {
#Bean(name = "martDb")
#ConfigurationProperties(prefix = "spring.ds_mart")
public DataSource martDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "martJdbcTemplate")
public JdbcTemplate martJdbcTemplate(#Qualifier("martDb") DataSource dsMart) {
return new JdbcTemplate(dsMart);
}
#Bean(name = "scdfDb")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "scdfJdbcTemplate")
public JdbcTemplate scdfJdbcTemplate(#Qualifier("scdfDb") DataSource scdfDb) {
return new JdbcTemplate(scdfDb);
}
}
DataloadTaskConfigurer.class
#Component
public class DataloadTaskConfigurer implements TaskConfigurer {
private DataSource dataSource;
private TaskRepository taskRepository;
private TaskExplorer taskExplorer;
private PlatformTransactionManager transactionManager;
#Autowired
public DataloadTaskConfigurer(#Qualifier("scdfJdbcTemplate") JdbcTemplate jdbcTemplate) {
this.dataSource = jdbcTemplate.getDataSource();
TaskExecutionDaoFactoryBean taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(dataSource);
this.taskRepository = new SimpleTaskRepository(taskExecutionDaoFactoryBean);
this.taskExplorer = new SimpleTaskExplorer(taskExecutionDaoFactoryBean);
}
#Override
public TaskRepository getTaskRepository() {
return this.taskRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() {
if (this.transactionManager == null) {
this.transactionManager = new DataSourceTransactionManager(this.dataSource);
}
return this.transactionManager;
}
#Override
public TaskExplorer getTaskExplorer() {
return this.taskExplorer;
}
public DataSource getTaskDataSource() {
return this.dataSource;
}
}
TaskConfiguration.class
#Configuration
public class TaskConfiguration {
#Bean
public TaskRepositoryInitializer taskRepositoryInitializerInDataMart(#Qualifier("scdfJdbcTemplate") JdbcTemplate jdbcTemplate) throws Exception {
TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer();
taskRepositoryInitializer.setDataSource(jdbcTemplate.getDataSource());
taskRepositoryInitializer.afterPropertiesSet();
return taskRepositoryInitializer;
}
}
application.properties
spring.datasource.url=jdbc:postgresql://192.168.33.10/analytics?currentSchema=bahmni_mart_scdf
spring.datasource.username=analytics
spring.datasource.password=""
spring.datasource.driver-class-name=org.postgresql.Driver
spring.ds_mart.url=jdbc:postgresql://192.168.33.10/analytics
spring.ds_mart.username=analytics
spring.ds_mart.password=""
spring.ds_mart.driver-class-name=org.postgresql.Driver

You'll need to not override the TaskRepositoryInitializer, but turn it off all together via spring.cloud.task.initialize.enable=false. From there, using Spring Boot, you'll want to pass in the schema for each datasource:
spring.datasource.schema=schema1.sql
spring.ds_mart.schema=schema2.sql

The problem was with the postgresql driver version. Once I update to latest version (42.2.2) everything working fine as expected

Related

multiple spring boot mongo connectors SpelEvaluationException error

I'm get this error while setting up a spring boot connecting to multiple mongo database (so custom mongoTemplate config). I'm using SpelExpression in document to dynamically determine collection name at runtime.
org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean 'prefixSupplier'
Here's my config:
#SpringBootApplication(exclude = {
MongoReactiveAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class})
public class MyApp {...}
#Configuration
public class MultipleMongoConfiguration {
#Primary
#Bean(name = "db1Properties")
#ConfigurationProperties(prefix = "mongodb.db1")
public MongoProperties getDb1Props() {
return new MongoProperties();
}
#Bean(name = "db2Properties")
#ConfigurationProperties(prefix = "mongodb.db2")
public MongoProperties getDb2Props() {
return new MongoProperties();
}
#Primary
#Bean
public ReactiveMongoDatabaseFactory db1MongoDatabaseFactory(#Qualifier("db1Properties") MongoProperties mongo) {
return new SimpleReactiveMongoDatabaseFactory(
MongoClients.create(mongo.getUri()), mongo.getDatabase()
);
}
#Bean
public ReactiveMongoDatabaseFactory db2MongoDatabaseFactory(#Qualifier("db2Properties") MongoProperties mongo) {
return new SimpleReactiveMongoDatabaseFactory(
MongoClients.create(mongo.getUri()), mongo.getDatabase()
);
}
#Primary
#Bean(name = "db1MongoTemplate")
public ReactiveMongoTemplate db1MongoTemplate() {
return new ReactiveMongoTemplate(db1MongoDatabaseFactory(getDb1Props()));
}
#Bean(name ="db2MongoTemplate")
public ReactiveMongoTemplate db2MongoTemplate() {
return new ReactiveMongoTemplate(db2MongoDatabaseFactory(getDb2Props()));
}
}
#Configuration
#EnableReactiveMongoRepositories(basePackages = {
"com.demo.database.repository.db1",
"com.demo.entity.db1"
}, reactiveMongoTemplateRef = "db1MongoTemplate")
public class Db1Configuration {
}
#Value
#Builder
#AllArgsConstructor(access = PRIVATE)
#Document(collection = "#{#prefixSupplier.get().getIdInLowerCase()}_collection")
public class Entity1 {...}
Any idea what i'm missing?
thanks
Was able to fix the issue adding this config:
#Primary
#Bean(name = "productdbMongoTemplate")
public MongoTemplate productdbMongoTemplate(ApplicationContext appContext, MongoCustomConversions customConversions) {
MongoDatabaseFactory dbFactory = productdbMongoDatabaseFactory(getProductDbProps());
MongoTemplate mongoTemplate = new MongoTemplate(dbFactory);
MongoMappingContext mappingContext = (MongoMappingContext) mongoTemplate.getConverter().getMappingContext();
mappingContext.setApplicationContext(appContext);
MappingMongoConverter mmc = (MappingMongoConverter)mongoTemplate.getConverter();
mmc.setCustomConversions(customConversions);
mmc.afterPropertiesSet();
return mongoTemplate;
}

jOOQ with Spring Boot not inserting entity

I faced an issue with jOOQ not inserting entities unless the repository is annotated with #Transactional.
Here's my configuration:
#Configuration
#EnableTransactionManagement
#RequiredArgsConstructor
public class PersistenceConfig {
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUser;
#Value("${spring.datasource.password}")
private String dbPassword;
#Bean
#SneakyThrows
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
// https://mariadb.com/kb/en/about-mariadb-connector-j/
config.setDriverClassName(DatabaseDriver.MARIADB.getDriverClassName());
config.setJdbcUrl(dbUrl);
config.setUsername(dbUser);
config.setPassword(dbPassword);
config.setAutoCommit(false);
// https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
config.addDataSourceProperty("cacheServerConfiguration", true);
config.addDataSourceProperty("useServerPrepStmts", true);
config.addDataSourceProperty("useLocalSessionState", true);
config.addDataSourceProperty("cacheResultSetMetadata", true);
config.addDataSourceProperty("rewriteBatchedStatements", true);
config.addDataSourceProperty("elideSetAutoCommits", true);
config.addDataSourceProperty("maintainTimeStats", false);
config.addDataSourceProperty("cachePrepStmts", true);
config.addDataSourceProperty("prepStmtCacheSize", 350);
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
return new HikariDataSource(config);
}
#Bean
public TransactionAwareDataSourceProxy transactionAwareDataSource() {
return new TransactionAwareDataSourceProxy(dataSource());
}
#Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
public DataSourceConnectionProvider connectionProvider() {
return new DataSourceConnectionProvider(transactionAwareDataSource());
}
#Bean
public ExceptionTranslator exceptionTransformer() {
return new ExceptionTranslator();
}
#Bean
public SpringTransactionProvider springTransactionProvider() {
return new SpringTransactionProvider(transactionManager());
}
#Bean
public DefaultConfiguration configuration() {
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
jooqConfiguration.set(connectionProvider());
jooqConfiguration.set(new DefaultExecuteListenerProvider(exceptionTransformer()));
jooqConfiguration.set(SQLDialect.MARIADB);
jooqConfiguration.set(springTransactionProvider());
return jooqConfiguration;
}
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
#Bean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(transactionManager());
}
private static class ExceptionTranslator extends DefaultExecuteListener {
public void exception(ExecuteContext context) {
SQLDialect dialect = context.configuration().dialect();
SQLExceptionTranslator translator
= new SQLErrorCodeSQLExceptionTranslator(dialect.name());
context.exception(translator
.translate("Access database using jOOQ", context.sql(), context.sqlException()));
}
}
}
repository:
#Repository
public class UserRepository extends UserDao {
private final DSLContext dslContext;
public UserRepository(DSLContext dslContext) {
super(dslContext.configuration());
this.dslContext = dslContext;
}
}
So, calling userRepository.insert(...) doesn't actually insert into the database although the logs say that the following:
org.jooq.tools.LoggerListener : Executing query : insert into `user` (...)
org.jooq.tools.LoggerListener : -> with bind values : insert into `user` ...
However, If I overload UserDao's insert method and annotate it with #Transacational - it works, the rows actually get inserted. I suppose I have misconfigured something.
Spring Boot with jOOQ boot starter is used.
The problem is actually with setAutoCommit(false).

Java Library that uses Spring Data

i´m going to write a library that does some stuff and uses spring data.
The idea is that projects which uses this library can import this jar and use this library: MyLib.doSomeStuff().
It is possible to use Spring in this way and how can i initialize the ApplicationContext within the doSomeStuff() method, so that DI and the #Configuration Classes with the DataSources will be loaded?
public class MyLib {
#Autowired
private static SomeJpaRepository someJpaRepository;
public static void doSomeStuff(){
...init ApplicationContext....
...setup DataSources...
List<SomeEntity> someEntityList = someJpaRepository.someMethod();
}
// or
public static List<SomeEntity> getSomeEntityList() {
return someJpaRepository.finAll();
}
}
//other package
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "gxEntityManager", transactionManagerRef = "gxTransactionManager",
basePackages = "com.gx")
public class GxConfig {
#Primary
#Bean(name = "gxDataSource")
public DataSource gxDataSource() {
DataSourceBuilder dataSourceBuilderGx = null;
//..
return dataSourceBuilderGx.build();
}
#Primary
#Bean(name = "gxEntityManager")
public LocalContainerEntityManagerFactoryBean gxEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(gxDataSource()).packages("com.gx").build();
}
#Primary
#Bean(name = "gxTransactionManager")
public PlatformTransactionManager gxTransactionManager(
#Qualifier("gxEntityManager") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
//other package
public interface SomeEntity extends JpaRepository<SomeEntity, Long>
{
SomeEntity findById(Long id);
}
If you have a root configuration class it can be as simple as
ApplicationContext context =
new AnnotationConfigApplicationContext(GxConfig.class);
Just don't do it every time you call doStuff() as creating an application context is expensive. If you library is meant to be used as a black box, I guess it's ok to have this isolated application context.
You can do something like this:
public class MyLib {
private ApplicationContext context;
public MyLib() {
context = new AnnotationConfigApplicationContext(GxConfig.class);
}
public void doStuff() {
SomeBean bean = context.getBean(SomeBean.class);
// do something with the bean
}
}

Unable to autowire service in extended java class, throwing NullPointerException

I'm trying to Autowire service inside java class which is extended from TimerTask. This Returning null value while trying to return service in java class. This is the class in which I'm trying to Autowire service:
#Component
public class Task extends TimerTask
{
#Autowired
FileDetailsService fileDetailsService;
int count = 1;
#Override
public void run()
{
fileDetailsService.updateProcessingStatus(fileAudit);
}
Configuration classes: There is no web.xml.....I have configured in java using spring 4
//DataConfig.java
#Configuration
#MapperScan("com.fileC.mapper")
public class DataConfig {
#Bean
public DataSource dataSource() {
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(oracle.jdbc.driver.OracleDriver.class);
dataSource.setUsername("username");
dataSource.setUrl("jdbc***thin**sample **url");
dataSource.setPassword("****");
return dataSource;
}
#Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setTypeAliasesPackage("com.fileC.model");
return sessionFactory;
}
//ApplConfig.java
#Configuration
#ComponentScan(basePackages="com.filec")
public class ApplConfig {
#Bean
public CommonsMultipartResolver multipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setDefaultEncoding("utf-8");
commonsMultipartResolver.setMaxUploadSize(50000000);
return commonsMultipartResolver;
}
}
I'm using Spring4, java1.8, ibatis, SQL database.
Here is the service class,
#Service("fileDetailsService")
#Transactional
public class FileDetailsServiceImpl implements FileDetailsService{
private static Logger logger = LoggerFactory.getLogger(FileDetailsServiceImpl.class);
#Autowired
FileDetailsMapper fileDetailsMapper;
#Override
public void insertFileInfo(Details details){
fileDetailsMapper.insertDetails(details);
}
Here is the exception details,
fileAuditMapper>>>>null
Exception in thread "Timer-9" java.lang.NullPointerException at com.filecompare.service.Task.run(Task.java:117)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Please, let me know if I need to add something in config.

Dynamic datasource routing - DataSource router not initialized

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

Categories