How Are Spring Data Repositories Actually Implemented

How are Spring Data repositories actually implemented?

First of all, there's no code generation going on, which means: no CGLib, no byte-code generation at all. The fundamental approach is that a JDK proxy instance is created programmatically using Spring's ProxyFactory API to back the interface and a MethodInterceptor intercepts all calls to the instance and routes the method into the appropriate places:

  1. If the repository has been initialized with a custom implementation part (see that part of the reference documentation for details), and the method invoked is implemented in that class, the call is routed there.
  2. If the method is a query method (see DefaultRepositoryInformation for how that is determined), the store specific query execution mechanism kicks in and executes the query determined to be executed for that method at startup. For that a resolution mechanism is in place that tries to identify explicitly declared queries in various places (using @Query on the method, JPA named queries) eventually falling back to query derivation from the method name. For the query mechanism detection, see JpaQueryLookupStrategy. The parsing logic for the query derivation can be found in PartTree. The store specific translation into an actual query can be seen e.g. in JpaQueryCreator.
  3. If none of the above apply the method executed has to be one implemented by a store-specific repository base class (SimpleJpaRepository in case of JPA) and the call is routed into an instance of that.

The method interceptor implementing that routing logic is QueryExecutorMethodInterceptor, the high level routing logic can be found here.

The creation of those proxies is encapsulated into a standard Java based Factory pattern implementation. The high-level proxy creation can be found in RepositoryFactorySupport. The store-specific implementations then add the necessary infrastructure components so that for JPA you can go ahead and just write code like this:

EntityManager em = … // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);

The reason I mention that explicitly is that it should become clear that, in its core, nothing of that code requires a Spring container to run in the first place. It needs Spring as a library on the classpath (because we prefer to not reinvent the wheel), but is container agnostic in general.

To ease the integration with DI containers we've of course then built integration with Spring Java configuration, an XML namespace, but also a CDI extension, so that Spring Data can be used in plain CDI scenarios.

how annotation @Repository in java spring work?

Spring Repository is responsible for importing the DAO's into the DI container and also it makes the unchecked exceptions into Spring DataAccessException. The Spring Repository annotation is meta annotated with the @Component annotation so that the repository classes will be taken up for component scanning.

Teams implementing traditional Java EE patterns such as "Data Access
Object" may also apply this stereotype to DAO classes, though care
should be taken to understand the distinction between Data Access
Object and DDD-style repositories before doing so. This annotation is
a general-purpose stereotype and individual teams may narrow their
semantics and use as appropriate.

A class thus annotated is eligible for Spring DataAccessException
translation when used in conjunction with a
PersistenceExceptionTranslationPostProcessor. The annotated class is
also clarified as to its role in the overall application architecture
for the purpose of tooling, aspects, etc.

Source: JavaDoc

but in your case you are also extending the JpaRepository of Spring Data JPA. Spring Data automatically provides implementations of common CRUD operations. The JpaRepository extends the interface CrudRepository which has the methods declared for all basic crud operations.

public interface EquipmentRepository extends JpaRepository<Account, Long> { … }

Defining this interface serves two purposes:

  • First, by extending JpaRepository we get a bunch of generic CRUD
    methods into our type that allows saving Equipments, deleting them and
    so on.
  • Second, this will allow the Spring Data JPA repository infrastructure
    to scan the classpath for this interface and create a Spring bean for
    it.

The @EnableJpaRepositories scans all packages below com.acme.repositories for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository (spring data provides default imlpementations of CRUD repository through this class).

So that is why even when you haven't defined the method , you are able to do crud operations through this setup.

Refer : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.repositories

How spring picks up the implementation for interface at run time

What @sairamch04 said is alright. I can only add, that when query is defined via @Query annotation or using standard jpa method name, a proxy object is created to execute the query. The creation of these proxies is within factory objects, such as JpaRepositoryFactory mentioned. Otherwise it falls back to implementation such as SimpleJpaRepository. For more information about repository, you can call RepositoryFactorySupport.getReporitory() method in spring-data-commons.

How are the methods: findAll(), save() etc implemented in spring-data?

There's no way to answer your question with just the information you've provided. That's not a bad thing. That's a very good thing. That's the power of interfaces and of Spring's most basic ability, dependency injection. The whole idea behind both of those concepts is that your code, and maybe even the programmer, doesn't know anything about the implementation of a particular interface. There can be a few or 100s of unique implementations of that interface, and each one of those means at least a slightly different answer to your question.

Another way to "answer" your question is to say "The implementation of the interface you're interacting with in your code is defined by whichever concrete implementation of that interface you've configured to actually perform that role.".

In the case of Spring Boot, the answer to your question is often defined by your application's package dependencies, usually defined via a Maven or Gradle project file. Here, for example, are the lines you'd add to a Gradle project definition to cause Spring Boot to implement your CRUD operations using JPA and MySQL:

compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile("mysql:mysql-connector-java:5.1.13")

If you have a working app, then the answer to your question is probably determined by lines similar to these in your Maven or Gradle file or in the equivalent definition of your project per whatever method you are using to define it. If you don't have a working app, then maybe you haven't yet even chosen a backing implementation, and your question literally has no answer.

If you want a more definitive answer, why don't you post more details about your app, like the code, and more importantly, your Maven or Gradel project file.

Spring Data JPA underlying mechanism without implementation

(1) How does it work when those methods in repository interfaces have
no implementations and those are not inherited from any super class?

The Repository interfaces are being implemented (backed up) by Spring Container at Runtime.

(2) Does it work with name conventions and reflections?

Yes, it works on naming conventions and spring container uses JDK's proxy classes to intercept the calls to the Repository.

(3) Does Spring Data has inmemory database to work with?

No, Spring does not use any inmemory database

Please refer the below link for more detailed explanation:

How are Spring Data repositories actually implemented?

Spring JPA custom repository implementation does not work

I'm going to answer my own question after my investigation.

It is not obvious (at least not to me) in Spring's documentation here that the CustomRepository/Impl mechanism must only be used for a single repository. If you want to create some custom implementation to be inherited by multiple repositories, you'll have to customize the base repository, which is going to be used to back all repository beans.

So I ended up adding an int myTestMethod(OffsetDateTime threshold) implementation to the base repository impl BaseRepositoryImpl. This method will be used to back the method declared in MyEntityDataRepository which extends TestRepository. Note: you must let your repository to extend the interface that declares the custom method. Otherwise, the function in the base repository impl will not be available to the repository bean which is a proxy for the interfaces only, not the base repository impl.

Also, you can actually override the base repository's implementation if you customize the same method in the entity repository.

This is not ideal, but it works. I'd hope I can restrict the custom method availability to some repositories only. One way to do that is to split the different "groups" of repositories into separate disjoint packages and declare a distinct base-class for each separate search path. But that may not make sense in many cases.

What implementation will be used for PagingAndSortingRepository in runtime?

The implementation for the methods declared in the PagingAndSortingRepository are defined in SimpleJpaRepository.

If all what you want to do within a transaction is handled by a single repository call you don't need any extra @Transactional, but typically you do want the transaction to cover more then one call or at least a load operation and the manipulation of an entity afterwards. In those cases you would typically use a method annotated with @Transactional. But you could also use for example a TranactionTemplate which can be nice for example in certain kind of tests.

Quarkus extension for spring data API - automatic repository implementation generation

Answering to my own question.

I have opened an issue in github. And it turns out that @RepositoryDefinition isn't supported at the moment. But there is a good chance that it will be supported soon. You can follow the issue here.



Related Topics



Leave a reply



Submit