Skip to content

DATAJDBC-219 - add support for optimistic locking #124

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

Conversation

thombergs
Copy link

This PR adds support for optimistic locking by evaluating the @Version annotation.

Some indentations are wrong as I didn't find a code formatter configuration but I can fix indentations if you point me to your code formatter.

Copy link
Contributor

@schauder schauder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Tom,

Thanks for tackling this.

I think we can replace the VersionsAccessor with to simple methods as in Mongo Db: https://github.com/spring-projects/spring-data-mongodb/blob/8f4db5540d70ddf4926824d050c50f73a35c6aab/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java#L672

These will also take care of handling immutable Version attributes properly.

A delete where an entity is given as a parameter should also check for optimistic locking.

I rebased and squashed your changes into a branch: https://github.com/spring-projects/spring-data-jdbc/tree/pr/datajdbc-219
Please use that for further work on this pr.
We try to avoid merge commits, since they make it hard to understand the changes made.

}

String getUpdateWithVersion(Number version) {
return String.format("%s AND %s = %s", updateSql.get(), entity.getVersionProperty().getColumnName(), version);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The versions should be a bind parameter as all other parameters.
For the parameter name pick something that is highly unlikely to collide with any other parameter names, like ___oldOptimisticLockingVersion

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@thombergs thombergs force-pushed the datajdbc-219-optimistic-locking branch 2 times, most recently from af1a414 to f60919b Compare March 15, 2019 16:56
The @Version annotation is now evaluated properly and an
OptimisticLockingFailureException is thrown on updates when the version
has been incremented in the meantime. The @Version field can be of type
Long, Integer or Short (or their primitive counterparts).
@thombergs thombergs force-pushed the datajdbc-219-optimistic-locking branch from f60919b to 67eeca8 Compare March 15, 2019 17:15
@thombergs
Copy link
Author

Hi @schauder,

I was wondering if there was a simpler way for accessing and modifying the version field. I simplified it and removed the VersionAccessor class. However, there is still an ugly if/else for supporting the different field types. I force pushed a cleanly squashed commit.

To support optimistic locking for the delete operation, I think we would have to modify DeleteRoot to include the entity instead of just the ID of the entity. Since this would mean a lot of changes, I'd like to hear your feedback on that first.

@schauder
Copy link
Contributor

I force pushed a cleanly squashed commit.

Great, I'll take a look.

To support optimistic locking for the delete operation, I think we would have to modify DeleteRoot to include the entity instead of just the ID of the entity. Since this would mean a lot of changes, I'd like to hear your feedback on that first.

I don't think we need the full entity, but just the version attribute.
In any case, changes to the public API must be made in a backward compatible way, i.e. don't simply change the signature, but add a new method with the additional parameter.

Copy link
Contributor

@schauder schauder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

private <T> Number getNextVersion(T instance, RelationalPersistentEntity<T> entity, ConversionService conversionService) {
Number version = getVersion(instance, entity, conversionService);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this fail if we just use always Long? I'd have thought that it would simply work.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if I use a ConvertingPropertyAccessor, it automatically converts from Long to the required type. The next push will contain a simplified version.

And no, it currently does not work with an immutable version field. I'll add a test. I see the merit in having an immutable version field, but I fail to see how the code in AggregateChange handles modifying an immutable ID field. It uses a PropertyAccessor, too, doesn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trick is that the PropertyAccessor creates a new bean with the new value for id/version and everything after that uses the new bean returned by propertyAccessor.getBean(). See the end of the method I linked to.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I get that. But in my case it fails even earlier with an UnsupportedOperationException right here: https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java#L85

BeanWrapper seems to expect a wither method. Is the existence of a wither method a valid constraint for our use case?

@thombergs
Copy link
Author

@schauder as for supporting optimistic locking for delete: Wouldn't it be cleaner to pass the whole entity through from JdbcAggregateTemplate, RelationalEntityDeleteWriter, and DeleteRoot instead of passing through only the current value of the version property? It would then be symmetrical to the update operation, at least ;).

And where does the public API start? I wasn't aware that any of this code is public API.

@schauder
Copy link
Contributor

@thombergs symmetry is a good argument (especially when talking to a physicist).
Let's go with the full entity.

Anything that is public (i.e. public methods in public classes) are considered public API by Spring.
Having to keep these compatible really encourages usage of package scope ;-)

@schauder
Copy link
Contributor

Yes, if an attribute is immutable we require a wither.

@thombergs
Copy link
Author

thombergs commented Mar 25, 2019

@schauder The "don't touch public API" policy is hard on refactorings like this one where we have to pass through the whole stack :).

Is it OK to modify the semantics of RelationalEntityDeleteWriter#write(Object, AggregateChange) so that the Object is interpreted as the root entity itself instead of as an ID? Otherwise, I guess we'd have to create another writer next to the existing one that does basically the same just with an entity as input instead of an ID. That seems awkward to me. Or there is a better way I don't see :)

@schauder
Copy link
Contributor

Don't we need both implementations anyway since deleting by id only should still be supported?

Of course, both implementations should reuse most of the code

@schauder
Copy link
Contributor

schauder commented Apr 8, 2019

@thombergs Sorry, missed the question about the formatters.

See https://github.com/spring-projects/spring-data-build/tree/master/etc/ide
and for more information about style of commit messages and more:
https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc

@fabiocvalente
Copy link

Hi, I'm changing my application from Spring Data JPA to Spring Data JDBC and we use @Version for optimistic locking.
I noticed that the version column in the databse was not being set.
After some googling I found this issue that has an open PR since March, but it has conflicts.
I would like to continue using this feature with JDBC.
Do you have an idea when this wil be available in milestone and release versions?

@schauder
Copy link
Contributor

This is certainly one of the more pressing issues but I can't really give any dates.

If someone would rebase the PR on the current master and/or address the open issues layed out above that would certainly help.

@tkvangorder
Copy link

@schauder I have taken the work Tom started, rebased, and I have the update/insert working. I am going to look at the delete next. I am not yet ready for a PR, but the branch is here : https://github.com/tkvangorder/spring-data-jdbc/tree/DATAJDBC-219

@schauder
Copy link
Contributor

There is a new PR, building on this one and replacing it: #166

@schauder schauder closed this Sep 12, 2019
mp911de added a commit that referenced this pull request Feb 21, 2022
mp911de added a commit that referenced this pull request Feb 21, 2022
Reduce Lombok usage to within tests.
mp911de added a commit that referenced this pull request Feb 21, 2022
Tighten nullability constraints. Consistent Javadoc.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants