Injecting one of data repositories in Service layer in Spring - java

In my web app I have two different data storages - db and file. In application.properties I can set which one I want to use. I use Spring Data JPA for accessing object from db, so my DataBaseRepository extends CrudRepository.
I want to inject one inteface of Repository in Service layer, and
implementation will depend on chosen profile(in application.properties).
Problem is that my FileRepository doesn't implement CrudRepository, so my repositories haven't common interface for injecting.
Approach 1:Suppose, that my FileRepository extends CrudRepository(and I mark FileRepository with #NoRepositoryBean)
Problem: my implementation of FileRepository must implement many methods, which I don't need(I don't know if it is normal approach and it is worked)
Approach2:don't use CrudRepository interface
Problem: writing many boilerplate code
So, please tell me about another approaches,if they exist in such situation, or say which one is better. Any help is appreciated.

You could create a CustomCrudRepository that extends CrudRepository and a BaseRepository.The BaseRepository interface contains every method that has to be supported by any implementation. Most likely copy the signature from CrudRepository. Than inject based on the BaseRepository.
Hard to explain so see the following example without generics. You can add them on your own.
public interface BaseRepo {
// common methods
long count();
...
}
#NoRepositoryBean
interface CustomCrudRepository extends CrudRepository, BaseRepo {
}
interface EntityRepository extends CustomCrudRepository {
}
class FileRepository implements BaseRepo {
#Override
public long count() {
return 0;
}
}
#Service
class SomeService {
#Autowired
private BaseRepo repo;
}

Related

Spring cache abstraction (AdviceMode.ASPECTJ) not working inside spring-data-jpa repositories

