ronif
ronif

Reputation: 805

How to use my base repository methods in custom implementation repository in Spring-Data-JPA?

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

Answers (3)

Guildenstern70
Guildenstern70

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

jelies
jelies

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

Parvez
Parvez

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

Related Questions