micronaut-projects / micronaut-data

Ahead of Time Data Repositories
Apache License 2.0
466 stars 197 forks source link

In JDBC one-to-many relations, with inmutable objects, get same object representation when you save or find #1124

Closed alejandropg closed 3 years ago

alejandropg commented 3 years ago

Feature description

Hi @dstepanov (I mentioned you for our previos conversations about this subject, sorry If you are not the proper person πŸ™)

I'm testing Micronaut Data JDBC 3.0.0 to store a one-to-many relation of inmutable objects, and I saw that when you save a new object, the save method returns one representation of the saved data, but when you use the find method, you get a different representation.

I will try to explain each case:

With the save method you get:

Screenshot 2021-08-22 at 11 19 10

With the find method you get:

Screenshot 2021-08-22 at 11 20 10

I think that the find method is a better representation, because is simpler, and doesn't reference the original instances.

You can find an example in: https://github.com/alejandropg/micronaut-data-jdbc-demo-one-to-many And the test: https://github.com/alejandropg/micronaut-data-jdbc-demo-one-to-many/blob/main/src/test/kotlin/com/example/ParentRepositoryTest.kt , lines 37 and 40

What do you think?

Thanks a lot!

dstepanov commented 3 years ago

Can you summarize what do you expect to see?

It's a bit problematic with immutable objects, every modification means a new instance, it goes somehow like this:

That's why you see the parent instance which is an old one, replacing it and trying to have the full object tree correct will take a lot of modification not sure if it's worse.

There might be a way to modify the collection which is not going to help for one-to-one mappings. For mutable entities, the behaviors should remain the same: set children collection should have all the children...

graemerocher commented 3 years ago

yeah I don't think we should support cases like this, immutable types especially records are not designed for set logic

alejandropg commented 3 years ago

Yes, It's true that the immutable case it's not easy. Really It's impossible to do in a bidirectional relation without creating different instances. But in this situation I think that is not the problem.

I mean, in this case, the issue is that the objects graph obtained after save is different than after find. This is not a bug, all works fine, but is strange that the two operations return a different graph of objects.

The save method returns:

This graph of objects is more complex than the returned by find:

As you can see, in the save graph, you have one more level.

I'm just curious about why the two methods (save and find) don't return the same graph of objects. And an improvement could be that the two methods return the same structure (the find objects graph).

Thanks!

graemerocher commented 3 years ago

Micronaut Data JDBC is not an object synchronization framework like JPA for one. It is a simple stateless data mapper.

if you are looking for something that synchronizes the state of the object graph with the database you should stick with JPA

alejandropg commented 3 years ago

Hi @graemerocher,

I'm not talking about or pretending that Micronaut Data JDBC works like an ORM or something like that. In fact, we like Micronaut Data JDBC because is not an ORM!

I'm just talking about the internals of Micronaut Data JDBC. Just to know why in one case (when you use the save method) you get an object representation, but when you use another method (the find method) you get another representation/structure of the same object/entity.

I think that could be an improvement that both save and find return the same object representation/structure because in both situations we are talking about exactly the same entity.

Do you understand what I mean? Sorry for my english πŸ˜…

graemerocher commented 3 years ago

well they work completely differently for one. One performs an insert of an existing object graph using SQL INSERT statements, the other materializes a new graph using a SELECT.

alejandropg commented 3 years ago

Yes, I suppose that, but really when you do the INSERT you already are creating and returning a new instance of the saved object. This new instance is needed because in the INSERT new data could be generated by the database, like the id of the entity.

So, if after INSERT already you are creating a new instance, why not create in this instance the same object structure that when you are using SELECT.

This is my proposal (but maybe there is not technically possible or has no sense).

dstepanov commented 3 years ago

@alejandropg The save only sets the ids where find generates the object graph from the tabular-like structure.

We can support some kind of Provider interface where the parent can be set back without modifying its children etc.

alejandropg commented 3 years ago

Well, you set the "id" creating new instances of the parent and children (in an ONE_TO_MANY relation), so you are recreating all the structure. When you recreate this structure why not doing it in the same way as the SELECT?

I mean, I don't want to set one specific "parent", the "provider" is not the issue I'm talking about. I'm talking just about consistency in the structures returned by the different methods.

Anyway, this is just an idea, so I think we could stop here. If you think that you have enough info and get the idea you could close the issue πŸ‘

Thanks for listening!

dstepanov commented 3 years ago

By setting a value of the immutable structure we need to create it again and set all the values but it doesn't mean we are recreating it from the DB. In that case, only ids are sent from DB as a response, if you have UUID ids nothing has to be modified.

I will close it as there is nothing at this point to change.

alejandropg commented 3 years ago

Thanks for the clarification @dstepanov πŸ‘Œ

I suggest a possible little modification. When you create children instances to set the IDs, you need to create a fake parent. When you create this fake parent (fake because is only needed to satisfy the bidirectional relation) you could set their children as an empty list of children (that is more like the find method).

Regards! πŸ‘πŸ‘πŸ‘ (EOT 😌)