NoUniqueBeanDefinitionException when injecting JpaRepository to Generic class - java

How to inject repository to generic class?
public class FruitComboBox<T extends Fruit> extends ComboBox {
#Autowired
private JpaRepository<T, Integer> repository;
...
}
public class FruitMarket {
#Autowired
FruitComboBox<Apple> appleCombobox; // Apple extends Fruit
#Autowired
FruitComboBox<Orange> orangeCombobox; // Orange extends Fruit
...
}
Also I have two repositories
#Repository
public interface AppleRepository extends JpaRepository<Apple, Integer> {
}
#Repository
public interface OrangeRepository extends JpaRepository<Orange, Integer> {
}
I suppose that the generic repository from FruitComboBox should be resolved as one of two existing repositories according it's T, and autowired by Spring.
The NoUniqueBeanDefinitionException: expected single matching bean but found 2 occurs at runtime (not at start of application). So I supposed that at runtime all the types are defined and Spring has known what concrete type is T.

Spring data needs to know at the bootstrap time the the entity the repository represents. That means you can not leave it with Generic Parameter during autowiring.
Create a separate interface like below.
#Repository
public interface MyGenericRepository<T> extends JpaRepository<T, Integer>{
}
Now you should be able to autowire it with a definite type (not generic type). This is as generic as it gets.
#Autowired
private MyGenericRepository<Apple, Integer> repository;
Above can not be left with generic parameter.
Reference http://docs.spring.io/spring-data/jpa/docs/1.6.5.RELEASE/reference/html/repositories.html
Also try to use Long for the ID's.

Related

Spring Boot/Data: Generic service class for crud operations

Let's say I want to create a REST API which performs basic CRUD operations on several entities. For that I've created generic interface:
public interface CrudService<T>{
//generic CRUD methods
}
And its implementation for Foo entity:
#Entity
public class Foo {
}
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>{
}
#Service
#Transactional
public class FooCrudServiceImpl implements CrudService{
#Autowired
private FooRepository repository;
//CRUD methods implementation
}
#RestController
class FooController{
#Autowired
private CrudService<Foo> crudService;
//Controller methods
}
What I want to avoid now is creating service implementation for each entity with basically the same logic. So I tried to create a generic service class which can be called from multiple controllers(FooController, BarController etc.):
#Service
#Transactional
class GenericCrudServiceImpl<T> implements CrudService{
#Autowired
private JpaRepository<T, Long> repository;
//generic CRUD methods implementation
}
and pass that service class to each controller where the entity type would be specified. The problem is that there will be multiple repository beans that could be injected into GenericCrudServiceImpl (FooRepository, BarRepository etc.) and just by specifying the type of JpaRepository Spring still doesn't know which bean to inject. I don't want to call repository beans directly from controller classes to maintain seperation of responsibilities.
Additionally, for some reason this problem doesn't occur on controller level where I inject CrudService interface and Spring understands which bean should it choose, which messes with my whole understanding of dependency injection.
Is there a way to create such a generic service class? Other posts on stackoverflow didn't provide me with an answer.
Bonus question: what's the difference between using a #Qualifier annotation and injecting a specific implementation (in this example FooCrudServiceImpl instead of CrudService in controller class)? In both cases pointing to different use implementation requires changing one line of code.
What about that:
#Transactional
public class GenericCrudServiceImpl<T> implements CrudService{
private JpaRepository<T, Long> repository;
public GenericCrudServiceImpl(JpaRepository<T, Long> repository) {
this.repository = repository;
}
}
And Spring configuration:
#Configuration
public PersistanceConfiguration {
#Bean
public JpaRepository<Foo, Long> fooJpaRepository() {
...
}
#Bean
public JpaRepository<Foo, Long> booJpaRepository() {
...
}
#Bean
public CrudService<Foo> fooService(JpaRepository<Foo, Long> fooJpaRepository) {
return new GenericCrudServiceImpl(fooJpaRepository);
}
#Bean
public CrudService<Foo> booService(JpaRepository<Foo, Long> booJpaRepository) {
return new GenericCrudServiceImpl(booJpaRepository);
}
}
And Controller
#RestController
class FooController{
// Injection by bean name 'fooService'
#Autowired
private CrudService<Foo> fooService;
//Controller methods
}

