Reputation: 3033
I'm trying to autowire repositories that are created by a repository factory bean. I need to override the save() method of certain repositories with custom code. I've followed the example for the factory bean at Spring Data's introduction.
But whatever I do, I'm getting NoSuchBeanDefinitionException
.
On the other hand, 'interface-only repositories' are created and injected just fine.
Very likely I'm making a silly mistake somewhere. As this is supposed to be Spring Data 101.
Can somebody give me a clue of what else I might to try to make it work ?
The specs
Hibernate 4.1.9.Final
Spring 3.2.1.RELEASE
Spring Data 1.3.0.RELEASE
JDK 1.6.0_39.
Arch Linux x64
The repository factory
In case the entity is of type Bar I'm returning a different repository. Otherwise the default behavior is applied.
public class RepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory(entityManager);
}
private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
public MyRepositoryFactory(final EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
protected Object getTargetRepository(final RepositoryMetadata metadata) {
if (metadata.getDomainType().equals(Bar.class)) {
return new BarDaoImpl((Class<Bar>) metadata.getDomainType(), entityManager);
}
return super.getTargetRepository(metadata);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
if (metadata.getDomainType().equals(Bar.class)) {
return BarDao.class;
}
return super.getRepositoryBaseClass(metadata);
}
}
}
The repository class and interface
package com.bar.persistence.dao;
@NoRepositoryBean
public interface BarDao extends JpaRepository<Bar, Long> {
// no methods - because I want to override save()
}
package com.bar.persistence.dao;
public class BarDaoImpl extends SimpleJpaRepository<Bar, Long> implements BarDao {
public BarDaoImpl(final Class<Bar> domainClass, final EntityManager em) {
super(domainClass, em);
}
@Override
public <S extends Bar> S save(final S entity) {
// some code to perform before actually saving the entity
entity.setName("intercepted save !");
// do the save
return super.save(entity);
}
}
The test injecting the repository
The test fails because it can't find a bean of type BarDao to inject.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/persistence-beans.xml")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@TransactionConfiguration
public class SpringDataLearningTest {
@Autowired
private BarDao barDao;
@Transactional
@Test
public void testModifiedSave() throws Exception {
final Bar original = createRandomBar();
final Bar saved = barDao.save(original);
assertNotNull("No id was assigned to the saved entity", saved.getId());
assertEquals("intercepted save !", saved.getName());
}
private static Bar createRandomBar() {...}
}
Spring configuration
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:data="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- Tell Spring Data where to find the interfaces -->
<data:repositories base-package="com.bar.persistence.dao" factory-class="com.bar.persistence.dao.RepositoryFactoryBean"/>
<!-- include the definitions of the 'upstream' modules -->
<import resource="common-beans.xml"/>
<import resource="io-beans.xml"/>
<!-- The exception translator service-->
<bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>
<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- Use the classic Spring JPA Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- Describes how to connect to the database -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="user" value="${database.user.name}"/>
<property name="password" value="${database.user.password}"/>
<property name="driverClass" value="${database.driver}"/>
<property name="jdbcUrl" value="${database.url}"/>
<property name="initialPoolSize" value="1"/>
<property name="maxPoolSize" value="${database.max.pool.size}"/>
<property name="minPoolSize" value="${database.min.pool.size}"/>
<property name="acquireIncrement" value="1"/>
<property name="acquireRetryAttempts" value="3"/>
<property name="checkoutTimeout" value="10000"/>
</bean>
<!-- Describes how to create hibernate sessions -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
<property name="persistenceUnitName" value="bars"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
<prop key="hibernate.cache.use_second_level_cache">${hibernate.second.level.cache}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.query.cache}</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
<prop key="hibernate.show_sql">${database.show.sql}</prop>
<prop key="hibernate.dialect">${database.hibernate.dialect}</prop>
<prop key="hibernate.search.default.directory_provider">filesystem</prop>
<prop key="hibernate.search.default.indexBase">${hibernate.search.dir}</prop>
</props>
</property>
</bean>
</beans>
Stack trace
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.bar.persistence.dao.SpringDataLearningTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bar.persistence.dao.barDao com.bar.persistence.dao.SpringDataLearningTest.barDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bar.persistence.dao.BarDao] 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.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:288)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1120)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:379)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:313)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bar.persistence.dao.BarDao com.bar.persistence.dao.SpringDataLearningTest.barDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bar.persistence.dao.BarDao] 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:514)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
... 29 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bar.persistence.dao.BarDao] 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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:967)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:837)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:749)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
... 31 more
Upvotes: 1
Views: 5199
Reputation: 3775
Something like that:
@NoRepositoryBean
from BarDao
@NoRepositoryBean
on BarDaoImplBarDaoImpl
and BarDao
to com.bar.persistence.dao.bar
interface BarDaoMyRepository extends BarDao
to
com.bar.persistence.dao.repository
base-package="com.bar.persistence.dao.repository"
BarDaoMyRepository
You using way to add some functionality to every repository which extends BarDao
(so you need to extend it and may add some declarative methods).
If you want add functionality to only one repository - see
http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.introduction 1.4.1 Adding behaviour to single repositories
Upvotes: 2