次のフィールドを持つクラスがあります。
@Version
private Long version = 1L;
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
私はSpring Web + Hibernateを使用しています。@Version を使用して同じレコードへの同時編集を防止する比較的完全な例が見られることを願っています。
SOをグーグルで検索しました。何も見つかりませんでした。バージョン フィールドに非表示の HTML フォーム フィールドを使用しましたが、機能しません。
どんな情報/ポインタも本当に感謝しています。
よろしくお願いします!
更新 1
これが私がしたことです。2 つのブラウザ ウィンドウに同じレコードをロードしました。両方のウィンドウの HTML フォームに同じ値の非表示のバージョン フィールドがあることを確認できました。1 つのウィンドウでフォームを送信し、データベースを確認したところ、[バージョン] フィールドが増加していることに気付きました。次に、別のウィンドウを送信しました。何も起こらず、レコードは正常に更新/保存されました。何らかの例外がスローされることを期待していました。
バージョン フィールドの定義に加えて、Spring/Hibernate の組み合わせを機能させるように構成する必要がありますか?
更新 2
Hibernate がレコードを更新する方法を追跡するために、次のことを行いました。
hibernate.show_sql=true
log4j.logger.org.hibernate.type=trace
これで、Hibernate がレコードを更新する方法を確認できました。Hibernate が次の SQL でレコードを更新する瞬間に気付きました
update ... where id=? and version=?
Hibernate は常にデータベースの最新バージョン番号を使用してバインドしますが、レコードを保存する前に、Web レイヤーでレコードに古いバージョン番号と新しいデータを含む他のフィールドがまだ残っていることを確認できます。
どうして?
アプリで楽観的ロックが何らかの形で無効になっているように感じますが、このための Hibernate 構成は行いません。Hibernate 4.2.1.Final と Spring 3.2.2.RELEASE を使用しています。
Hibernate 楽観的ロックを有効/無効にする明示的な方法はありますか?
アップデート 3
以下は、データ オブジェクトとデータベース層のコードです。「friendGroupDao」は、この DAO オブジェクトが呼び出されて Web レイヤーにオブジェクトをロードし、オブジェクトをデータベースに保存するトランザクション サービス レイヤーに自動配線されます。関連するサービス レイヤー メソッドは、コードを追加せずに単に friendGroupDao.load() と friendGroupDao.save() を呼び出します。
-------------- ドメイン オブジェクト --------------
@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);
}
}