how to generalize greatest-n-per-group within Spring Boot Data Jpa repositories - spring-data-jpa

In the context of a project using spring boot jpa with a lot of "historized" data, I would like to generalize the greatest-n-per-group to get particular data in time range (like the most recent at a given date).
Historical entities implement a special interface with a date range contract (getStartDate() and getEndDate()).
I know how to handle manually this kind of queries (the greatest-n-per-group problem), either with the query generator or with #Query, but but what if I want to generalize this to avoid repeating over and over the same piece of code in my repo ?

Related

Hibernate search 6 : updating EmbededIndex from another Microservice

Good morning,
I have a rather special scenario and I would like to have your opinion on the best way to handle this situation.
We have an application divided into several functional microservices, but a common database (it's not ideal but for the moment we have no choice).
From a microservice A, I index entity A with entities B, C and D, like IndexedEmbeded.
1- if I make modifications on A, by changing B or C or D, is it automatically propagated in the indexing document or does it require additional configuration?
2- the tables of entities B, C and D are updated by other microservices and in this case I have to update my index of entity A. What is the best way to do this?
I thought of doing manual indexing trimmed every change in the other microservices. but I'm not sure that's the best way to do it.
Thank you
I'll state the obvious and say that if you use the same model across microservices, you're in for some headaches, especially when updating your schema, but I guess you know that and can't do anything about it. So, let's have a look at solutions...
if I make modifications on A, by changing B or C or D, is it automatically propagated in the indexing document or does it require additional configuration
Assuming everything happens in the same microservice, and the updates are performed using Hibernate ORM (and not native SQL), it should be automatic. See https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#mapper-orm-reindexing-basics .
the tables of entities B, C and D are updated by other microservices and in this case I have to update my index of entity A. What is the best way to do this?
Assuming your other microservices share the same Hibernate ORM mapping (they know of entity A, they just don't deal with it), e.g. they all import a common JAR that contains your annotated entities... you could simply rely on outbox-polling coordination, which allows multiple instances of an application (or of different applications with the same model/mapping) to cooperate and index safely and reliably, as long as they all use Hibernate Search with compatible configuration.
If that's not your case, e.g. each microservice has its own entity classes, and may not include all entities from other microservices... I'm afraid Hibernate Search can't solve that problem for you (yet). Hibernate Search exposes a way to trigger reindexing based on entity events (entity created, entity property 'foo.bar' updated, entity deleted, ...) that you input manually, via the SearchIndexingPlan, but you will have to devise a way to propagate these events from one microservice to another. And that kind of makes Hibernate Search a lot less useful, unfortunately.

JPA Entity CRUD type Operations support in MyBatis

Due to some odd reason, I cannot go with JPA vendor like Hibernate, etc and I must use MyBatis.
Is there any implementation where we can enrich similar facility of CRUD operation in Mybatis?
(Like GenericDAO save, persist, merge, etc)
I have managed to come up with single interface implementation of CRUD type of operations (like Generic DAO) but still each table has to write it's own query in XML file (as table name, column names are different).
Will that make sense to come up with generic implementation?
Where I can give any table object for any CRUD operation through only 4 XML queries. (insert, update, read, delete) passing arguments of table name, column names, column values..etc.
Does it look like re-inventing the wheel in MyBatis or does MyBatis has some similar support?
you can try Mybatis Plus.This is for these cases.
MyBatis is not an ORM, instead it maps the result from SQL statements to objects.
You need to write SQL.
You will have a hard time if you try and apply the JPA model to working in MyBatis. You need to learn how MyBatis works instead.
You may be interested in the MyBatis Generator. Here is a screenshot of the introduction paragraph.
And here is the URL.
The generator looks at the Physical tables in an RDBMS and generates the CRUD mapping.That is half the job done. The other half is to utilize these mappings in your actual code.
Let this assumption also be cleared. The generator generates only the CRUD. For more complex operations like aggregations or joins et al, you may need to write the mappers on your own.

Spring data repository and DAO Java Generics

Reading about using Java Generics in DAO layer, I have a doubt applying this in spring data repositories. I mean, with spring data repositories, you have something like this:
public interface OrderRepository extends CrudRepository<Order,OrderPK>{
}
But if I have other 10 entities, I have to create 10 interfaces like the one above to execute CRUD operations and so on and I think this is not very scalable. Java Generics and DAO is about creating one interface and one implementation and reuse this for entities but with Spring Data repositories I have to create one interface for each entity so ...
You didn't really state a question, so I just add
Is this really true? And if so, why?
and answer it:
Yes, this is (almost) correct. Almost, because you should not create one repository per entity, but one repository per Aggregate Root. See http://static.olivergierke.de/lectures/ddd-and-spring/
Spring Data Repositories offer various features for which Spring Data needs to know, what entity it is dealing with. For example query methods need to know the properties of the entity, in order to convert the method name to JPA based query. So you have to pass in the information to Spring Data at some point and you also have to pass in the information, which entities should be considered Aggregate Roots. The way you do that, is by specifying the interface.
Do you really need that? Well if all you want is generic Crud functionality, you can get that straight out of the box with JPA. But if you want query methods, Pagination, simple native queries and much more Spring Data is a nice way to avoid lots of boiler-plate code.
(Please keep in mind that I'm biased)

Is there a mismatch between Domain-Driven Design repositories and Spring Data ones?

DDD specifies repository per aggregate, but when embracing Spring Data JPA, we can leverage the benefits only when we declare interface per entity. How this impedance mismatch can be resolved?
I'm hoping to try out repository interfaces encapsulated within the aggregate repository, is that a OK solution or anything better available?
To given an example: Customer is the aggregate root and entities are like Demographics, Identification, AssetSummary etc. where each entity can benefit from having their own repository interfaces. What is the best way without violating DDD much?
…, but when embracing Spring Data JPA, we can leverage the benefits only when we declare interface per entity…
That's wrong and I would like to learn where you get this impression from (feel free to comment). Spring Data repositories are expecting the exactly same approach to your domain model design: you identify aggregates in your domain model and only create repository interfaces for exactly those.
I'd argue that all you need to do is applying the DDD concept to your domain model. Simply don't declare repository interfaces for entities that are not an aggregate root. In fact, if you declared those, you basically break the concept of an aggregate, as the actual root cannot control business constraints anymore as the other entities can be manipulated through the repository interface defined for them, i.e. without using the aggregate root.
Find an example of this applied correctly in this Spring Data example. In it, Order is an aggregate root, LineItem is just an ordinary entity. The same applies to Customer (root) and Address (ordinary entity). Repository interfaces only exist for the aggregate roots.
In fact, that particular relationship is the fundamental principle that makes modules like Spring Data REST working in the first place. It only exposes HTTP resources for aggregate roots, embeds ordinary entities within the representations created and creates links to other aggregates.

Combining Spring Data query builder with Spring Data JPA Specifications?

Spring Data allows you to declare methods like findByLastname() in your repository interface and it generates the queries from the method name automatically for you.
Is it possible to somehow have these automatically-generated queries also accept a Specification, so that additional restrictions can be made on the data before it's returned?
That way, I could for example call findByLastname("Ted", isGovernmentWorker()), which would find all users that have the last name Ted AND who satisfy the isGovernmentWorker() specification.
I need this because I'd like the automated query creation provided by Spring Data and because I still need to be able to apply arbitrary specifications at runtime.
There is no such feature. Specifications can only be applied on JpaSpecificationExecutor operations.
Update
The data access operations are generated by a proxy. Thus if we want to group the operations (as in findByName + Criteria) in a single SELECT call, the proxy must understand and support this kind of usage; which it does not.
The intended usage, when employing Specification API would look like this for your case:
findAll(Specifications.where(hasLastName("Ted")).and(isGovernmentWorker())
Spring data allows you to implement custom repository and use Specifications or QueryDSL.
Please see this article.
So at the end you will have one YourCustomerRepository and appropriate YourRepositoryImpl implementation, where you will put your findByLastname("Ted", isGovernmentWorker()) method.
And then YourRepository should extend YourCustomerRepository interface.