Want to configure Job with out DataSource - spring-boot

I am able to run my Springbatch application when Datasource was configured in my SpringbatchConfiguration class. But i dont want Datasource to be configured. SO i used ResourcelessTransactionManager. See below my configuration class. Some one guide me how i can launch Jobs without configuring Datasource as part of Batchjob configurations.
#Configuration
#EnableBatchProcessing
#EnableAutoConfiguration
//#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class SprintgBatchConfiguration {
/*#Autowired
private DBConfiguration dbConfig;*/
/*#Autowired
private DataSource dataSource;
#Autowired
private DataSourceTransactionManager transactionManager;
*/
//Tomcat relaated configuration//
#Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize("124MB");
factory.setMaxRequestSize("124MB");
return factory.createMultipartConfig();
}
#Bean(name="csvjob")
public Job job(JobBuilderFactory jobBuilderFactory,StepBuilderFactory stepBuilderFactory,ItemReader<List<CSVPojo>> itemReader,ItemProcessor<List<CSVPojo>,CsvWrapperPojo> itemProcessor,AmqpItemWriter<CsvWrapperPojo> itemWriter){
Step step=stepBuilderFactory.get("ETL-CSV").<List<CSVPojo>,CsvWrapperPojo>chunk(100)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
Job csvJob= jobBuilderFactory.get("ETL").incrementer(new RunIdIncrementer())
.start(step).build();
return csvJob;
}
#Bean(name="exceljob")
public Job jobExcel(JobBuilderFactory jobBuilderFactory,StepBuilderFactory stepBuilderFactory,ItemReader<List<ExcelPojo>> itemReader,ItemProcessor<List<ExcelPojo>,ExcelWrapperPojo> itemProcessor,AmqpItemWriter<ExcelWrapperPojo> itemWriter){
Step step=stepBuilderFactory.get("ETL-Excel").<List<ExcelPojo>,ExcelWrapperPojo>chunk(100)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
Job ExcelJob= jobBuilderFactory.get("ETL-Excel").incrementer(new RunIdIncrementer())
.start(step).build();
return ExcelJob;
}
/*#Override
public void setDataSource(DataSource dataSource){
System.out.println("overriden");
}*/
/*#Bean
public FlatFileItemReader<CSVPojo> fileItemReader(Resource resource){
return null;
}*/
/*#Bean(name="dataSource")
public DataSource dataSource() throws SQLException
{
//BasicDataSource dataSource = new BasicDataSource();
return dataSource;
}*/
#Bean(name="transactionManager")
public ResourcelessTransactionManager transactionManager() throws SQLException{
return new ResourcelessTransactionManager();
}
/*#Bean(name="transactionManager")
public DataSourceTransactionManager transactionManager() throws SQLException{
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource());
return transactionManager;
}*/
/*#Bean
public JobRepository jobRepository() throws Exception{
JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean();
factoryBean.setDatabaseType("ORACLE");
factoryBean.setDataSource(dataSource);
factoryBean.setTransactionManager(transactionManager);
factoryBean.setIsolationLevelForCreate("ISOLATION_READ_UNCOMMITTED");
return factoryBean.getObject();
}*/
#Bean
public JobRepository jobRepository(ResourcelessTransactionManager transactionManager) throws Exception {
MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean = new MapJobRepositoryFactoryBean(transactionManager);
mapJobRepositoryFactoryBean.setTransactionManager(transactionManager);
return mapJobRepositoryFactoryBean.getObject();
}
}
But i am getting below exception when i am running Application.
ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fileProcessController': Unsatisfied dependency expressed through field 'jobLauncher'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration': Unsatisfied dependency expressed through field 'dataSources'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
Thanks in advance!!!!

Spring Boot is intended for building production grade applications. When it is used to build a Spring Batch application, it requires a data source to persist Spring Batch meta-data (See BATCH-2704).
But you can always use either:
an embedded datasource supported by Spring Boot (H2, HSQL or Derby) by just adding it to the classpath. This data source will be picked up automatically by Spring Batch
or provide a custom BatchConfigurer and use the MapJobRepository (See here)
Hope this helps.