Custom generic spring data repository

Using spring data, I have two tables that shares the same structure.
The two tables are represented by two different entities, that inherit from the same class :
#MappedSuperclass
public abstract class SuperEntity<T extends SuperEntity> {
// ...
}
#Table(name = "FOO")
#Entity
public class Foo extends SuperEntity<Foo> {
// ...
}
#Table(name = "BAR")
#Entity
public class Bar extends SuperEntity<Bar> {
// ...
}
I also have a generic repository, that I would like to use to factorize to requesting logic, and two sub-repository : one for each table.
public interface GenericEvtRepository <T extends SuperEntity<?>> extends JpaRepository<T, String> { }
public interface FooRepository extends GenericEvtRepository<Foo> {}
public interface BarRepository extends GenericEvtRepository<Bar> {}
I would like to add an actual query implementation to this repository (i.e. using EntityManager / Criteria).
Therefore I tried to adapt the custom repository strategy to my generic case
#Repository
public class GenericEvtRepositoryImpl<T extends SuperEntity<?>> extends SimpleJpaRepository<T, String> implements GenericEvtRepository<T> {
#PersistenceContext
EntityManager entityManager;
// Some logic using entityManager
public SuperEntity myCustomRequest() { /*...*/ }
}
However my application doesn't start, with the exception :
org.springframework.data.mapping.PropertyReferenceException: No property myCustomRequest found for type Foo!
Not sure what I'm doing wrong, but Spring seems to think that myCustomRequest is an attribute from my entities, instead of a method.
I'm using spring-boot 1.5.6 and spring-data-jpa 1.11.6.
Minimal reproductible exemple
Luckily I was able to reproduce your issue,
How spring recommends custom repository implementation is specified here in spring docs.
So, you can do something like below,
public interface CustomEntityRepository<T extends SuperTag<?>>
public interface FooRepository extends JpaRepository<Foo, Integer>, CustomEntityRepository<Foo>
public interface BarRepository extends JpaRepository<Bar, Integer>, CustomEntityRepository<Bar>
And define common implementation for CustomEntityRepository<T extends SuperTag<?>> as below,
#Repository
// NOTE: Implementation name must follow convension as InterfaceName + 'Impl'
public class CustomEntityRepositoryImpl<T extends SuperTag<?>> implements
CustomEntityRepository<T> {
#PersistenceContext
private EntityManager entityManager;
// Generic custom implementation here
}
Spring automatically detects implementation of Custom Interface CustomEntityRepository based on implementation class naming convention.

Is there a way to autowire Generics in Java Interfaces?

This is what I have. I am trying to autowire the JpaRepository so that I can access it in a generic way.
public interface Enabler<T, R> {
#Autowired
R repository;
default ServiceEnabler<T> getEnabler(){
return new ServiceEnabler<>(repository);
}
}
public class User implements Enabler<User, UserRepository>, Serializeable {
private static final long serialVersionUID = -5454763392593161707L;
Long id;
String name;
}
#Repository
public UserRepository extends JpaRepository<User, Long> {
List<User> findByName();
}
#Service
public GenericService<T extends Enabler<T, R>,
R extends JpaRepository<T, Long>> {
List<T> getAll(T generic){
ServiceEnabler<T> enabler = generic.getEnabler();
return enabler.getRepo().findAll();
}
}
Is there anyway that this is possible?
Purpose of "Interface" in Java is to define a contract for all classes implementing the interface; "Interface" in Java cannot have any state, you can only have runtime constants or methods.
Now since interface cannot have any state so you cannot create instance variables like this R repository;.
Now coming to #Autowired annotation, that as well you cannot do because purpose of this annotation is to inject a object, now like I said earlier since Java Interface cannot have a state so there is no question of injecting an object.
So, overall answer is that you cannot do what you are trying to do and #Autowired R repository; will result in compilation error.

