Reputation: 805
I followed the reference guide for creating and customizing Repositories and came up with the following:
public class MyBasicRepositoryFactoryBean<R extends JpaRepository<T, I>, T extends BaseEntity, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
@Override
protected RepositoryFactorySupport createRepositoryFactory(
EntityManager entityManager) {
return new MyBasicRepositoryFactory<T, I>(entityManager);
}
private static class <T extends AlisEntity, I extends Serializable>
extends JpaRepositoryFactory {
private EntityManager entityManager;
public AlisDaoFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
@Override
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new AlisDaoImpl<T, I>((Class<T>) metadata.getDomainType(),
entityManager);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the
// JpaRepositoryFactory
// to check for QueryDslJpaRepository's which is out of scope.
return MyBasicRepository.class;
}
}
@NoRepositoryBean
public interface MyBasicRepository<T extends MyEntity, KEY extends Serializable> extends CrudRepository<T, KEY> {
void customBaseFoo(T entity);
}
public interface ChildCustomRepository{
void customChildFoo();
}
public interface ChildRepository extends MyBasicRepository<Child, Integer>, ChildCustomRepository {
void findByName(String name);
}
now on the implementation of customChildFoo()
, I would like to call ChildRepository.findByName
or perhaps even MyBasicRepository.customBaseFoo()
.
These of course are not accessible since the ChildRepositoryImpl
implements ChildCustomRepository
and not ChildRepository
, otherwise I would have to wrote the implementation for the basic CRUD and custom interface as well.
And so I tried to inject ChildRepository
to the Impl as such:
public class ChildRepositoryImpl implements ChildCustomRepository{
@Resource
private ChildRepository base;
@Override
public void customChildFoo(){
Child child = base.findByName();
// do some logic with found child here
base.customBaseFoo(child);
}
But it fails due to:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'childDao': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:149)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:109)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1442)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:848)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:707)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:438)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:416)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:549)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:150)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:303)
... 65 more
Caused by: java.lang.NullPointerException
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:125)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:41)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
... 78 more
So, how can accomplish that?
Upvotes: 4
Views: 2821
Reputation: 1859
You must be very careful in your naming to avoid circular references.
For instance, suppose you want to add custom method to
UserDAO
To do so, you create a new repository interface called
UserDAOCust
and then, to be able to use UserDAO repository methods inside your implementation of UserDAOCust, your implementation must be named:
UserDAOImpl
and inject UserDAO as a member of your fragment.
(By reading the Spring JPA documentation, it seems you must name your implementation as UserDAOCustImpl, but that is actually causing a circular reference)
Complete source (in Kotlin):
@Repository
interface UserDAO : CrudRepository<User, Int>, UserDAOCust
{}
interface UserDAOCust
{
fun findByType(): Optional<List<User>>
}
class UserDAOImpl : UserDAOCust
{
@Autowired
private lateinit var userDAO: UCUserDAO
override fun findByType(): Optional<List<User>>
{
// use this.userDAO here
}
}
Upvotes: 2
Reputation: 9290
It's OK to inject parent repository into custom implementation, it's working for me. But I'm not using any RepositoryFactoryBean
to customize my repos. Currently using Spring Data JPA 1.3.0, maybe you should try with latest version.
Upvotes: 0
Reputation: 641
One solution I know is to implement interface ApplicationContextAware
in ChildRepositoryImpl
class and read the bean like applicationContext.getBean(<bean name>)
.
I don't want to implement this but this is the only option I know as of now.
Upvotes: 2