Related

How to test complete IntegrationFlow from MessagingGateway to ServiceActivator

In our Spring Boot project we have the following IntegrationFlow configuration
package our.configs;
#Configuration
#EnableIntegration
public class MessageChannelsConfiguration {
public static final String OUTBOUND_CHANNEL = "outboundChannel";
public static final String OUTBOUND_CHANNEL_GROUP_ID = "outboundMessageGroup";
#Bean
IntegrationFlow outboundSnapshotMessageChannel(ChannelMessageStore outboundChannelMessageStore,
OutboundFixMessageService outboundMessageService) {
return f -> f
.channel(c -> c.queue(
OUTBOUND_CHANNEL,
outboundChannelMessageStore,
OUTBOUND_CHANNEL_GROUP_ID))
.handle(outboundMessageService, "processOutboundMessage");
}
#Bean
OutboundMessageService outboundFixMessageService(ObjectMapper objectMapper){
return new OutboundMessageService(objectMapper);
}
#Bean
ChannelMessageStore outboundChannelMessageStore(#Qualifier("dataSource") DataSource dataSource,
ChannelMessageStoreQueryProvider channelMessageStoreQueryProvider) {
JdbcChannelMessageStore jdbcChannelMessageStore = new JdbcChannelMessageStore(dataSource);
jdbcChannelMessageStore.setChannelMessageStoreQueryProvider(channelMessageStoreQueryProvider);
jdbcChannelMessageStore.setRegion("TX_TIMEOUT");
return jdbcChannelMessageStore;
}
#Bean
#Profile({"test"})
ChannelMessageStoreQueryProvider jdbcChannelMessageStoreQueryProvider() {
return new H2ChannelMessageStoreQueryProvider();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller(TransactionManager transactionManager,
Initiator clientInitiator) {
return Pollers.fixedRate(500))
.maxMessagesPerPoll(1)
.advice(transactionInterceptor(transactionManager), new CheckSessionPollingAdvise(clientInitiator))
.get();
}
private TransactionInterceptor transactionInterceptor(TransactionManager transactionManager) {
return new TransactionInterceptorBuilder()
.transactionManager(transactionManager)
.propagation(Propagation.NESTED)
.build();
}
}
and the messaging Gateway which is defined as in a separate package then the above configuration
package our.businesslogic;
#MessagingGateway
public interface OutboundMessageGateway {
#Gateway(requestChannel = MessageChannelsConfiguration.OUTBOUND_CHANNEL)
void sendMarkerMessage(Object markerMessage,
#Header(ChannelMessageHeaders.RECIPIENT_ID) String institutionId,
#Header(ChannelMessageHeaders.MESSAGE_TYPE) ChannelMessageType channelMessageType);
#Gateway(requestChannel = MessageChannelsConfiguration.OUTBOUND_CHANNEL)
void sendOrderMessage(Order message,
#Header(ChannelMessageHeaders.RECIPIENT_ID) String institutionId,
#Header(ChannelMessageHeaders.MESSAGE_TYPE) ChannelMessageType channelMessageType);
}
I want to test the behavior of the complete flow including the persistence to the JdbcChannelMessageStore (and later also the transactional scenarios) with JUnit5.
E.g.
#Test
void whenSendTrancheMessage_givenPollingBlockedByAdvise_thenCorrectNumberOfMessagesOnQueue() {
//given
String recipientId = "Mocked-recipient";
List<Order> orders = Arrays.asList(
new Order(),
new Order(),
new Order(),
new Order()
);
//when
clientInitiator.stopConnection(); // Queue will not be read as long as
// there is no connection
orders.forEach(order->
outboundMessageGateway.sendOrderMessage(order,recipientId,ChannelMessageType.SNAPSHOT));
//then
Assertions.assertThat(outboundChannelMessageStore.messageGroupSize(FixMessageChannelsConfiguration.OUTBOUND_CHANNEL_GROUP_ID))
.isEqualTo(orders.size());
}
#Test
void whenSendTrancheMessage_givenPollingIsNotBlocked_thenMessagesAreReceivedByHandler() {
//some test code with mocked ServiceActivator
}
I have tried with two different ways
as an Integration test with #SpringBootTest
as a context specific JUnit test with #ContextConfiguration and #SpringIntegrationTest
In case of 1) my tests are working when called separately, but are failing with the following exception when they are run together with existing integration tests
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class p
ath resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation vi
a factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servl
et.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.h2.jdbc.JdbcSQLNonTransientConnection
Exception: Exception opening port "9092" (port may be in use), cause: "java.net.BindException: Address already in use: JVM_Bind" [900
61-199]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:483)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowir
eCapableBeanFactory.java:1336)
In case of 2) the following exception is thrown showing problems with the outboundMessageGateway
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
... ... ...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'outboundMessageGateway': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [our.businesslogic.OutboundMessageGateway]: Specified class is an interface
I would appreciate it very much if someone could help me to solve this issues.
Exception opening port "9092"
You probably need to add a #DirtiesContext along side with the mentioned #SpringBootTest.
Failed to instantiate [our.businesslogic.OutboundMessageGateway]
If you don't use Spring Boot in your test environment, you must be explicit #EnableIntegration and #IntegrationComponentScan must be there to Spring Integration infrastructure available.
See docs for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/overview.html#configuration-enable-integration
Assertions.assertThat(outboundChannelMessageStore.messageGroupSize(FixMessageChannelsConfiguration.OUTBOUND_CHANNEL_GROUP_ID))
.isEqualTo(orders.size());
You can't check this if your handle(outboundMessageService, "processOutboundMessage") is not stopped. Since you have a poller for the queue channel, it is going to pull all the message from the store and lets that handler to process them. So, there is going to be nothing in the store at the moment you try to verify it (or some wrong unexpected number).

