Mesut Dogan
Mesut Dogan

Reputation: 592

How To Use Optimistic Locking in Spring MVC

I have been developing blog application using Spring MVC + Spring Data JPA + Hibernate. Now I have a problem with optimistic locking mechanism. I will share a piece of code below. I have added @Version annotation to entity. Than I am using two different browser to update same record at same time. Every time I perform the save action it increases the versioon +1 and updates data. But doesnt throw any exception As you know , expected exception is like OptirmisticException. I have searched but no information about it yet. If you could clarify me, I will be appricate. Here is a piece of code from controller. Thanks All.

@RequestMapping(value="/edit/{id}", method = RequestMethod.POST) 
public String postEdit(@PathVariable Long id , @ModelAttribute("category")Category formCategory){

    try {
        Category category = categoryService.findOneCategory(id);
        if(category!=null) {
            category.setCatName(formCategory.getCatName());
            categoryService.save(category);
        }
    } catch (Exception e) {
          LOGGER.debug("Error message  : "+e);
    } 

    return PAGE_DEFAULT;
}

Upvotes: 2

Views: 5443

Answers (3)

Basil Vandegriend
Basil Vandegriend

Reputation: 485

Applying optimistic locking within Spring MVC is a multi-step process.

Step 1: When displaying the form for the user to edit, store the version # of the entity to be edited. This can either be stored as a hidden field in the form or within the session (to minimize the size of your session, you can just store the version number and not the whole entity). You also need to store the primary key of the entity.

Step 2: When saving the entity (your postEdit step), you first need to load it from the database using the stored primary key. The easiest way to do this I believe is to use a @ModelAttribute method.

Step 3: In your postEdit method, obtain the stored version and compare it to the version of the loaded entity. If they differ, report an error to the user. I prefer to indicate a validation error rather than throw an exception, since it results in a cleaner user experience.

Some code examples that are based on storing the version using a hidden field:

@ModelAttribute("entity")
public Entity getEntity(@RequestParam(value="id", required=false) Long id) {
    if (id == null) {
        return new Entity();
    } else {
        Optional<Entity> entity = entityRepo.findById(id);
        // TODO: Throw exception if not found (probably concurrently deleted).
        return entity.orElse(null);
    }
}

@RequestMapping(value="/entity/save", method=RequestMethod.POST)
public String postEdit(@Valid @ModelAttribute("entity") Entity entity, BindingResult bindingResult, @RequestParam(value = "version", required=false) Long version) {

    if (entity.getVersion() != version) {
        bindingResult.reject("concurrency", "The data was modified concurrently.");
    }

    if (bindingResult.hasErrors()) {
        // TODO: Redisplay edit form
    } else {
        // TODO: Proceed with saving.
    }
}

Note that if you are using a hidden field named "version" and the property of your entity is also named "version" then you should NOT have a setVersion() method on your entity, as otherwise Spring will automatically populate (bind) the version property based on the hidden field rendering your check useless.

Upvotes: 2

Sanjay
Sanjay

Reputation: 8945

As I know, a common way is to send the version column to the client as a hidden field or something, and get that posted back. Then, in the post method, compare that to the freshly retrieved version from the database. Throw an exception if they don't match.

Refer to Spring Lemon for a detailed example.

Response to comment:

See this post for details. In summary, JPA does check for the version column, but it won't allow you to set it. Hence, we are left with no option but to check it manually.

Upvotes: 0

Neil McGuigan
Neil McGuigan

Reputation: 48236

To do it correctly, you need to store the Entity in session between requests.

You could store the version in a hidden form field, but session is more secure, in that a user cannot change the value.

Don't reload it from the database in your POST handler.

Add @SessionAttributes("category") above your controller.

See Spring MVC: Validation, Post-Redirect-Get, Partial Updates, Optimistic Concurrency, Field Security

Upvotes: 1

Related Questions