i'm using spring-data-jpa 1.9.0.RELEASE and want to use the spring caching mechanism inside my repositories, e.g.
public interface LandDao extends CrudRepository<Land, Long> {
#Cacheable("laender")
Land findByName(String land)
}
Here is my cache configuration:
#Configuration
#EnableCaching(mode=AdviceMode.ASPECTJ)
public class EhCacheConfiguration extends CachingConfigurerSupport {
...
Note that i'm using AdviceMode.ASPECTJ (compile time weaving). Unfortunately caching is not working when calling the repo method 'findByName'.
Changing the caching mode to AdviceMode.PROXY all works fine.
To ensure that caching works in principle with aspectJ, i wrote the following service:
#Service
public class LandService {
#Autowired
LandDao landDao;
#Cacheable("landCache")
public Land getLand(String bez) {
return landDao.findByName(bez);
}
}
In this case the cache works like a charm. So i think that all parts of my application are correctly configured and the problem is the combination of spring-data-jpa and AspectJ caching mode. Does anyone have an idea what's going wrong here?
Okay, found the answer to my question by myself. The javadoc of the responsible aspect org.springframework.cache.aspectj.AnnotationCacheAspect says:
When using this aspect, you must annotate the implementation class (and/or methods within that class), not the interface (if any) that the class implements. AspectJ follows Java's rule that annotations on interfaces are not inherited.
So it's not possible to use the #Cacheable annotation inside repository interfaces together with aspectj. My solution now is to make use of custom implementations for Spring Data repositories:
Interface for custom repository functionality:
public interface LandRepositoryCustom {
Land findByNameCached(String land);
}
Implementation of custom repository functionality using query dsl:
#Repository
public class LandRepositoryImpl extends QueryDslRepositorySupport
implements LandRepositoryCustom {
#Override
#Cacheable("landCache")
public Land findByNameCached(String land) {
return from(QLand.land).where(QLand.land.name.eq(land)).singleResult(QLand.land);
}
}
Note the #Cacheable annotation for the findByNameCached method.
Basic repository interface:
public interface LandRepository extends CrudRepository<Land, Long>, LandRepositoryCustom {
}
Using the repository:
public class SomeService {
#Autowired
LandRepository landDao;
public void foo() {
// Cache is working here:-)
Land land = landDao.findByNameCached("Germany");
}
}
It would be helpful to add a note relating to this limitation in the spring data reference.

Spring JPA: How to get dependencies right with injection?

My data model consists of three objects, let's call them A, B and C. A has a one-to-many relationship to B, and B to C. They are only modeled as interfaces.
To get concrete versions of them, I have interfaces AProvider, BProvider and CProvider which provide create, retrieve, and delete operations.
I am now doing an implementation using Spring-JPA and Hibernate by the means of spring-boot-starter-data-jpa. For this, I have three #Entitys InternalA, InternalB and InternalC which do not implement the A, B and C interfaces, but are used as transfer objects only. To access them, I use the autogenerated repositories from Spring (see CrudRepository).
For creating the "real" objects, I have implementations of the XProvider interfaces which inject their necessary dependencies. This structure is the following (I prefer javax.inject style injection):
#Component
public class AProviderImpl implements AProvider {
#Inject
private InternalARepository _aRepository;
// implementation
}
#Component
public class BProviderImpl implements BProvider {
#Inject
private InternalBRepository _bRepository;
#Inject
private AProvider _aProvider;
// implementation
}
#Component
public class CProviderImpl implements CProvider {
#Inject
private InternalCRepository _cRepository;
#Inject
private BProvider _bProvider;
// implementation
}
I figure out that this should work, and the AutowireCapableBeanFactory correctly figures out what to instantiate first. But this only works up to the BProviderImpl, e.g., when removing the CProviderImpl. As soon as CProviderImpl exists, initialization fails with No qualifying bean of type [com.somewhere.BProvider] found.
It makes no difference if I use #Autowired instead of #Inject.
I stepped through the initialization process with a debugger and the CProvider is indeed initialized first, i.e., the bean factory does not correctly figure out that it needs the BProvider which in turn needs the AProvider first.
My current workaround is using #DependsOn like this:
#Component("myAProvider")
public class AProviderImpl implements AProvider { ... }
#Component("myBProvider")
#DependsOn("myAProvider")
public class BProviderImpl implements BProvider { ... }
#Component("myCProvider")
#DependsOn("myBProvider")
public class CProviderImpl implements CProvider { ... }
This works, but I read elsewhere that this is a code smell and should be avoided because it introduces implicit dependencies. Currently, this is all local to one module, so it is no problem there, but later my model will grow and I will have model elements and providers spread over multiple modules, so I cannot use #DependsOn until kingdom come.
Is there a better way to tackle this?
You can use the #Order annotation, this will enable to prioritize Bean loading and doesn't pollute your loading hierarchy as with #DependsOn. With this you can certainly modularize your design later without a problem. Lowest order value has highest priority.
#Component("myAProvider")
#Order(1)
public class AProviderImpl implements AProvider { ... }
#Component("myBProvider")
#Order(2)
public class BProviderImpl implements BProvider { ... }
#Component("myCProvider")
#Order(3)
public class CProviderImpl implements CProvider { ... }
I found another workaround: Lazy initialization. Since initialization of my XProviders are not long-running, I just added the #Lazy annotation. That way, Spring creates uninitialized instances and adds them to the registry, and when at a later point an injected dependency is accessed, it is initialized.
I don't know if this is the best solution, but I think it is better than using #DependsOn and it should be working over module boundaries.

Is it right to use spring DI without interface

I am using spring dependency injection,where i can inject object dependency through some external xml file.
My question is :
Is it fine to use spring DI without using interfaces?
Because with DI,we want to achieve one thing :
If a class is being replaced by some other class having same methods but different definitions,then we don't need to make any change in code where this class is being referenced.
This is fine if i am using interface as interface can point any class which is implementing this interface but if i am directly injecting class object through DI then there is no meaning of DI because in this case if class get replaced,i have to change my code also where it is being referenced.
Please let me correct if something is wrong.
Let's say i have
Class Datasource{
String url;
String user;
String database;
}
Now i am using it without DI as
Class Abc{
Datasource datasource = new Datasource();
}
what's the problem in this and what are the benefits i can get if i use DI.
Is getting singleton object only goal of DI?
Dependency Injection isn't about interfaces or classes or enums or... It is about Inversion of Control.
Imagine the following class.
public class PersonService {
private PersonRepository repository = new PersonRepository();
}
Apparently there is nothing wrong with this. However what if PersonRepository needs other dependencies, what if it takes another complex object as construct argument. Suddenly the PersonService is burdend with the logic on how to construct an object and all of its dependencies. Whereas it only wants to use the object.
public class PersonService {
private PersonRepository repository;
public PersonService() {
InitialContext ctx = new InitialContext();
repository = ctx.lookup("java:comp/env/repositories/person");
}
}
The code above is tied to JNDI, how are you going to test it (easily) of course you can construct your own Mock JNDI service and pre configure that with a constructed or mocked repository but that is quite cumbersome.
public class PersonService {
private final PersonRepository repository;
public PersonService(PersonRepository repository) {
this.repository=repository;
}
}
The above makes basically everything possible, there is no burder on the PersonService on how to construct the PersonRepository it is just handed one, where it comes from it doesn't matter. Is it the actual class or a (class-based) proxy, is doesn't care.
Hence dependency injection, you want to hand the PersonRepository to use to the PersonService it shouldn't matter to it where it comes from, how it is constructed or if it is a proxy to an actual object. It just needs a PersonRepository.

Understanding the Spring Data JPA #NoRepositoryBean interface

I encountered the #NoRepositoryBean interface several times whilst reading the Spring Data documentation.
To quote from the documentation:
If you're using automatic repository interface detection using the
Spring namespace using the interface just as is will cause Spring
trying to create an instance of MyRepository. This is of course not
desired as it just acts as indermediate between Repository and the
actual repository interfaces you want to define for each entity. To
exclude an interface extending Repository from being instantiated as
repository instance annotate it with #NoRepositoryBean.
However, I am still not sure when and where to use it. Can someone please advise and give me a concrete usage example?
The annotation is used to avoid creating repository proxies for interfaces that actually match the criteria of a repo interface but are not intended to be one. It's only required once you start going into extending all repositories with functionality. Let me give you an example:
Assume you'd like to add a method foo() to all of your repositories. You would start by adding a repo interface like this
public interface com.foobar.MyBaseInterface<…,…> extends CrudRepository<…,…> {
void foo();
}
You would also add the according implementation class, factory and so on. You concrete repository interfaces would now extend that intermediate interface:
public interface com.foobar.CustomerRepository extends MyBaseInterface<Customer, Long> {
}
Now assume you bootstrap - let's say Spring Data JPA - as follows:
<jpa:repositories base-package="com.foobar" />
You use com.foobar because you have CustomerRepository in the same package. The Spring Data infrastructure now has no way to tell that the MyBaseRepository is not a concrete repository interface but rather acts as intermediate repo to expose the additional method. So it would try to create a repository proxy instance for it and fail. You can now use #NoRepositoryBean to annotate this intermediate interface to essentially tell Spring Data: don't create a repository proxy bean for this interface.
That scenario is also the reason why CrudRepository and PagingAndSortingRepository carry this annotation as well. If the package scanning picked those up by accident (because you've accidentally configured it this way) the bootstrap would fail.
Long story short: use the annotation to prevent repository interfaces from being picked up as candidates to end up as repository bean instances eventually.
We can declare a new interface as our custom method:
#NoRepositoryBean
public interface ExtendedRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
List<T> findByAttributeContainsText(String attributeName, String text);
}
Our interface extends the JpaRepository interface so that we'll benefit from all the standard behavior.
You'll also notice we added the #NoRepositoryBean annotation. This is necessary because otherwise, the default Spring behavior is to create an implementation for all subinterfaces of Repository.
public interface ExtendedStudentRepository extends ExtendedRepository<Student, Long> {
}

Spring Data MongoDB tries to generate queries for custom repository methods

Based on the Spring Data Document documentation, I have provided a custom implementation of a repository method. The custom method's name refers to a property which doesn't exist in the domain object:
#Document
public class User {
String username;
}
public interface UserRepositoryCustom {
public User findByNonExistentProperty(String arg);
}
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
#Override
public User findByNonExistentProperty(String arg) {
return /*perform query*/;
}
}
public interface UserRepository
extends CrudRepository<?, ?>, UserRepositoryCustom {
public User findByUsername(String username);
}
However, perhaps because of the method name I've chosen (findByNonExistentPropertyName), Spring Data attempts to parse the method name, and create a query from it. When it can't find the nonExistentProperty in User, an exception is thrown.
Possible resolutions:
Have I made a mistake in how I provide the implementation of the custom method?
Is there a way to instruct Spring to not attempt to generate a query based on this method's name?
Do I just have to avoid using any of the prefixes that Spring Data recognizes?
None of the above.
Thank you!
Your implementation class has to be named UserRepositoryImpl (if you stick to the default configuration) as we try to look it up based on the Spring Data repository interface's name being found. The reason we start with that one is that we cannot reliably know which of the interfaces you extend is the one with the custom implementation. Given a scenario like this
public interface UserRepository extends CrudRepository<User, BigInteger>,
QueryDslPredicateExecutor<User>, UserRepositoryCustom { … }
we would have to somehow hard code the interfaces not to check for custom implementation classes to prevent accidental pick-ups.
So what we generally suggest is coming up with a naming convention of let's say the Custom suffix for the interface containing the methods to be implemented manually. You can then set up the repository infrastructure to pick up implementation classes using CustomImpl as suffix by using the repository-impl-postfix attribute of the repositories element:
<mongo:repositories base-package="com.acme"
repository-impl-postfix="CustomImpl" />
There's more information on that in the reference documentation but it seems you have at least briefly checked that. :)

Categories

Resources