How do you use both Spring Data JPA and Spring Data Elasticsearch repositories on the same domain class in a Spring Boot application? - spring-boot

I'm trying to use both Spring Data JPA and Spring Data Elasticsearch on the same domain object but it doesn't work.
When I tried to run a simple test, I got the following exception:
org.springframework.data.mapping.PropertyReferenceException: No
property index found for type Person! at
org.springframework.data.mapping.PropertyPath.(PropertyPath.java:75)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:270)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:241)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.Part.(Part.java:76)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree$OrPart.(PartTree.java:235)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:373)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree$Predicate.(PartTree.java:353)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree.(PartTree.java:84)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.PartTreeJpaQuery.(PartTreeJpaQuery.java:61)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:95)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:206)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:73)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.(RepositoryFactorySupport.java:408)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:206)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] ... 43 common frames
omitted
They work when disabling either one.
The project is based on Spring Boot 1.3.0.M5.
This is a sample project reproducing the situation:
https://github.com/izeye/spring-boot-throwaway-branches/tree/data-jpa-and-elasticsearch

Repositories in Spring Data are datasource agnostic, meaning that JpaRepository and ElasticsearchRepository both roll up into Repository interface. When this is the case, then auto-configuration of Spring Boot will cause Spring Data JPA to try and configure a bean for each repository in the project that inherits any Spring Data Commons base repository.
To fix this problem you need to move your JPA repository and Elasticsearch repository to separate packages and make sure to annotate your #SpringBootApplication application class with:
#EnableJpaRepositories
#EnableElasticsearchRepositories
Then you need to specify where the repositories are for each enable annotation. This ends up looking like:
#SpringBootApplication
#EnableJpaRepositories("com.izeye.throwaway.data")
#EnableElasticsearchRepositories("com.izeye.throwaway.indexing")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Then your application will be able to disambiguate which repositories are intended for which Spring Data project.

You can use like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
#EnableElasticsearchRepositories(includeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
public class DataConfiguration {
...
}
Or in SpringBoot:
#SpringBootApplication
#EnableJpaRepositories(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
#EnableElasticsearchRepositories(includeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
public class MyApplication {
...
}

Related

CucumberBackendException: No qualifying bean of type

