I have a class that has the following field:
@Version
private Long version = 1L;
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
I use Spring web + Hibernate. I hope to see a relatively complete example of using @Version to prevent simultaneous edits to the same record.
Googled and searched SO. Failed to find any. I used a hidden HTML form field for the version field and it is not working.
Any info/pointer is really appreciated.
Thanks and regards!
Update 1
Here is what I did. I loaded the same record in two browser windows. I was able to see that there was a hidden version field with the same value in the HTML form of both windows. I submitted the form in one window and checked the database and notice that the Version field was increased. Then I submitted another window. Nothing happened and the record was updated/saved successfully. I was expecting some sort of exception was thrown.
Do I need to configure the spring/hibernate combination to make it work, in addition to the Version field definition?
Update 2
I did the following to trace how Hibernate update the record:
hibernate.show_sql=true
log4j.logger.org.hibernate.type=trace
Now I am able to see how Hibernate update the record. I notice that at the moment of Hibernate updating the record with the following SQL
update ... where id=? and version=?
Hibernate always uses the latest version number from the database to bind, even though I can confirm that in the web layer a record still has the old version number plus other fields with new data before saving the record.
How come?
I feel optimistic locking is disabled somehow in my app, but I do not do Hibernate config for this. I am using Hibernate 4.2.1.Final and Spring 3.2.2.RELEASE.
Is there a way to explicit to enable/disable Hibernate optimistic locking?
Update 3
The following is the data objects and database layer code. "friendGroupDao" is auto-wired into the transactional service layer where this DAO object is called to load an object to the web layer and saves an object to the database. The related service layer methods simply call the friendGroupDao.load() and friendGroupDao.save() without any additional code.
-------------- domain objects -------------
@MappedSuperclass
public abstract class BaseObject implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Version
private Long version = 1L;
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public BaseObject() {
}
}
@Entity
public class FriendGroup extends BaseObject {
@Column(columnDefinition = "NVARCHAR(120)")
private String name;
public String getName() {
return name;
}
public void setName(String accountName) {
this.name = accountName;
}
}
--------------- database -------------
public interface BaseObjectDao<T extends BaseObject> {
void delete(T o);
T load(Long id);
void save(T o);
}
public class BaseObjectDaoImpl<T extends BaseObject> implements
BaseObjectDao<T> {
private Class<T> domainClass;
@Autowired
protected SessionFactory sessionFactory;
public BaseObjectDaoImpl() {
this.domainClass = (Class<T>) BaseObject.class;
}
public BaseObjectDaoImpl(Class<T> domainClass) {
this.domainClass = domainClass;
}
public SessionFactory getSessFactory() {
return sessionFactory;
}
public void setSessFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
public void delete(T object) {
getSession().delete(object);
}
public T load(Long id) {
return (T) getSession().get(domainClass, id);
}
public void save(T object) {
getSession().saveOrUpdate(object);
}
public Session getSession() {
return sessionFactory.getCurrentSession();
}
}
public interface FriendGroupDao extends BaseObjectDao<FriendGroup> {
}
@Repository("friendGroupDao")
public class FriendGroupDaoImpl extends BaseObjectDaoImpl<FriendGroup> implements
FriendGroupDao {
public FriendGroupDaoImpl() {
super(FriendGroup.class);
}
}