Spring Boot: How to declare a custom repository factory bean
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;
}
// ...
}
Nicolas
Updated on June 27, 2022Comments
-
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 about 8 yearsI 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 about 8 yearsWould this approach work if I needed to customize more than one repository? Looks like you can only define one repositoryFactoryBeanClass in @EnableJpaRepositories.
-
Michael Desigaud about 8 yearsyes because repositoryFactoryBeanClass allows you to point to your custom SimpleJpaRepository instance wich will be shared by all of your repositories
-
woemler about 8 yearsOP has not followed up, but I tried it out and it worked as suggested.