When trying to run step defs with abstract class contains all the context configuration spring sees 2 differnt beans parent and stepdef
I'm using spring boot version: 2.6.4 , with Junit 5 and cucumber version 7.2.3
#SpringBootTest(classes = CoreApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes = AbstractIntegrationTest.Config.class)
#CucumberContextConfiguration
public abstract class AbstractIntegrationTest implements En {}
#Suite
#IncludeEngines("cucumber")
#SelectClasspathResource("com/example/bdd")
#ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example.bdd")
public class CucumberIntegrationTest {
}
public class MyStepdefs extends AbstractIntegrationTest{
public MyStepdefs() {
When("^client post to \"([^\"]*)\" with valid data$", (String arg0) -> {
});
Then("^the client receives status code of (\\d+)$", (Integer arg0) -> {
});
}
}
Exception Stack trace:
io.cucumber.core.backend.CucumberBackendException: No qualifying bean of type 'com.orange.ces.core.bdd.AbstractIntegrationTest' available: expected single matching bean but found 2: com.orange.ces.core.bdd.MyStepdefs,com.orange.ces.core.bdd.AbstractIntegrationTest
at io.cucumber.spring.TestContextAdaptor.notifyTestContextManagerAboutAfterTestMethod(TestContextAdaptor.java:124)
at io.cucumber.spring.TestContextAdaptor.stop(TestContextAdaptor.java:107)
at io.cucumber.spring.SpringFactory.stop(SpringFactory.java:161)
at io.cucumber.core.runner.Runner.disposeBackendWorlds(Runner.java:156)
at io.cucumber.core.runner.Runner.runPickle(Runner.java:78)
at io.cucumber.core.runtime.Runtime.lambda$executePickle$6(Runtime.java:128)
at io.cucumber.core.runtime.CucumberExecutionContext.lambda$runTestCase$3(CucumberExecutionContext.java:151)
at io.cucumber.core.runtime.RethrowingThrowableCollector.executeAndThrow(RethrowingThrowableCollector.java:23)
at io.cucumber.core.runtime.CucumberExecutionContext.runTestCase(CucumberExecutionContext.java:151)
at io.cucumber.core.runtime.Runtime.lambda$executePickle$7(Runtime.java:128)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at io.cucumber.core.runtime.Runtime$SameThreadExecutorService.execute(Runtime.java:249)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
at io.cucumber.core.runtime.Runtime.lambda$runFeatures$3(Runtime.java:110)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.SliceOps$1$1.accept(SliceOps.java:199)
at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1632)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at io.cucumber.core.runtime.Runtime.runFeatures(Runtime.java:111)
at io.cucumber.core.runtime.Runtime.lambda$run$0(Runtime.java:82)
at io.cucumber.core.runtime.Runtime.execute(Runtime.java:94)
at io.cucumber.core.runtime.Runtime.run(Runtime.java:80)
at io.cucumber.core.cli.Main.run(Main.java:87)
at io.cucumber.core.cli.Main.main(Main.java:30)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.orange.ces.core.bdd.AbstractIntegrationTest' available: expected single matching bean but found 2: com.orange.ces.core.bdd.MyStepdefs,com.orange.ces.core.bdd.AbstractIntegrationTest
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1271)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:494)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172)
at io.cucumber.spring.TestContextAdaptor.notifyTestContextManagerAboutAfterTestMethod(TestContextAdaptor.java:120)
... 30 more
In case you have several step definition classes, you can create a stub class with annotations only
#ContextConfiguration(classes = AbstractIntegrationTest.Config.class)
#CucumberContextConfiguration
public class StubConfig { }
Place it into a separate package and mention it in #CucumberOptions (if you have it)
Another ugly workaround:(

How can I fix bugs in Custom Repository?

I'm trying to make a Custom Repository with spring-data-jpa.
My codes
Repository
#NoRepositoryBean
interface CustomRepository<T, ID : Serializable> : JpaRepository<T, ID> {
fun findBySomeRestrict(id: ID): T?
}
RepositoryImpl
#Transactional(readOnly = true)
class CustomRepositoryImpl<T, ID : Serializable>(
private val entityManager: EntityManager,
jpaEntityInformation: JpaMetamodelEntityInformation<T, ID>
) : SimpleJpaRepository<T, ID>(jpaEntityInformation, entityManager), CustomRepository<T, ID> {
override fun findBySomeRestrict(id: ID): T? {
// TODO
}
}
Main class
#EnableJpaAuditing
#EnableJpaRepositories(repositoryBaseClass = CustomRepositoryImpl::class)
#SpringBootApplication
class ForSubmitApplication
fun main(args: Array<String>) {
runApplication<ForSubmitApplication>(*args)
}
MyErrorMessage
Caused by: java.lang.IllegalStateException: No suitable constructor found on interface com.example.forsubmit.global.custom.CustomRepository to match the given arguments: org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation, jdk.proxy2.$Proxy132. Make sure you implement a constructor taking these
at org.springframework.data.repository.core.support.RepositoryFactorySupport.lambda$instantiateClass$6(RepositoryFactorySupport.java:578) ~[spring-data-commons-2.6.0.jar:2.6.0]
at java.base/java.util.Optional.orElseThrow(Optional.java:403) ~[na:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport.instantiateClass(RepositoryFactorySupport.java:578) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getTargetRepositoryViaReflection(RepositoryFactorySupport.java:542) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:182) ~[spring-data-jpa-2.6.0.jar:2.6.0]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:164) ~[spring-data-jpa-2.6.0.jar:2.6.0]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:75) ~[spring-data-jpa-2.6.0.jar:2.6.0]
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:324) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:322) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.util.Lazy.getNullable(Lazy.java:230) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.util.Lazy.get(Lazy.java:114) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:328) ~[spring-data-commons-2.6.0.jar:2.6.0]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:144) ~[spring-data-jpa-2.6.0.jar:2.6.0]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.13.jar:5.3.13]
... 72 common frames omitted
What I do to fix it
Change the type of constructor to JpaEntityInformation
Decompile the code
It seems you only want to provide the implementation of one method.
In that case you must not set repositoryBaseClass.
Instead you want to follow the steps outlined in the documentation.
Create an interface just containing the custom method(s). You currently don't have that.
Create an implementation of it. You are currently implementing the final Repository interface which is wrong.
Extend your final repository interface from the custom interface created in step 1.

Spring data redis override default serializer