Dynamically create Spring beans that extend an abstract class with parameterized types

I would like to take advantage of Spring 4.0's support for autowiring of generic types but I would like to avoid having to create explicit concrete or anonymous classes for each type. To use an example, lets say I have an interface:
public interface Cache<T extends Entity>
And an abstract implementation of the interface:
public abstract class AbstractCache<T extends Entity> implements Cache<T>
{
#Autowired
private EntityDao<T> dao;
#Autowired
private List<CacheListener<T>> listeners;
...
}
And entity classes A to Z that implement Entity (e.g):
public class A implements Entity
public class B implements Entity
...
public class Z implements Entity
Is there a way I can create instances of Cache<A> through Cache<Z> such that I can autowire these generic types in other classes? E.g.
#Autowire
private Cache<Z> zCache;
I know I can achieve this by individually defining each bean, E.g.
#Bean
public Cache<Z> cacheZ() {
return new AbstractCache<Z> () {};
}
But I have been unable to come up with a way to do this for all Entity classes in a particular package. E.g.
public void registerEntityCaches (BeanFactory beanFactory) {
for (Class<? extends Entity> cls : entityPackage.getAllClasses()) {
beanFactory.registerBean(new AbstractCache<cls>() {});
}
}
Is something like this possible or do I have to define them individually?

How to reference the 'normal' spring data repo from a custom implementation?

I want to extend a JpaRepository with a custom implementation, so i add a MyRepositoryCustom interface and a MyRepositoryImpl class extending this interface.
Is there a way to call methods from JpaRepository inside my custom class?
Note: This was also asked as a comment on https://stackoverflow.com/a/11881203/40064, but I think it is common enough to deserve a separate question.
tl;dr
To inject the core repository interface into a custom implementation, inject a Provider<RepositoryInterface> into the custom implementation.
Details
The core challenge to get that working is setting up the dependency injection correctly as you are about to create a cyclic dependency between the object you're about to extend and the extension. However this can be solved as follows:
interface MyRepository extends Repository<DomainType, Long>, MyRepositoryCustom {
// Query methods go here
}
interface MyRepositoryCustom {
// Custom implementation method declarations go here
}
class MyRepositoryImpl implements MyRepositoryCustom {
private final Provider<MyRepository> repository;
#Autowired
public MyRepositoryImpl(Provider<MyRepository> repository) {
this.repository = repository;
}
// Implement custom methods here
}
The most important part here is using Provider<MyRepository> which will cause Spring to create a lazily-initialized proxy for that dependency even while it's creating an instance for MyRepository in the first place. Inside the implementation of your custom methods you can then access the actual bean using the ….get()-method.
Provider is an interface from the #Inject JSR and thus a standardized interface and requires an additional dependency to that API JAR. If you want to stick to Spring only, you can used ObjectFactory as an alternative interface but get the very same behavior.
The section titled Adding custom behaviour to all repositories in the documentation should help you.
For example (only for illustration purposes):
public interface ExtendedJpaRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
T findFirst();
T findLast();
}
public class ExtendedJpaRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID>
implements ExtendedJpaRepository<T, ID> {
public ExtendedJpaRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, entityManager);
}
public T findFirst() {
List<T> all = findAll();
return !all.isEmpty() ? all.get(0) : null;
}
public T findLast() {
List<T> all = findAll();
return !all.isEmpty() ? all.get(all.size() - 1) : null;
}
}
Then, configure ExtendedJpaRepositoryImpl for use as per the instructions given in the documentation linked above.
Since ExtendedJpaRepositoryImpl extends SimpleJpaRepository (which is an implementation of JpaRepository), all methods from JpaRepository can be called from ExtendedJpaRepositoryImpl.

Categories