I use Spring JPA repositories and entities in an application. Now, in a flavor of that application, I need to extend one of my entities and also provide an extended repository.
For all other beans I need to override/extend I simply create a new implementation and annotate it with #Primary so it will be autowired instead of the default implementation.
For repositories, however, this does not work. I can annotate the new repository with #Primary but it has no effect (both beans are found and can thus not be autowired). This makes sense because the repository is an interface and not an implementation, the implementation is provided by Spring dynamically.
Can I somehow tell Spring (via annotation on the repository or via configuration) which repository to use? Or do I have to do a manual workaround like this Using #Primary in Spring Data JPA repositories or should I come up with a kind of repository provider instead of autowiring?
Edit to make things clearer:
Let's say I have an entity A
#Entity
public class A {
#Id
private long id;
}
and its repository
public ARepository extends Repository<A, Long> {
}
Now I extend it to the entity B
#Entity
public class B extends A {
}
public interface BRepository extends ARepository {
}
Normally, as per the documentation, you use repositories like this:
#Autowired
private ARepository repository;
This does, however, not work because there are now two beans of the type ARepository. For beans that I implement myself I would use #Primary on the extending class but for repositories there is no implementation of the interface at compile time.
I would adapt the idea form this answer: https://stackoverflow.com/a/27549198/280244 and this git example https://github.com/netgloo/spring-boot-samples/tree/master/spring-boot-springdatajpa-inheritance/src/main/java/netgloo/models
Introduce a common abstract Repository that is marked with #NoRepositoryBean
#NoRepositoryBean
public interface AbstractARepository<T extends A>
extends Repository<T, Long> {
T findByName(String name); //or what ever your queries are
}
public ARepository extends AbstractARepository<A> {
//almost emtpy
}
public BRepository extends AbstractARepository<B> {
//queries that are special for B
}
Now you can inject ARepository and BRepository, and both are type save!
Just for the record, it is possible to add #Primary support to JPA repositories as suggested here Using #Primary in Spring Data JPA repositories
My implementation of the missing code:
private boolean isSpringDataJpaRepository(Class<?> beanClass) {
return JpaRepositoryFactoryBean.class.isAssignableFrom(beanClass);
}
I think the answer of #Ralph is better because of the type safety.
Can I somehow tell Spring (via annotation on the repository or via
configuration) which repository to use?
Yes you can. First you give each repository class a unique bean name.
#Repository("myARepository")
public ARepository extends Repository<A, Long> {
}
#Repository("myBRepository")
public interface BRepository extends ARepository {
}
Then when you autowire using ARepository as a type you should use the #Qualifier annotation to tell Spring which of the repositories you want.
#Autowire
#Qualifier("myBRepository")
private ARepository repository;
This will autowire a BRepository
Related
I'm using Spring Boot, when I want to extend SimpleJpaRepository like this interface:
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID>{}
and this implementation:
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID>
{
private final EntityManager entityManager;
public BaseRepositoryImpl(Class<T> domainClass, EntityManager entityManager)
{
super(domainClass, entityManager);
this.entityManager = entityManager;
}
}
I got the following error:
Could not autowire. No beans of 'Class<T>' type found.
How can I resolve it?
When trying to make an implementation of JpaRepository through extending SimpleJpaRepository, it is important that spring knows you are doing this. By default spring will try to create a SimpleJpaRepository, even if you extend it. Therefore your new implementation of the SimpleJpaRepository will not be populated, created or even remotely available. Therefore all repositories extending your new custom repository will not be created either.
To solve this problem, one should add some repository config. More specifically you need to provide a repositoryBaseClass in the #EnableJpaRepositories annotation; e.g.
#Configuration
#EnableJpaRepositories(
repositoryBaseClass = RepositoryImpl.class
)
Usually you already have some config class to provide your DataSource-bean, EntityManager-bean and/or JpaTransactionManager-bean. As a general best-practice I suggest placing it over there.
Sometimes spring gets a little confused when you do this and still doesn't manage to find your newly defined repositoryBaseClass. In that case, help the spring repository autowirer a little and also provide a basepackage, e.g.
#Configuration
#EnableJpaRepositories(
repositoryBaseClass = RepositoryImpl.class,
basePackages = {
"be.your.company.your.path.to.your.repositories"
}
)
Make a interface extending JpaRepository
For eg -
public interface Repository extends JpaRepository<Entity,Integer>,RepositoryCustom // this is our custom repository{
}
Repository Custom is a Interface
public interface RepositoryCustom {
List<Entity> getAll(); // define the method signature here
}
Implementing the Custom Interface
#Transactional
public class RepositoryImpl implements RepositoryCustom{
#PersistenceContext // this will inject em in your class
private EntityManager entityManager;
write the method body and return
}
Keep in mind the Repository naming convention . If the Interface name
is Repository . then the custom interface shuld be named as
Repository and the implementation as Repository
I'm working on a Spring Boot v1.4.2.RELEASE application with JPA.
I defined repository interfaces and implementations
ARepository
#Repository
public interface ARepository extends CrudRepository<A, String>, ARepositoryCustom, JpaSpecificationExecutor<A> {
}
ARepositoryCustom
#Repository
public interface ARepositoryCustom {
Page<A> findA(findAForm form, Pageable pageable);
}
ARepositoryImpl
#Repository
public class ARepositoryImpl implements ARepositoryCustom {
#Autowired
private ARepository aRepository;
#Override
public Page<A> findA(findAForm form, Pageable pageable) {
return aRepository.findAll(
where(ASpecs.codeLike(form.getCode()))
.and(ASpecs.labelLike(form.getLabel()))
.and(ASpecs.isActive()),
pageable);
}
}
And a service
AServiceImpl
#Service
public class AServiceImpl implements AService {
private ARepository aRepository;
public AServiceImpl(ARepository aRepository) {
super();
this.aRepository = aRepository;
}
...
}
My application won't start with the message :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
| aRepositoryImpl
└─────┘
I followed all steps discribed in http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behaviour
Please help !
Laurent
Use #Lazy
A simple way to break the cycle is by asking Spring to initialize one of the beans lazily. That is: instead of fully initializing the bean, it will create a proxy to inject it into the other bean. The injected bean will only be fully created when it’s first needed.
#Service
public class AServiceImpl implements AService {
private final ARepository aRepository;
public AServiceImpl(#Lazy ARepository aRepository) {
super();
this.aRepository = aRepository;
}
...
}
source: https://www.baeldung.com/circular-dependencies-in-spring
Use #Lazy annotation it will be resolved
#Component
public class Bean1 {
#Lazy
#Autowired
private Bean2 bean2;
}
There's a simple fix for your original problem:
Just remove #Repository from ARepositoryCustom and from ARepositoryImpl.
Keep all the naming and interface/class hierarchies. They are all OK.
I've tested your source code, and found something tricky.
First, with your source code, I got the following error:
There is a circular dependency between 1 beans in the application context:
- ARepositoryImpl (field private test.ARepository test.ARepositoryImpl.aRepository)
- aRepositoryImpl
Then, I guess Spring 'confused' between ARepository (JPA repository) and ARepositoryImpl (Custom repository).
So, I would suggest you rename ARepository to something else, such as BRepository. It worked if I renamed the class name.
According to offcial documentation of Spring Data (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/) :
These classes need to follow the naming convention of appending the namespace element’s attribute repository-impl-postfix to the found repository interface name. This postfix defaults to Impl
add this in your pom.xml file. It works for me
spring.main.allow-circular-references:true
In my case:
I added
spring:
main:
allow-circular-references: true
in the application.yml
Or you can add
spring.main.allow-circular-references=true
in the application.properties
Both application.yml and application.properties files in the below directory:
import org.springframework.context.annotation.Lazy;
Add #Lazy on one dependency injection to make it lazy load.
Good day,
I'm trying to dynamically create spring-data repositories creation and injection for spring 4.
So, I would like to do something like this:
public abstract class CRUDService<T extends AbstractEntity> {
#Autowired
private CrudRepository<T, Long> repository;
....
}
without concrete implementation of CrudRepository for each entity.
Is there any way to implement this just using some of spring core mechanism? (I was trying to implement it using BeanFactoryPostProcessor, but no success).
I am attempting to get a reference to my repository interface (UserRepository) that extends CrudRepository within my custom implementation (UserRepositoryExtensionImpl) in order to gain access to all the methods provided by Spring JPA.
Crud Extension:
#Repository
public interface UserRepository extends CrudRepository<User, String>, UserRepositoryExtension<RosterUser> {
...any custom spring JPA methods...
}
Extension Interface:
#Repository
public interface UserRepositoryExtension <T> {
public T put(T entity);
}
Custom Implementation:
public class UserRepositoryExtensionImpl implements UserRepositoryExtension<User> {
UserRepository userRepository;
#Autowired
public UserRepositoryExtensionImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public User put(User user) {
System.out.println(user + "was put");
// ...put logic here
return null;
}...
}
However, I am unable to inject UserRepository since a circular dependency exists (given that UserRepository extends the interface implemented by my UserRepositoryImpl). I am getting the following error:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ' userRepositoryImpl': Requested bean is currently in creation: Is there an unresolvable circular reference?
A possible, but less than ideal solution would be to inject and EntityManager into UserRepositoryImp, but in that case, I do not have access to any of the Spring JPA methods provided by CrudRepository, or any additional methods that I might have created in UserRepository.
Any suggestions on how to get around this?
Any help would be greatly appreciated.
EDIT: As mentioned in #shelley's answer, I was able to solve this by making 3 changes:
Removing the #Repository from UserRepositoryExtensionImpl
Renaming UserRepositoryExtensionImpl to UserRepositoryImpl. Apparently this makes Spring aware of the implementation's existence. See Spring Doc
Removing my constructor and moving the #Autowired to the userRepository field
SUCCESS!
A couple small things need to be changed in order for this to work:
Remove the #Repository annotation from the custom repository interface (UserRepositoryExtension).
The custom repository implementation should actually be named "<StandardRepository>Impl" rather than "<CustomRepository>Impl". In your code example, this should be UserRepositoryImpl instead of UserRepositoryExtensionImpl.
As shelley pointed out, the naming is really important to make the autowire work. In the example below, I follow the right naming standard for my custom interface and its implementation. But my interface that extended the JpaRepository was named “ItemDao” instead of “ItemRepository”, this resulted in that spring ignored my custom implementation altogether...
OBS!!! Should be "ItemRepository"
#Repository
public interface ItemDao extends JpaRepository<Item, Long>, ItemRepositoryCustom {}
my interface
interface ItemRepositoryCustom {...}
my implementation class
class ItemRepositoryImpl implements ItemRepositoryCustom {...}
If anyone have similar problems, start by following the naming standard that is used in the spring documentation at the link below.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
There is a well defined way to create custom repository implementations in Spring Data JPA which you should follow. Basically you need to extend CrudRepository so you don't have to inject an instance of it in your custom implementation.
I have solved problem by injecting ApplicationContext and getting bean in lazy way using applicationContext.getBean(UserRepository.class).
It works this way.
I found I way of how to do it without the need for #Autowire:
public interface UserRepository extends
UserRepositoryBasic,
UserRepositoryExtension
{
}
public interface UserRepositoryBasic extends
JpaRepository<User, String>
{
// standard Spring Data methods, like findByLogin
}
public interface UserRepositoryExtension
{
public void customMethod();
}
public class UserRepositoryExtensionImpl implements
UserRepositoryExtension
{
private final UserRepositoryBasic userRepositoryBasic;
// constructor-based injection
public UserRepositoryExtensionImpl(
UserRepositoryBasic userRepositoryBasic)
{
this.userRepositoryBasic = userRepositoryBasic;
}
public void customMethod()
{
// we can call all basic Spring Data methods using
// userRepositoryBasic
}
}
Well in this case I suggest to use the #Lazy annotation.
public class MyCustomRepositoryImpl implements MyCustomRepository {
#Lazy
#Autowired
private MyRepository myRepository;
#Override
public boolean customMethod() {
return myRepository.count() > 0;
}
}
With constructor parameter Spring tries to create the "basic" repository class which require you custom repository which requires you "basic" repository - the typical case with circular dependency.
Without #Lazy but with only the #Autowired it also won't work (there will be problem with factory bean for the basic repo).
I think in this case the #Lazy is the most elegant solution.
Given the list of all spring data repositories in some class Bar:
#Autowired
private List<Repository> repositories;
How can I find the repository for an existing domain class Foo in the above list?
Assuming that the following exists:
#Entity
public class Foo {
...
}
and
public interface FooRepository extends JpaRepository<Foo, String> {}
Spring Data Commons contains a class Repositories that takes a ListableBeanFactory to find all repository beans defined in it and exposes an API to obtain these instances by domain class (through ….getRepository(Class<?> type)).
This class should be used with care. As there's some serious proxy generation going on for the repository instances you have to make sure the Repositories instance is created as late as possible during the ApplicationContext creation. The preferred way is to implement ApplicationListener and create the instance by listening to the ContextRefreshedEvent.
In case you're writing a web application, the safest way to use Repositories is by bootstrapping the repositories in the ApplicationContext created by the ContextLoaderListener and place the Repositories (see the reference documentation of Spring MVC for details.
#Service
public class GenericRepository {
#Autowired
private WebApplicationContext appContext;
Repositories repositories = null;
public GenericRepository() {
repositories = new Repositories(appContext);
}
public JpaRepository getRepository(AbstractPersistable entity) {
return (JpaRepository) repositories.getRepositoryFor(entity.getClass());
}
public Object save(AbstractPersistable entity) {
return getRepository(entity).save(entity);
}
public Object findAll(AbstractPersistable entity) {
return getRepository(entity).findAll();
}
public void delete(AbstractPersistable entity) {
getRepository(entity).delete(entity);
}
}
The key to the solution is
Spring's org.springframework.data.repository.core.support.DefaultRepositoryMetadata which provides the method getDomainType().
DefaultRepositoryMetadata needs the repository interface as constructor arg. So one can loop over all existing repositories, retrieve the repository interface (which is still a tricky part because the repository instance has more than one interface) and find the one where getDomainType()equals Foo.class.
This worked for me :
Repositories repositories = new Repositories(context);
CrudRepository repo = (CrudRepository) repositories.
getRepositoryFor(entityClass).get();
You can use Spring's GenericTypeResolver to get the Entity class from your Repository.
Repository<Foo, String> fooRepository = repositories.stream()
.filter(repository -> GenericTypeResolver
.resolveTypeArguments(repository.getClass(), Repository.class)[0].equals(Foo.class))
.findFirst().get();