0

I have the following:

public class Member {

    @Id
    @Column
    private String id;

    @Column
    private String location;

    // setters/getters
}

I have to implement the following business rules:

Set Location:

  1. Get member from DB based on id.
  2. If location is null, assign a value to location and save, otherwise do nothing.

public void setLocation(String id, String location){
    Member m = memberDao.find(id); // assume m is not null
    if(null == m.getLocation()){
         m.setLocation(location);
         memeberDao.saveOrUpdate(m);
    }
    else
        throw new MemberAlreadyHasALocationException();
}

Remove Location:

  1. Get member from DB based on id.
  2. If location has a non-null value and current value equals parameter value, set location to null and update, otherwise do nothing

public void removeLocation(String id, String location){
    Member m = memberDao.find(id);
    if(m.getLocation() != null && m.getLocation.equalsIgnoreCase(location)){
         m.setLocation(null);
         memeberDao.saveOrUpdate(m);
    }
}

I am tasked with handling concurrency issues but I am not sure how to do this in hibernate. More specifically, how can I handle concurrent calls to setLocation for the same ID given that the entity with that ID does not currently have a location.

Basically, the first update should win and the second should fail.

Does it suffice to add a version to the Member class


@Version
@Column(name="version")
private Long version;

However, say that there is a gender field, I would like to be able to update the gender field without caring if the location is being updated by another transaction.

Thanks.

jsl
  • 1
  • 1

1 Answers1

0

Basically, the first update should win and the second should fail.

Does it suffice to add a version to the Member class

Yes, it does. Say you and I read an entity from the database with version = x. I make some changes to property1 of the entity and commit to the database. Now the entity in the database has version = x+1. Now, you change property2 of your version of the entity (property1 still holds the older value). When you update the changed entity to the database, the changes made by me to property1 would be lost as you would overwrite it with the old value. But this will not happen as Hibernate would check from any changes that might have occurred between the time of reading the entity and updating it. When you go to update the entity Hibernate sees that the version of the entity in the database is different from the version of entity that you have provided. This means that someone else has made some changes to the object i.e. the object that you have supplied is stale and hence a StaleStateException is thrown.

However, say that there is a gender field, I would like to be able to update the gender field without caring if the location is being updated by another transaction.

This is not possible using hibernate (it should be clear from the above example). If this was supported, then changes made to the location would be lost when and update made to the gender is committed containing the older value of location. Some degree of tolerance can be achieved using some logic and explicit version checking and updating the changed fields before updating the entity, but the problem still exists, you can never know at what moment the changes will be made to the object. In my opinion, such an implementation will be an overkill with not enough benefit.

Satadru Biswas
  • 1,555
  • 2
  • 13
  • 23
  • Thanks for the info. I guess then now, moving away from hibernate, I only want to give location a value only if it is currently null, allowing for concurrency. However, I don't want to block on each call to setLocation, rather, I want to block on calls to setLocation for a particular member id, so that two calls to setLocation for two different members don't block, but two calls to setLocation for the same member do block. I haven't found a good way to do this that I like: http://stackoverflow.com/questions/659915/synchronizing-on-an-integer-value – jsl Apr 28 '11 at 14:18
  • Would it suffice to use a stored procedure that checks for the member, if he does not exist, return -1, otherwise if the member does exist and he has a location, return 0 (don't update), otherwise update the location and return 1. Would the database properly handle concurrent calls to the stored procedure? – jsl Apr 28 '11 at 14:22
  • @jsl You should check out pessimistic locking in Hibernate. This would allow only one thread to make changes to the entity at a time, blocking others who want to make changes to the same entity. However this will also block threads who want to update some property other than location – Satadru Biswas Apr 28 '11 at 16:21