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.
Related
I created a custom implementation of a repository using the Spring Data Annotations and autowiring. The base repository interface is below and is controlled by Spring annotations.
#Repository
#Transactional
public interface BasicRepository extends JpaRepository<BasicMaster, Long>, BasicCustomRepository {}
The custom repository interface is:
public interface BasicCustomRepository{
List<BasicResponse> getBasic(BasicRequest basicRequest);
}
and the custom repository implementation is:
public class BasicCustomRepositoryImpl implements BasicCustomRepository {
///CODE
}
This code works as expected but for some reason, IntelliJ says "Class BasicCustomRepositoryImp is never used". How do I get IntelliJ to recognize that this is an implementation of a used interface?
It turns out that the custom repository and the implementation must have a Spring type annotation. In this case, #Repository. Spring is then able to correctly identify it and use spring based bean injection. so
#Repository
public interface BasicCustomRepository{
List<BasicResponse> getBasic(BasicRequest basicRequest);
}
and
#Repository
public class BasicCustomRepositoryImpl implements BasicCustomRepository {
///CODE
}
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
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.
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 have a package named com.example.service, and in my Spring Configuration class I have the annotation #ComponentScan({"com.example.service"},{"com.example.controller"}).
When I try to #Autowire a service, code compilation fails with a NoSuchBeanDefinitionException. The MyService interface is annotated with #Service.
Currently I use a quite ugly workaround and declare every single service bean in my ExampleConfig.java like
#Bean
public MyService myService() {
return new MyServiceImpl();
}
Generally the #ComponentScan seems to work, if I remove the controller package, the controllers are not found. What did I understand wrong? Please let me know, if I missed out any relevant information.
The MyService interface is annotated with #Service
You must annotate the implementation of your interface. Not the interface itself.
Try using below code for ComponentScan annotation for scanning multiple packages:
#ComponentScan({"com.example.service","com.example.controller"})
instead of
#ComponentScan({"com.example.service"},{"com.example.controller"})
#ComponentScan uses string array for scanning multiple base packages.