Spring Boot: How to declare a custom repository factory bean

14,363

Solution 1

Indeed you have to declare a new FactoryBean in your @EnableJpaRepositories annotation:

@Configuration
@EnableJpaRepositories(value = "your_package",
        repositoryFactoryBeanClass = CustomFactoryBean.class)
public class ConfigurationClass{}

CustomFactoryBean.java:

public class CustomFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I>{

    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new SimpleJpaExecutorFactory(entityManager);
    }

    /**
     * Simple jpa executor factory
     * @param <T>
     * @param <I>
     */
    private static class SimpleJpaExecutorFactory<T, I extends Serializable> extends JpaRepositoryFactory{

        private EntityManager entityManager;

        /**
         * Simple jpa executor factory constructor
         * @param entityManager entity manager
         */
        public SimpleJpaExecutorFactory(EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }

        @Override
        protected Object getTargetRepository(RepositoryMetadata metadata) {
            JpaEntityInformation entityInformation =
                    getEntityInformation(metadata.getDomainType());
            return new SomethingRepositoryImpl<T,I>(entityInformation, entityManager);
        }

        @Override
        protected Class getRepositoryBaseClass(RepositoryMetadata metadata) {
            return SomethingRepositoryImpl.class;
        }
    }
}

Then it will be your SimpleJpaRepository instance: SimpleJpaRepositoryImpl that will be used

Solution 2

From version 1.9, you no longer need to create a new FactoryBean class, instead you can just set the base repository directly. From Baeldung:

@Configuration
@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao", 
  repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
    // additional JPA Configuration
}

public class ExtendedRepositoryImpl<T, ID extends Serializable>
  extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
    
    private EntityManager entityManager;

    public ExtendedRepositoryImpl(JpaEntityInformation<T, ?> 
      entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }

    // ...
}
Share:
14,363
Nicolas
Author by

Nicolas

Updated on June 27, 2022

Comments

  • Nicolas
    Nicolas almost 2 years

    I'm using Spring Boot (1.3.3) with annotation-based/JavaConfig configuration on an application. I have the following repository interface:

    @RepositoryRestResource(collectionResourceRel = "something", path = "something")
    public interface SomethingRepository 
        extends CrudRepository<SomethingRepository, Long> {
    
    }
    

    What I would like to do is override the behavior of some methods in the generated repository proxy. The only way I found of doing this is based on what the documentation suggests for adding new custom methods (see: Adding custom behavior to single repositories), so I define the following interface:

    public interface SomethingRepositoryCustom {
        Something findOne(Long id);
    }
    

    ...and I add the corresponding implementation:

    public SomethingRepositoryImpl extends SimpleJpaRepository<Something, Long> 
        implements SomethingRepositoryCustom {
    
        public SomethingRepositoryImpl(<Something> domainClass, EntityManager em) {
            super(domainClass, em);
            this.entityManager = em;
        }
    
        @Override
        public Something findOne(Long id) {
            System.out.println("custom find one");
            // do whatever I want and then fetch the object
            return null;
        }
    
    }
    

    Now, if I start the application, I get the following error:

    ... org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.dummy.repositories.SomethingRepositoryImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.dummy.repositories.SomethingRepositoryImpl.() ...

    Question: How can I solve the BeanInstantiationException? I'm assuming I need to declare a repository factory bean but I'm not sure how to do so overriding the Spring Boot configuration.

  • Nicolas
    Nicolas about 8 years
    I tried this and it was still trying to instantiate the default constructor for SomethingRepositoryImpl. That's because, by convention, Spring fetches for class names ending in Impl. I changed the Impl suffix and added the code you provided and it worked.
  • Nicolas
    Nicolas about 8 years
    Would this approach work if I needed to customize more than one repository? Looks like you can only define one repositoryFactoryBeanClass in @EnableJpaRepositories.
  • Michael Desigaud
    Michael Desigaud about 8 years
    yes because repositoryFactoryBeanClass allows you to point to your custom SimpleJpaRepository instance wich will be shared by all of your repositories
  • woemler
    woemler about 8 years
    OP has not followed up, but I tried it out and it worked as suggested.