Is there a way to create Spring Repository as Bean in configuration class? So that I can have multiple repositories in one class instead of creating new interface file for each entity?
I tried to create a nested #Repository interface but that couldn't been autowired.
Edit:
tried (as said earlier)
#Repository
public static interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Customer> findByLastNameStartsWithIgnoreCase(String lastName);
}
which is inner class in SpringBootApplication class. I cannot use #Bean annotation beacuse it gives compilation errors
Turns out there is a method considerNestedRepositories.
Related
I have a MyService class which has repository field supposed to be injected.
public class MyService {
#Autowired
private MyRepository repository;
// ...ommited
}
And there is MyRepository interface only implemented by MyRepositoryImpl class with #Mapper annotation of MyBatis.
public interface MyRepository {
// ...ommited
}
#Mapper
public interface MyRepositoryImpl extends MyRepository {
// ...ommited
}
When I try to start SpringBootApplication, NoUniqueBeanDefinitionException is thrown.
#SpringBootApplication(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
#MapperScan(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
(...omitted)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'com.example.MyService':
Unsatisfied dependency expressed through field 'repository';
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type 'com.example.MyRepository' available:
expected single matching bean but found 2: com.example.MyRepositoryImpl,com.example.MyRepository
(...omitted)
Why is MyRepository interface registered as one of bean even though it doesn't have #Component annotation nor isn't included any bean configurations?
And I found that everything work fine if I don't use FullyQualifiedAnnotationBeanNameGenerator as nameGenerator.
Any ideas?
There can be many other ways to mark an interface as a bean. Some of them are:
#RepositoryDefinition above MyRepository interface
MyRepository extends CrudRepository or JpaRepository
Check if any of these exist.
Update 1:-
The problem seems to be in #MapperScan. What it does is scans for all the interfaces in a package and register them as bean; and if I am not wrong MyRepository and MyRepositoryImpl are in the same package. That's the reason why 2 beans are being created with names com.example.MyRepositoryImpl, com.example.MyRepository and basically both are of same type as MyRepositoryImpl extends MyRepository.
Now when you are using #Autowired on repository field of MyService, it gets confused as in which one to inject. To resolve this the easiest approach is to use #Primary over MyRepositoy which will give that interface a priority while injecting. Another approach is to use #Qualifier("uniqueName") over both the interfaces and also above repository field of MyService along with #Autowired specifying which one you want to inject. You can visit official documentation from here to learn more.Hope this helps a bit .
Spring is not able to figure out the bean whose class is supposed to be implementing the Repository interface.
Put the annotation #Repository above the RepositoryImpl Class. It will find it.
I have to implement custom repository class annotated with #Repository which should inherit another class which is annotated #Repository as well.
What is correct implementation of that usecase? Can directly inherit that class and add #Repository to main, or there is another best practice? Actually I have problem when I have defined
#EnableJpaRepositories(basePackages = { "com.example.foo.repositories", "com.example.bar.repositories" }
in #Configuration class in root it doesn't scan my repositories and I can't autowire it.
here is sample of my repository class:
parent repository (third party class):
#Repository
public abstract class ParentRepository {
// ...
}
interface and impl class which are in package com.example.foo.repositories:
public interface IFooRepository {
Foo getFoo();
}
#Repository
public class FooRepository extends ParentRepository implements IFooRepository {
Foo getFoo() {
// ...
}
}
Do you have idea how to fix it and make possible to autowire IFooRepository ?
Thank you in advice.
EDIT:
I find out green bean next to #EnableJpaRepositories and when I click on the bean it redirects me to bar repository, and doesn't show FooRepository Bean. I don't understand because both repositories are identical implemented.
According to the doc for jpa repositories
In the preceding example, Spring is instructed to scan com.acme.repositories and all its subpackages for interfaces extending Repository or one of its subinterfaces. For each interface found, the infrastructure registers the persistence technology-specific FactoryBean to create the appropriate proxies that handle invocations of the query methods.
see
So basicly #EnableJpaRepositories - is the same as xml configuration from the link - it instructs to find classes that extending Repository. In you example you have #Repository annotation - that instructs Spring to translate exceptions. You should includ "com.example.foo.repositories", "com.example.bar.repositories" into components scan. Try #ComponentScan annotation see
Adding the #Primary annotation to your custom repository should autowire it into another class whenever possible by default, even if there are other valid beans for the autowire.
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
When the bean is createt for a class as MyBean the bean id is myBean but what will be the bean ID if I create the service bean from an interface like below?
#Service
public class ProfileServiceImpl implements ProfileService
When I try to access the bean as #profileService thymeleaf gives the below error.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'profileService' is defined
All this time I'm using this bean by autowiring to the controller. But at the moment I need to access this from the thymeleaf.
My thymeleaf code segment
<div th:unless="${#profileService.isMe(user)}">
When Spring creates a Bean Definition from a #Service or #Component annotation, it will by default create an id for the bean by lowercasing the first letter of the Class Name. If you want to override that the behavior, you can provide an alternative id in the annotation, eg. #Service("profileService").
Regarding what you are experiencing with the Repository - by default Spring looks for a custom implementation of a Repository by appending "Impl" to the Repository Interface name. If it finds it, it will not create a default implementation. So, if you had UserRepositoryImpl extends UserRepository instead of UserRepositoryImpl extends DatatablesCriteriasRepository than Spring wouldn't have created the userRepository bean. Addtionally, if you add #NoRepositoryBean annotation to the UserRepository interface, that will suppress the creation of the userRepository bean.
However, UserRepositoryImpl really should be implementing UserRepository. If it really is intended to extend DatatablesCriteriasRepository, than it should be nameed DatatablesCriteriasRepositoryImpl. Having UserRepsitoryImpl extend DatatablesCriteriasRepository is indication of a problem in the design.
for (String name : context.getBeanDefinitionNames()){
System.out.println(name);
}
This test revealed some interesting outcomes.
service beans
Service beans are named after the concrete class name regardless of the interface.
#Service
public class ProfileServiceImpl implements ProfileService
ie. profileServiceImpl in the above question.
Repository beans
Further Repository beans are something more interesting. Below is my crud repository interface without any annotations.
public interface UserRepository extends CrudRepository<User, Long>, DatatablesCriteriasRepository<User>{
And I created an implementation of the UserRepositoryImpl for the DatatablesCriteriasRepository as below without any annotations.
public class UserRepositoryImpl implements DatatablesCriteriasRepository<User>
these two included two beans with IDs userRepository userRepositoryImpl respectively.
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.