Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Spring Boot v2.6.0-RC1] Entity callbacks invoked on both entity and projections #684

Closed
JoseLion opened this issue Nov 12, 2021 · 5 comments
Labels
status: invalid An issue that we don't feel is valid

Comments

@JoseLion
Copy link
Contributor

JoseLion commented Nov 12, 2021

Hi,

I'm not sure if this is the right place to report this because the issue is on the Release Candidate of Spring Boot, so I'm not sure which version of spring-data-r2dbc we might need to check. Anyways, I hope this helps 🙂

I'm using Spring Boot v2.6.0-RC1 to test issue #591, which was solved and shipped on this version. However, I noticed that custom entity callbacks are invoked for both the entity and any projection of that specific entity. I'm not sure if this is the expected behavior though, it could be convenient not having to repeat the same callbacks for many projections. But on the other hand, projections are usually intended to have fewer fields than the actual entity, so it can be easy to fall into NullPointerExceptions and errors of that sort.

Given this behavior is intended, what would be the best/safest way to tell if the entity received in the callback is some specific projection and not the entity?

As always, I'll be more than happy to help with a PR if needed. I'd only need some guidance in the best approach to take on this.

Cheers!

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 12, 2021
@mp911de
Copy link
Member

mp911de commented Nov 12, 2021

Do you see two invocations for the same row or is it just that you see a callback that happens on the projection when using projections?

Generally, we try to read data as efficiently as possible so reading rows directly into a DTO projection, without creating the entity first doesn't impose any overhead.

@mp911de mp911de added the status: waiting-for-feedback We need additional information before we can continue label Nov 12, 2021
@JoseLion
Copy link
Contributor Author

@mp911de thanks for the quick response. I don't see two invocations, but I do see the entity callback invoked when using projections of that entity.

To be a bit more precise about the issue, let's assume we have a User entity, a UserView projection of that entity, and a UserRepository. We also have a method in the repository like Mono<UserView> findViewById(UUID id) to get projected values. Finally, we define a custom callback like AfterConvertCallback<User> afterConvertUser(..).

With all that set, if we get a projection using findViewById, the afterConvertUser callback is invoked with the projected value passed in the callback argument.

I hope this helped to clarify the scenario 🙂

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 13, 2021
@mp911de
Copy link
Member

mp911de commented Nov 15, 2021

I do see the entity callback invoked when using projections of that entity

That is expected as we want to read data directly into the projection.

Right now, a lot of projections in Spring Data read data into a Map. Some still materialize the entity first and then use the backing object as a source for projections. That, however, is an implementation detail that we're going to change over time to consistently read projections into maps which gives the framework much more flexibility (e.g. when the entity uses non-null constraints that we cannot bypass easily)

Generally one could argue that a projection isn't an entity and hence we should skip entity callbacks in that case alltogether.

@JoseLion
Copy link
Contributor Author

Got it, that makes sense! I can also see its benefits, but I wasn't sure which of the 2 was the expected behavior. The only downside is that it's hard to tell if the callback receives the complete entity or just a projection. We can always assume that all fields can be null, but that's still a bit unsafe (especially when we have nullability annotations). Any advice to handle this?

As a side note, I usually inject other repositories in my custom callbacks, and I noticed that on v2.6.0-RC1, this caused a circular reference, which I think makes sense because callbacks now use r2dbcEntityTemplate, right? I was able to solve this by adding @Lazy to the injection of the repository in my callback @Bean, but I wonder if that's the correct solution or there's probably something different we can do 🤔

Expand for circular reference error screenshot 🙂

image

@mp911de
Copy link
Member

mp911de commented Dec 9, 2021

As a side note, I usually inject other repositories in my custom callbacks, and I noticed that on v2.6.0-RC1, this caused a circular reference, which I think makes sense because callbacks now use r2dbcEntityTemplate, right? I was able to solve this by adding @lazy to the injection of the repository in my callback @bean, but I wonder if that's the correct solution or there's probably something different we can do 🤔

Spring Boot 2.6 forbids by default circular bean arrangements. @Lazy or ObjectProvider are good ways out.

@mp911de mp911de closed this as completed Dec 9, 2021
@mp911de mp911de added status: invalid An issue that we don't feel is valid and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Dec 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

3 participants