multiple spring boot mongo connectors SpelEvaluationException error - java

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;
}

Related

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).

spring-data - multiple DB: NoUniqueBeanDefinitionException No qualifying bean of type 'TransactionManager' available

I have created a spring-data project (version = 2.5.5) with two datasources GitHub. It works fine, as long, as I am using the interface CrudRepository.java. But when I am trying to implement my own DAO based on SimpleJpaRepository.java I am getting the error
No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: db1TransactionManager,db2TransactionManager
DB2Config.java (DB1Config.java is the same but instead of '2' the bean names has '1')
#Configuration
#PropertySource("classpath:db2-config.properties")
#EnableJpaRepositories(
basePackages = "com.vscoding.jpa.db2.entity",
entityManagerFactoryRef = "db2EntityManagerFactory",
transactionManagerRef = "db2TransactionManager"
)
public class DB2Config {
#Bean
#ConfigurationProperties(prefix = "db2.datasource")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public FactoryBean<EntityManagerFactory> db2EntityManagerFactory(#Qualifier("db2DataSource") DataSource db2DataSource, Environment env){
var em = new LocalContainerEntityManagerFactoryBean();
var va = new HibernateJpaVendorAdapter();
var properties = new HashMap<String,Object>();
properties.put("hibernate.hbm2ddl.auto","create");
em.setDataSource(db2DataSource);
em.setPackagesToScan("com.vscoding.jpa.db2.entity");
em.setJpaVendorAdapter(va);
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public PlatformTransactionManager db2TransactionManager(#Qualifier("db2EntityManagerFactory") FactoryBean<EntityManagerFactory> db2EntityManagerFactory) throws Exception {
var tm = new JpaTransactionManager();
tm.setEntityManagerFactory(db2EntityManagerFactory.getObject());
return tm;
}
#Bean
#Profile("with-init")
public DataSourceInitializer dataSourceInitializer2(#Qualifier("db2DataSource") DataSource datasource) {
var populator = new ResourceDatabasePopulator();
populator.addScript(new ClassPathResource("db2.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(datasource);
dataSourceInitializer.setDatabasePopulator(populator);
return dataSourceInitializer;
}
}
ProductCustomRepository.java
#Repository
#Transactional(transactionManager = "db2TransactionManager")
public class ProductCustomRepository extends SimpleJpaRepository<ProductEntity2, Integer> {
private final EntityManager entityManager;
public ProductCustomRepository(#Qualifier("db2EntityManagerFactory") EntityManager entityManager) {
super(ProductEntity2.class, entityManager);
this.entityManager = entityManager;
}
public List<ProductEntity2> customQuery() {
var query = entityManager.createQuery("SELECT p FROM ProductEntity2 p WHERE p.name='special'",ProductEntity2.class);
return query.getResultList();
}
}
I would expect, that #Transactional(transactionManager = "db2TransactionManager") would select the right transactionManager, but maybe I am missing something.
ProductCustomRepositoryTest.java (test to reproduce the error)
#SpringBootTest
class ProductCustomRepositoryTest {
#Autowired
private ProductCustomRepository sut;
#Test
void customQuery() {
//Given
var special = new ProductEntity2("special");
sut.save(special);
//When
var result = sut.customQuery();
//Then
assertTrue(result.contains(special));
}
}
Thanks in advance.
As mentioned by Simon, I could solve the error, by following https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
Changed my ProductCustomRepository.javato CustomRepositoryImpl.java and added Interface CustomRepository.java
#Repository
#Transactional(transactionManager = "db2TransactionManager")
public class CustomRepositoryImpl implements CustomRepository {
// no changes here
}
Then extend my main Repository with the interface.
public interface ProductRepository2 extends CrudRepository<ProductEntity2,Integer>,CustomRepository {
}

TaskConfigurer does not considering schema name for datasource

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

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.

(Spring MVC + Hibernate 4 + Test 4) autowired DAO return NULL

Maven Dependencies
<!-- SPRING MVC -->
<spring-version>4.0.3.RELEASE</spring-version>
<spring-test-version>4.2.1.RELEASE</spring-test-version>
<!-- TESTS -->
<junit-Version>4.11</junit-Version>
<!-- DATA BASE -->
<hibernate.version>4.1.5.Final</hibernate.version>
<hibernate-validator>4.2.0.Final</hibernate-validator>
<mysql.connector.version>5.1.32</mysql.connector.version>
AccountDao.java
#Repository
#Transactional
public class AccountDao {
private final SessionFactory sessionFactory;
#Inject
public AccountDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
[...]
}
Test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringMVCInitializer.class}, loader=AnnotationConfigContextLoader.class)
public class LoadingTests extends TestMachine {
#Autowired
private AccountDao accountDao;
AppConfig.java
#Configuration
#PropertySource("classpath:application.properties")
#ComponentScan(basePackages = "company")
#EnableTransactionManagement
public class AppConfig {
#Bean
public AccountDao accountDao() {
return new AccountDao();
}
#Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
/*LOCAL*/
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/compamny");
dataSource.setUsername("root");
dataSource.setPassword("");
return dataSource;
}
#Bean(name = "sessionFactory")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan("insynctive.model");
sessionFactoryBean.setHibernateProperties(hibProperties());
return sessionFactoryBean;
}
#Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(Environment.HBM2DDL_AUTO, "create");
properties.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");
properties.put(Environment.SHOW_SQL, true);
return properties;
}
}
SpringMVCConfiguration.java
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="company.controller")
public class SpringMVCConfiguration extends WebMvcConfigurerAdapter {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolverJSP = new InternalResourceViewResolver();
viewResolverJSP.setOrder(1);
viewResolverJSP.setViewClass(JstlView.class);
viewResolverJSP.setPrefix("views/jsp/");
viewResolverJSP.setSuffix(".jsp");
return viewResolverJSP;
}
}
SpringMVCInitializer.java
public class SpringMVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AppConfig.class, SpringMVCConfiguration.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
My autowired of AccountDao is returning null Why? but in my web application everything works good.
I Try lot of things like:
Create a new sessionFactory but doesn't work.
Used: classes ={AppConfig.class,SpringMVCConfig.class}.
Used: #ContextConfiguration(initializers = {SpringMVCInitializer.class}, loader=AnnotationConfigContextLoader.class) throw Type mismatch: cannot convert from Class to Class>
Used: #SpringApplicationConfiguration(classes = {SpringMVCInitializer.class})
I think you need to set initializers instead of classes on your LoadingTests class.
#ContextConfiguration(initializers = {SpringMVCInitializer.class}, loader=AnnotationConfigContextLoader.class)
Also please consider using constructor injection on your DAO.
Since you have #Repository you should include them with
#EnableJpaRepositories(basePackages="yourrepositories") at your config class
try adding
#WebAppConfiguration
before you test class.
The mere presence of #WebAppConfiguration on a test class ensures that a
WebApplicationContext will be loaded for the test using a default for
the path to the root of the web application.
I resolved this making a HibernateUtils to create a sessionFactory.

Categories