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
}
Related
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'm learning about Spring Boot and JPA, Spring Data Rest, H2 database and I have found a tutorial. I try to understand it and it is an easy example but I don't understand something. Why isn't it necessary to put #Repository or #Component in the AlienRepo class?
The repo object is injected in the AlienController class, and I know from previous tutorials that you need to use #Component or #Repository. I thought that it is mandatory to use this annotation.
The controller:
package com.dgs.springbootjpa.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.dgs.springbootjpa.dao.AlienRepo;
import com.dgs.springbootjpa.model.Alien;
#Controller
public class AlienController {
#Autowired
AlienRepo repo;
#RequestMapping("/")
public String home() {
return "home.jsp";
}
#RequestMapping("/addAlien")
public String addAlien(Alien alien) {
repo.save(alien);
return "home.jsp";
}
}
The POJO:
package com.dgs.springbootjpa.model;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Alien {
#Id
private int aid;
private String aname;
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname = aname;
}
#Override
public String toString() {
return "Alien [aid=" + aid + ", aname=" + aname + "]";
}
}
The dao interface:
package com.dgs.springbootjpa.dao;
import org.springframework.data.repository.CrudRepository;
import com.dgs.springbootjpa.model.Alien;
public interface AlienRepo extends CrudRepository<Alien, Integer> {
}
You can see that in the AlienRepo interface there is not #Repository or #Component and the code is working successfully.
Thank you!
Update---------------------------------------------
I want to fetch data from database and I add the below code. I want to ask you why #RequestParam isn't mandatory? If I delete the #RequestParam the app is working successfully.
#RequestMapping("/getAlien")
public ModelAndView addAlien(#RequestParam int aid) {
ModelAndView mv = new ModelAndView("showAlien");
Alien alien = repo.findById(aid).orElse(new Alien());
mv.addObject(alien);
return mv;
}
Did you notice, CrudRepository has #NoRepositoryBean annotation and this annotation makes CrudRepository an intermediate interface. An intermediate interface is basically adds
general functionality for your derived repositories.
If any class annotated with #NoRepositoryBean Spring Data does not create instances at run-time. Because it's intermediate class and it's created to add functionality for the derived class.
If you extend an interface that has #NoRepositoryBean and your derived interface doesn't have #NoRepositoryBean Spring knows that you are trying to use that functionality in your real Repository interface so adding #Repository annotation is verbose.
Edit: Why #RequestParam isn't mandatory?
From the official documentation you can see "Controller method argument" table, the last row of the table says:
Any other argument :
If a method argument is not matched to any of the above, by default it
is resolved as an #RequestParam if it is a simple type, as
determined by BeanUtils#isSimpleProperty, or as an
#ModelAttribute otherwise.
So if you omit annotation for "Controller Method Argument" and you don't write something like #MatrixVariable, #PathVariable or #RequestParam.
It's default a #RequestParam.
#Component and #Repository can be used on classes to use them as Spring Beans. However, AlienRepo is not a class, it's an interface which extends a Spring Data interface. Spring Data doesn't use annotations, it detects the interfaces by scanning the classpath and looking at the hierarchy of interfaces to see if a Spring Data interface is extended. If this is the case, it creates an implementation for each interface during runtime, which is then added to the application context as Spring Bean. That's why you don't need any annotations on a Spring Data interface.
Your #SpringBootApplication brings in the effect of #EnableAutoConfiguration annotation:
#EnableAutoConfiguration
public #interface SpringBootApplication
This annotation will guess and auto configure beans.
Auto-configuration for Spring Data's JPA Repositories.
Activates when there is a bean of type DataSource configured in the context, the Spring Data JPA JpaRepository type is on the classpath, and there is no other, existing JpaRepository configured.
Once in effect, the auto-configuration is the equivalent of enabling JPA repositories using the EnableJpaRepositories annotation.
This configuration class will activate after the Hibernate auto-configuration.
public class JpaRepositoriesAutoConfiguration {
}
When you add data-jpa dependency, JpaRepositoriesAutoConfiguration is triggered and bring effect of #EnableJpaRepositories annotation to your app
Your repository then auto-configured:
Spring Data repositories usually extend from the Repository or CrudRepository interfaces. If you use auto-configuration, repositories are searched from the package containing your main configuration class (the one annotated with #EnableAutoConfiguration or #SpringBootApplication) down.
Your AlienRepo class extends CrudRepository, so Spring will be able to skim over the packages specified in the configuration and find repositories.
As a side note, you can drop #Autowired if there is exactly one constructor AlienController(AlienRepo), Spring can figure that out as well.
I'm not sure if I understand it correctly so want to clarify.
If I want to create a repository for my entity eg.:
public interface BookRepository extends JpaRepository<Book, Id> {}
Should I annotate it with #Repository? According to this question #Repository annotation translates exceptions from SQL to persistence ones but doesn't JpaRepostiory already do that? What's the best practice - to annotate or not?
While using JpaRepository you don't need to annotate the interface with #Repository
It is just an interface and the concrete implementation is created dynamically as a proxy object by Spring and the JDBC Exceptions are handled there.
You need to use #Repository when you create a Custom DAO, so that spring creates a bean and handles the exception properly.
You need to annotate it with #Repository so spring knows it should instantiate that class as a bean. The #Component, #Service and #Repository annotations all serve the same purpose in that regard. #Repository narrows the scope to a service that specifically deals with obtaining and storing data.
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
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.