I am trying to create a RedisTemplate bean which will have the updated value serializer to serialize an object in JSON format in redis.
#Configuration
class RedisConfig {
#Bean(name = ["redisTemplate"])
#Primary
fun template(factory: RedisConnectionFactory): RedisTemplate<Any, Any> {
val template = RedisTemplate<Any, Any>()
template.connectionFactory = factory
template.valueSerializer = Jackson2JsonRedisSerializer(Object::class.java)
template.afterPropertiesSet()
return template
}
}
As per my understanding, spring should use the JSON serializer to serialize the object returned by the methods marked with Cacheable annotation. Despite this configuration, spring seems to be using the default Java serializer as this exception confirms this fact.
java.io.NotSerializableException: en.prateekj.vds.dto.Task
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at java.util.ArrayList.writeObject(ArrayList.java:766)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1128)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:46)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35)
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:94)
at org.springframework.data.redis.serializer.DefaultRedisElementWriter.write(DefaultRedisElementWriter.java:43)
at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.write(RedisSerializationContext.java:219)
at org.springframework.data.redis.cache.RedisCache.serializeCacheValue(RedisCache.java:238)
at org.springframework.data.redis.cache.RedisCache.put(RedisCache.java:144)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87)
at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:770)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:398)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:314)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
Am I missing any configuration or something by which spring is not able to determine what RedisTemplate to use?
You have probably solved it meanwhile, but for further answer seekers.
According to spring data redis reference:
By default, RedisCache and RedisTemplate are configured to use Java native serialization.
From stacktrace I can see that you are actually using Redis for caching, so you need to configure RedisCache and not RedisTemplate. RedisCache is not picking up your #Bean because it is not using RedisTemplate internally.
Example how you can do it in Java:
#EnableCaching
#Configuration
public class CacheConfig {
#Bean
#Primary
public RedisCacheConfiguration defaultCacheConfig(ObjectMapper objectMapper) {
return RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
}
}

Spring Boot Embedded Tomcat - No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3

I am working on my spring boot application and running with embedded tomcat 8.x. I am trying to configure three different oracle data sources using JNDI and followed this link. Below are my different files:
application-dev.properties
spring.oracle.datasource.oracleDS1.jndi-name=jdbc/oracleDS1
spring.oracle.datasource.oracleDS2.jndi-name=jdbc/oracleDS2
spring.oracle.datasource.oracleDS3.jndi-name=jdbc/oracleDS3
OracleDataSourceConfiguration
package com.adp.orbis.requesttracker.orbisrequesttracker;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
#Configuration
public class OracleDataSourceConfiguration {
#Value("${spring.oracle.datasource.oracleDS1.jndi-name}")
private String oracleDS1;
#Value("${spring.oracle.datasource.oracleDS2.jndi-name}")
private String oracleDS2;
#Value("${spring.oracle.datasource.oracleDS3.jndi-name}")
private String oracleDS3;
#Bean(name="dataSource1", destroyMethod = "")
#Profile("dev")
#Primary
public DataSource evolutionDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource(oracleDS1);
}
#Bean(name="dataSource2", destroyMethod = "")
#Profile("dev")
#Primary
public DataSource orbisQueryOnlyDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource(oracleDS2);
}
#Bean(name="dataSource3", destroyMethod = "")
#Profile("dev")
public DataSource orbisExportDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource(oracleDS3);
}
}
TomcatEmbeddedServletContainerFactory
#Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatEmbeddedServletContainer(tomcat);
}
#Override
protected void postProcessContext(Context context) {
ContextResource oracleDS1JNDIResource = new ContextResource();
oracleDS1JNDIResource.setName("jdbc/oracleDS1");
oracleDS1JNDIResource.setType(DataSource.class.getName());
oracleDS1JNDIResource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
oracleDS1JNDIResource.setProperty("url", "jdbc:oracle:thin:#localhost:1521/mydbservice1");
oracleDS1JNDIResource.setProperty("username", "db-user-name");
oracleDS1JNDIResource.setProperty("password", "db-user-pass");
oracleDS1JNDIResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
context.getNamingResources().addResource(oracleDS1JNDIResource);
ContextResource oracleDS2JNDIResource = new ContextResource();
oracleDS2JNDIResource.setName("jdbc/oracleDS2");
oracleDS2JNDIResource.setType(DataSource.class.getName());
oracleDS2JNDIResource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
oracleDS2JNDIResource.setProperty("url", "jdbc:oracle:thin:#localhost:1521/mydbservice2");
oracleDS2JNDIResource.setProperty("username", "db-user-name");
oracleDS2JNDIResource.setProperty("password", "db-user-pass");
oracleDS2JNDIResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
context.getNamingResources().addResource(oracleDS2JNDIResource);
ContextResource oracleDS3JNDIResource = new ContextResource();
oracleDS3JNDIResource.setName("jdbc/oracleDS3");
oracleDS3JNDIResource.setType(DataSource.class.getName());
oracleDS3JNDIResource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
oracleDS3JNDIResource.setProperty("url", "jdbc:oracle:thin:#localhost:1521/mydbservice3");
oracleDS3JNDIResource.setProperty("username", "db-user-name");
oracleDS3JNDIResource.setProperty("password", "db-user-pass");
oracleDS3JNDIResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
context.getNamingResources().addResource(oracleDS3JNDIResource);
}
};
}
But when I run this spring boot application, it throws below exception. Could you please help me what I am missing here? I tried adding #Primary with all DataSource bean. But no luck.
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: dataSource1,dataSource2,dataSource3
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: dataSource1,dataSource2,dataSource3
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1092) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init(DataSourceInitializer.java:77) ~[spring-boot-autoconfigure-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_112]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_112]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_112]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_112]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:134) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
... 38 common frames omitted
There should be atleast 1 bean with name dataSource. So either replace any one of the existing bean with name for exp dataSource1 to dataSource, or create a new one.
Trying to say, change
#Bean(name="dataSource1", destroyMethod = "")
#Profile("dev")
#Primary
public DataSource evolutionDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource(oracleDS1);
}
To
#Bean(name="dataSource", destroyMethod = "")
#Profile("dev")
#Primary
public DataSource evolutionDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource(oracleDS1);
}
OR Simply remove #Primary from any of the existing dataSource* beans. At most 1 bean can have #Primary under same #Profile
I am able to resolve this issue. The problem was, I had defined all data sources in my spring boot application and it was also importing dependent spring context file. That means, I had same data sources configured into my imported spring context file too. I removed all data source beans from OracleDataSourceConfiguration(as mentioned in my question on top) and used TomcatEmbeddedServletContainerFactory to initialize JNDI data sources (referred this) with the same name which are mentioned in my imported spring context file. It worked for me.
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDataSource" />