SpringBootTest, Testcontainers, container start up - Mapped port can only be obtained after the container is started

I am using docker/testcontainers to run a postgresql db for testing. I have effectively done this for unit testing that is just testing the database access. However, I have now brought springboot testing into the mix so I can test with an embedded web service and I am having problems.
The issue seems to be that the dataSource bean is being requested before the container starts.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/myproject/integrationtests/IntegrationDataService.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Caused by: java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Here is my SpringBootTest:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {IntegrationDataService.class, TestApplication.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringBootTestControllerTesterIT
{
#Autowired
private MyController myController;
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testRestControllerHello()
{
String url = "http://localhost:" + port + "/mycontroller/hello";
ResponseEntity<String> result =
restTemplate.getForEntity(url, String.class);
assertEquals(result.getStatusCode(), HttpStatus.OK);
assertEquals(result.getBody(), "hello");
}
}
Here is my spring boot application referenced from the test:
#SpringBootApplication
public class TestApplication
{
public static void main(String[] args)
{
SpringApplication.run(TestApplication.class, args);
}
}
Here is the IntegrationDataService class which is intended to startup the container and provide the sessionfactory/datasource for everything else
#Testcontainers
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#EnableTransactionManagement
#Configuration
public class IntegrationDataService
{
#Container
public static PostgreSQLContainer postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer("postgres:9.6")
.withDatabaseName("test")
.withUsername("sa")
.withPassword("sa")
.withInitScript("db/postgresql/schema.sql");
#Bean
public Properties hibernateProperties()
{
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
hibernateProp.put("hibernate.format_sql", true);
hibernateProp.put("hibernate.use_sql_comments", true);
// hibernateProp.put("hibernate.show_sql", true);
hibernateProp.put("hibernate.max_fetch_depth", 3);
hibernateProp.put("hibernate.jdbc.batch_size", 10);
hibernateProp.put("hibernate.jdbc.fetch_size", 50);
hibernateProp.put("hibernate.id.new_generator_mappings", false);
// hibernateProp.put("hibernate.hbm2ddl.auto", "create-drop");
// hibernateProp.put("hibernate.jdbc.lob.non_contextual_creation", true);
return hibernateProp;
}
#Bean
public SessionFactory sessionFactory() throws IOException
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setHibernateProperties(hibernateProperties());
sessionFactoryBean.setPackagesToScan("com.myproject.model.entities");
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() throws IOException
{
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public DataSource dataSource()
{
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(postgreSQLContainer.getDriverClassName());
dataSource.setUrl(postgreSQLContainer.getJdbcUrl());
dataSource.setUsername(postgreSQLContainer.getUsername());
dataSource.setPassword(postgreSQLContainer.getPassword());
return dataSource;
}
}
The error occurs on requesting the datasource bean from the sessionFactory from one of the Dao classes before the container starts up.
What the heck am I doing wrong?
Thanks!!!
The reason for your java.lang.IllegalStateException: Mapped port can only be obtained after the container is started exception is that when the Spring Context now gets created during your test with #SpringBootTest it tries to connect to the database on application startup.
As you only launch your PostgreSQL inside your IntegrationDataService class, there is a timing issue as you can't obtain the JDBC URL or create a connection on application startup as this bean is not yet properly created.
In general, you should NOT use any test-related code inside your IntegrationDataService class. Starting/stopping the database should be done inside your test setup.
This ensures to first start the database container, wait until it's up- and running, and only then launch the actual test and create the Spring Context.
I've summarized the required setup mechanism for JUnit 4/5 with Testcontainers and Spring Boot, that help you get the setup right.
In the end, this can look like the following
// JUnit 5 example with Spring Boot >= 2.2.6
#Testcontainers
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationIT {
#Container
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer()
.withPassword("inmemory")
.withUsername("inmemory");
#DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
}
#Test
public void contextLoads() {
}
}

spring-boot create bean only when action is called

I have some some spring configuration code which creates the spring bean
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
In some class I am using like below
#Autowired
private MongoTemplate mongoTemplate ;
Bean in getting created whenever spring is started but due to some some service I want to make sure bean should be created only when action is invoked on the object
like mongoTemplate.save etc
Proxy by CGLIB and lazy-initialization are available.
#Lazy
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/core.html#beans-factorybeans-annotations

Spring boot - Issue with multiple DataSource

I have a ReST service that needs to fetch data from two different DBs (Oracle and MySQL) and merge this data in the response.
I have below configuration.
Config for DB 1:
#Configuration
public class DbConfig_DB1{
#Bean(name="siebelDataSource")
public EmbeddedDatabase siebelDataSource(){
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("schema.sql").
addScript("test-data.sql").
build();
}
#Autowired
#Qualifier("siebelDataSource")
#Bean(name = "siebelJdbcTemplate")
public JdbcTemplate siebelJdbcTemplate(DataSource siebelDataSource) {
return new JdbcTemplate(siebelDataSource);
}
}
Config for DB2:
#Configuration
public class DbConfig_DB2{
#Bean(name="brmDataSource")
public EmbeddedDatabase brmDataSource(){
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("schema-1.sql").
addScript("test-data-1.sql").
build();
}
#Autowired
#Qualifier("brmDataSource")
#Bean(name = "brmJdbcTemplate")
public JdbcTemplate brmJdbcTemplate(DataSource brmDataSource) {
return new JdbcTemplate(brmDataSource);
}
}
Data Access:
#Repository
public class SiebelDataAccess {
protected final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier("siebelJdbcTemplate")
protected JdbcTemplate jdbc;
public String getEmpName(Integer id) {
System.out.println(jdbc.queryForObject("select count(*) from employee", Integer.class));
Object[] parameters = new Object[] { id };
String name = jdbc.queryForObject(
"select name from employee where id = ?", parameters,
String.class);
return name;
}
}
I am not able to start the app as I below error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.dataSource;
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: brmDataSource,siebelDataSource
The issue is with two DataSource beans in the context. How to resolve this?
You could mark one of them as #Primary so Spring Boot auto configuration for transactions manager would know which one to pick. If you need to manage transactions with both of them then I'm afraid you would have to setup transactions management explicitly.
Please refer to the Spring Boot documentation
Creating more than one data source works the same as creating the first one. You might want to mark one of them as #Primary if you are using the default auto-configuration for JDBC or JPA (then that one will be picked up by any #Autowired injections).

Spring Data: inject 2 repositories with same name but in 2 different packages

Context
I want to use in the same Spring context two different databases that have entities that share the same name, but not the same structure. I rely on Spring Data MongoDB and JPA/JDBC. I have two packages, containing among others the following files:
com.bar.entity
Car.class
com.bar.repository
CarRepository.class
RepoBarMarker.class
com.bar.config
MongoConfiguration.class
com.foo.entity
Car.class
com.foo.repository
CarRepository.class
RepoFooMarker.class
com.foo.config
JPAConfiguration.class
SpecEntityManagerFactory.class
The content of each Car.class is different, I cannot reuse them. bar uses Spring-Mongo and foo uses Spring-JPA, and repositories are initialised via #EnableMongoRepositories and #EnableJpaRepositories annotations. When in one of my application component I try to access the foo version of the repository:
#Resource
private com.foo.repository.CarRepository carRepository;
I have the following exception when the class containing the #Resource field is created:
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'carRepository' must be of type [com.foo.repository.CarRepository], but was actually of type [com.sun.proxy.$Proxy31]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:374)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:446)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:420)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:545)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:155)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:305)
... 26 more
It appears that Spring tries to convert a bar repository to a foo repository, instead of creating a new bean, as in the same stack I also have the following exception:
Caused by: java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy31 implementing com.bar.repository.CarRepository,org.springframework.data.repository.Repository,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.foo.repository.CarRepository]: no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:267)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93)
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
... 35 more
If I try instead to autowire the repository:
#Autowire
private com.foo.repository.CarRepository carRepository;
I get the following exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.foo.CarRepository com.shell.ShellApp.carRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.foo.CarRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:509)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:290)
... 26 more
Spring-data configuration
In foo (JPA) package, JPAConfigration.class:
#Configuration
#EnableJpaRepositories(basePackageClasses = RepoFooMarker.class)
public class JPAConfiguration {
#Autowired
public DataSource dataSource;
#Autowired
public EntityManagerFactory entityManagerFactory;
#Bean
public EntityManager entityManager(final EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public Session session(final EntityManager entityManager)
{
return entityManager.unwrap(Session.class);
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
final JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
SpecEntityManagerFactory.class:
#Configuration
public class SpecEntityManagerFactory {
#Bean
public EntityManagerFactory entityManagerFactory(final DataSource dataSource) throws SQLException {
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setDatabase(Database.POSTGRESQL);
final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.foo.entity");
factory.setJpaProperties(getHibernateProperties());
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
private Properties getHibernateProperties()
{
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
return hibernateProperties;
}
}
In bar (MongoDB) package, MongoConfiguration.class:
#Configuration
#EnableMongoRepositories(basePackageClasses = RepoBarMarker.class)
public class MongoConfiguration extends AbstractRepoConfig {
#Override
#Bean
public MongoOperations mongoTemplate() {
final MongoClient mongo = this.getMongoClient();
final MongoClientURI mongoUri = this.getMongoClientUri();
final MongoTemplate mongoTemplate = new MongoTemplate(mongo, mongoUri.getDatabase());
mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());
mongoTemplate.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
return mongoTemplate;
}
}
Question
If I change in foo repository the entity name to CarFoo.class and the repository to CarFooRepository.class, then everything works. But is there away to avoid renaming them and still have a real wiring per type, instead of name (as it is what seems to be done here), for Spring Data repositories?
In your case, you can use
#Repository("fooCarRepository")
on the interface declaration of
com.foo.repository.CarRepository
Although when using Spring Data #Repository is not generally needed on the interface, however in your case you need to supply it. That's because you need to make Spring register the implementation of the bean with a custom name (in this case fooCarRepository) in order to avoid the name collision.

Resources