How to avoid lazy initialization of spring-data repositories? - spring

I have a bean post processor (RepoRegistry) that picks up spring-data repositories and puts them i a map to make them available by type (repoRegistry.getRepositoryFor(MyEntity.class)). It seems as if the repo beans are created lazily. As a workaorud I now have to autowire all repositories manually somwhere so that they get created and processed by the post processor.
Is there another way to declare that some (or at least all) spring data repositories are declared non-lazy? I tried to add #Lazy to the repository interfaces without success.

You're probably seeing this on a Spring Data version that is older than the Codd release train (e.g. Spring Data JPA 1.4.x). The Codd release train (and thus Spring Data JPA 1.5) switched this model to eager init by default. Prior to that, repository beans were only create if there had been an injection target.
We generally recommend to use the Repositories type we already provide with Spring Data Commons to obtain all repositories contained in a BeanFactory:
Repositories repositories = new Repositories(applicationContext);
repositories.getRepositoryFor(Person.class);

Add this method to RepoRegistry:
#Autowired
public void setReps( List<Repository> repos ) {...}
Note: I haven't used Spring Data, yet. You need to use the common interface type of all repos as type argument of the List.
Spring will then collect all beans which implement Repository and pass them into the setter as a list.

Related

Why creation of repository beans need a datasource bean during start-up

I was working on a Spring-data-jpa project with spring boot, I see that the creation of repository beans required a datasoure bean to be present, Why is it so?
And can a repository bean be created without datasource bean.
The purpose of a repository is to load and save data into a persistent store.
Spring Data JPA does that using JPA so it needs an EntityManager which in turn need a DataSource.
Strictly speaking the DataSource is only used once the database is actually accessed.
While you definitely nee a DataSource bean you may delay the construction of a normal DataSource by providing a wrapper which instantiates the the actual DataSource at a later point in time.
DelegatingDataSource might be of help, either as a basis class or, since you are going to change the DataSource as a template for an implementation.
See the somewhat related question https://stackoverflow.com/a/61208585/66686

Adding new DB support in spring data

Currently spring data has multiple db support (mysql, cassandra, mongo.. very big list), however i want to add my custom repository from the scratch like adding custom db support in spring data. I don't want to extend any existing repositories, instead I want to create a parallel repository strutcutre for my custom datasource. Looking at current implementation it looks like tedious task. It would be a great if someone could help me with minimal requirement to do this.
You could create a repository annotated bean where you would inject EntityManager or the proper bean that is acting like that, depending on database type that you are using.
#Repository
public class MyCustomRepositoryImpl implements MyCustomRepository {
#Autowired
private EntityManager entityManager;
//the methods that you are going to create.
}
For more details see:
https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
Chapter: 1.3 Custom implementations for Spring Data repositories

How to use multiple spring-boot-starter-data-* dependencies

Taking an example: I want some entities to be persisted in MongoDB and some in Cassandra.
I have my repository interfaces extending CrudRepository. My Cassandra entities have #Table and my MongoDb entities have #Document annotations.
However, on startup, spring-data attempts to create an instance of a MyMongoObjectRepository, and thus complains that "Cassandra entities must have the #Table, #Persistent or #PrimaryKeyClass Annotation".
How are the libraries discovering which repository interfaces they are supposed to implement, and how can I control them so they don't try to implement them for unsupported entities?
Further question: if I wanted to store some entities in both storage systems, can multiple implementations of a single repository be generated, or do I need an interface for each store?
Edit
On further inspection, the problem seems to be from the entity scanning rather than the repository scanning. Both mappers pick up all the entities (as their annotations all extend #Persistent). One of the Mongo entities has a nested entity (without any annotations) that the Cassandra mapper cannot deal with.
You can use a basePackages setting in #EnableMongoRepositories and #EnableJpaRepositories to specify where they should look for repository definitions.
Like so:
#EnableMongoRepositories(basePackages={
"com.some.package.to.look.inside",
"com.some.package.to.look.also.at"
})
And
#EnableJpaRepositories(basePackages={
"com.some.differentpackage.to.look.inside",
"com.some.differentpackage.to.look.also.at"
})
For this to work you need to namespace your repository definitions in sensible packages.
Answer to your follow up question:
If you wanted to store entities multiple places at once I would implement a service in front of the repositories making use of #Autowire to dependency inject the repositories and setting a #Transactional on the service method which calls repository methods. Having #Transactional on the service method ensures that if an error would occur while saving it will ensure that no half-way saves are left laying around, even doing rollbacks if necessary.
Edit:
#Transactional does not work for db's that do not support transactions like Cassandra and MongoDB.
Problem is that all the different entity scanners use #Persistent as an annotation they're looking for, while all the repo-specific annotations (#Table, #Document, etc.) also have #Persistent as a meta-annotation.
Therefore, the entities for the different repositories must be in separate packages, and you must construct your own scanner in order to pass the packages to it, as it doesn't not accept a generic filter.