Mockito/Powermock not working as expected with Kotlin

I'm using mockito/powermock version 1.7.0, Spring Boot 2.0.0 M2, Kotlin 1.3.0.
I'm testing a Spring #Service class, that inject a Repository.
I want to mock that repository on my tests, so I can just test the class itself.
I'm new to Kotlin, but I come from Java, so I tried doing this "the java way", but probably I'm missing something...
Here is my Service interface:
interface MyService {
fun create(myObject: MyObject): MyObject
}
... and its implementation:
#Service
class DefaultMyService : MyService {
#Autowired
lateinit var myRepository: MyRepository
override fun create(myObject: MyObject): MyObject = this.myRepository.insert(myObject)
}
Here is my repository (I'm using Spring Data with Mongo):
interface MyRepository: MongoRepository<MyObject, String>
Here is my test class:
#RunWith(PowerMockRunner::class)
#PrepareForTest(MyService::class)
class MyServiceTest {
#Mock
lateinit var myRepository: MyRepository
#InjectMocks
lateinit var myService: MyService
#Test
fun shouldDoSomething() {
val myObject = MyObject(name = "Marco")
`when`(myRepository.insert(myObject)).thenReturn(myObject)
assertEquals(myObject.name, this.myService.create(myObject).name)
}
}
When I run this test, an exception occurs:
Exception in thread "main" java.lang.NoClassDefFoundError: org/mockito/exceptions/Reporter
at sun.reflect.GeneratedSerializationConstructorAccessor5.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
at org.powermock.reflect.internal.WhiteboxImpl.newInstance(WhiteboxImpl.java:260)
at org.powermock.reflect.Whitebox.newInstance(Whitebox.java:139)
at org.powermock.api.extension.reporter.AbstractMockingFrameworkReporterFactory.getInstanceForClassLoader(AbstractMockingFrameworkReporterFactory.java:41)
at org.powermock.api.extension.reporter.AbstractMockingFrameworkReporterFactory.create(AbstractMockingFrameworkReporterFactory.java:35)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.getMockingFrameworkReporter(JUnit4TestSuiteChunkerImpl.java:140)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:119)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:202)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass1(DeferSupportingClassLoader.java:89)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:79)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 16 more
What am I doing wrong? I think it's an issue with some configuration, but I can't understand exactly where the mistake is.
It seems like that your mockito and powermock versions are not working very well together. You have to use versions which are compatible. See the compatibility matrix of powermock https://github.com/powermock/powermock/wiki/Mockito#supported-versions
Latest compatible is Mockito: 2.8.x with PowerMock: 1.7.0

Resources