0

When I try to use the same POJO for Spring Data JPA integration with Spring Data GemFire, the repository always accesses the database with the POJO. But I want the repository to access data from GemFire, even though I added annotations @EnableGemfireRepositories and @EnableEntityDefinedRegions.

I think it is because I added the @Entity and @Region together on the same POJO class.

Please help fix and let me know if I can do so? Do I need to separate it into 2 POJO classes working for database and GemFire?

Thanks

John Blum
  • 7,381
  • 1
  • 20
  • 30
Luis
  • 11
  • 1

1 Answers1

1

No, you do not need 2 separate POJOs. However, you do need 2 separate Repository interface definitions, 1 for JPA and a 2nd for GemFire. I have an example of such an implementation here, in the repository-example.

In the contacts-core module, I have an example.app.model.Contact class that is annotated with both JPA's @Entity annotation as well as SDG's @Region annotation in addition to other annotations (e.g. Jackson).

I then create 2 Repository interfaces in the repository-example module, 1 for JPA, which extends o.s.d.jpa.repository.JpaRepository, and another for GemFire, which extends o.s.d.gemfire.repository.GemfireRepository. Notice too that these Repositories are separated by package (i.e. example.app.repo.jpa vs. example.app.repo.gemfire) in my example.

Keep in mind, Spring Data enforces a strict policy mode which prevents ambiguity if the application Repository definition (e.g. ContactRepository) is generic, meaning that the interface extends 1 of the common Spring Data interfaces: o.s.d.repository.Repository, o.s.d.repository.CrudRepository or perhaps o.s.d.repository.PagingAndSortingRepository, and the interface resides in the same package as the "scan" for both JPA and GemFire. This is the same for any Spring Data module that supports the Repository abstraction, including, but not limited to, MongoDB and Redis.

You must be very explicit in your declarations and intent. While it is generally not a requirement to extend store-specific Repository interface definitions (e.g. o.s.d.gemfire.repository.GemfireRepository), and rather extend a common interface (e.g. o.s.d.repository.CrudRepository), it is definitely advisable to put your different, per store Repository definitions in a separate package and configure the scan accordingly. This is good practice to limit the scan in the first place.

Some users are tempted to want a single, "reusable" Repository interface definition per application domain model type (e.g. Contact) for all the stores they persist the POJO to. For example, a single ContactRepository for both JPA and GemFire. This is ill-advised.

This stems from the fact that while most stores support basic CRUD and simple queries (e.g. findById(..)), though not all (so be careful), not all stores are equal in their query capabilities (e.g. JOINS) or function (e.g. Paging). For example, SDG does not, as of yet, support Paging.

So the point is, use 1 domain model type, but define a Repository per store clearly separated by package. Then you can configure the Spring Data Repository infrastructure accordingly. For instance, for JPA I have a configuration which points to the JPA-based ContactRepository using the ContactRepository class (which is type-safe and better than specifying the package by name using the basePackages attribute). Then, I do the same for the GemFire-based ContactRepository here.

By following this recipe, then all is well and then you can inject the appropriate Repository (by type) into the service class that requires it. If you have a service class that requires both Repositories, then you must inject them appropriately, for example.

Hope this helps!

John Blum
  • 7,381
  • 1
  • 20
  • 30
  • Thanks very much for your answers and examples, John, now when I use GemfireRepository, it throws the Exception, tells "No property save found for type [my pojo Class] – Luis Mar 28 '18 at 04:30
  • @John Blum, even i am facing issue same as Luis .can you please try to run your application, you might find runtime exception as Luis is mentioning – Uday Singh Apr 16 '20 at 08:14
  • Sorry, I just saw this/these comment(s). I have not updated and ran this GH repository example in awhile. I meant it more as a reference for the fact it is possible. I also have a Multi-Store Smoke Test in the SBDG project you can refer to as well (https://github.com/spring-projects/spring-boot-data-geode/tree/master/spring-geode-tests/smoke-tests/multi-store). Let me update the Contacts Application and play around with my old example. I will followup afterwards. Thank you for your patience. – John Blum Apr 21 '20 at 19:07
  • i think i found the issue, i was referring crud repository, which you already metioned in explanation, we should use "jpa repository" Luis might be facing issue due to similar thing i guess, Thanks your example works!! – Uday Singh Apr 22 '20 at 07:02
  • @John Blum i see gemfire function also given in CustomerFunctions, but CustomerFunctionExecutions is not exactly calling the same method, which had been annotated by GemfireFunction public List findAll, 1) can you please explain what is the naming convention in execution interface? 2) does GemfireFunction gets registered/works with ClientCacheApplication, if one is having ClientCacheApplication only ? – Uday Singh May 27 '20 at 08:43
  • @JohnBlum When I use JpaRepository and GemfireRepository for a class annotated with @Entity and @Region it fails with `Caused by: org.springframework.data.mapping.PropertyReferenceException: No property save found for type Entity!` what is wrong here? – eatSleepCode Nov 19 '20 at 10:55