Play Framework + Spring, dependency injection to Global object

I use Play Framework + Spring Data JPA from this example:
https://github.com/typesafehub/play-spring-data-jpa
I have models, repositories and now I want to initialize my DB on start up. I inject repositories to my Global.java file using #Autowired, but they equals to null on run time. It's because Spring don't try to inject dependencies to Global.java, because Global.java don't have even package name, so I can't tell Spring to scan his package!
With controllers Spring DI works well, but how to use it in Global.java? How to initialize database using repositories?
You can change package of Global.java in application.conf by setting property application.global.
Other way can be to fetch dependency explicitly calling
context.getBean(YourBeanClass)
in Global.java instead of autowiring it, as you have context available in Global.
You may explicitly create a bean by defining it in your context xml
<bean id="global" class="Global"></bean>
Also, having classes in default packages is not a good idea.

Combine two maven based projects on two frameworks

I have two maven projects say MvnSpring and MvnGuice.MvnSpring is working on spring and hibernate frame works.
And MvnGuice is working on google guice and mybatis. I need to combine both the features together.
Both are following singleton pattern. I need to get some class of MvnSpring in MvnGuice while coding. So that I created a jar of MvnSpring and put it in .m2 repository and give the dependacy details in MvnGuice. Now I can import classes of MvnSpring in MvnGuice classes.MvnSpring uses spring dependency injection and MvnGuice uses guice dependency injection for object creation. Now in MvnSpring flow is MSserviceImpl(implements MSservice) > MSdaoImpl(implements MSdao). Now I need to call MSService class from MvnGuice. Then at run time it shows error like MSService class is null. Then I made a guice dependency injection for MSService class in MvnGuice. Now the control reaches MSserviceImpl but now MSdao is null at here. Is it possible to start MvnSpring along with MvnGuice. I hope then I can solve the issue.
While Spring and Guice are targeted at the same problem, IoC, they take very different approaches to solve it. They differ both in functionality and in how they are configured, where Spring has bean definitions and Guice uses bindings.
Fortunately they do have common grounds in that they both support JSR-330, a standards specification that defines a set of annotations. This enables you to write your singletons and describe the injections that they need without depending on either Spring or Guice.
This way you can share your singletons between projects irregardless of the framework you use in a particular project. I would not recommend using both Guice and Spring in the same project, except if there's a clearly defined separation between them. For instance you might use Guice for a module that is used by Spring code via a defined API that hides the fact that it internally is based on Guice.
There was already mentioned JSR-330.
For some cases it can be not enough, e.g., you have code:
final String className = config.getProperty(«serviceImpl»);
// Class.forName(name) and check required interface for type safety
final Class<? extends Service> serviceClass = Reflection.classForName(className, Service.class);
final Service service = injector.getInstance(serviceClass);
In different DI environments you are supposed to support both com.guice.inject.Injector.getInstance() and org.springframework.context.ApplicationContext.getBean() implementations.
There is the draft solution sdif4j Simple Dependency Injection Facade.
The idea of this project is to encapsulate different DI frameworks logic with own abstraction to extend default JSR-330 possibilities. Note, there is no public releases yet, but you can find ideas how to solve your problem or make an internal release in a fork.
The general issue, is that your both MvnSpring and MvnGuice projects are supposed to be based on JSR-330 (instead of guice/spring annotations) and org.sdif4j:sdif4j-api (or your own abstraction; only if Injector functionality is required). It is recommended to make guice and spring dependencies optional (to compile but not export) to allow the library clients to choose the DI themselves.
In your MvnCompineGuiceAndSpring you just declare sdif4j-guice or sdif4j-spring dependency (it is similar to slf4j usage) and configure your DI environment. You can find different examples in testing subproject.
Some more notes:
Spring default scope is singleton, Guice - prototype (Spring terminology). So, if you want a prototype bean, you can use:
#org.springframework.context.annotation.Scope("prototype")
#javax.inject.Named
public class TestPrototype {
}
The Spring #Scope annotation should be ignored by guice even if spring does not present in your classpath.
Also you have to declare all your Singleton beans with #javax.inject.Named and #javax.inject.Singleton annotation to support both Spring and Guice, like this:
#javax.inject.Named
#javax.inject.Singleton
public class TestSingleton implements ITestSingleton {
public TestSingleton() {
}
}
As with #Scope annotation, you can use #ImplementedBy(#ProvidedBy) guice annotations on your code (when feasible; be careful with it, in general it is not a good practice), that should be also ignored in Spring DI (in both cases if Spring exists in classpath or not).
Hope, that's clear.

Resources