Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id)) Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id)) Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id)) Hibernate: create sequence book_seq start with 1 increment by 50 Hibernate: create sequence widget_seq start with 1 increment by 25 Hibernate: select next value for book_seq Hibernate: /* insert for com.ankurm.hibernatedemo.model.Book */insert into book (author,title,version,id) values (?,?,?,?) binding parameter (1:VARCHAR) <- [Robert C. Martin] binding parameter (2:VARCHAR) <- [Clean Code] binding parameter (3:BIGINT) <- [0] binding parameter (4:BIGINT) <- [1] SEED: inserted Book{id=1, title=Clean Code, author=Robert C. Martin, version=0} --- Step 1: load the row, then close the session (entity is now detached) --- Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=? binding parameter (1:BIGINT) <- [1] detached instance in hand: Book{id=1, title=Clean Code, author=Robert C. Martin, version=0} --- Step 2: a second, independent session edits the same row and commits --- Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=? binding parameter (1:BIGINT) <- [1] Hibernate: /* update for com.ankurm.hibernatedemo.model.Book */update book set author=?,title=?,version=? where id=? and version=? binding parameter (1:VARCHAR) <- [Robert C. Martin] binding parameter (2:VARCHAR) <- [Clean Code (2nd Edition)] binding parameter (3:BIGINT) <- [1] binding parameter (4:BIGINT) <- [1] binding parameter (5:BIGINT) <- [0] second session committed: Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- version column has now advanced in the database --- Step 3: mutate the ORIGINAL detached instance (still holding the OLD version) and merge() it --- detached instance before merge (note the version and title are both stale): Book{id=1, title=Clean Code, author=Robert C. Martin (Uncle Bob), version=0} Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=? binding parameter (1:BIGINT) <- [1] merge() threw jakarta.persistence.OptimisticLockException: Row was already updated or deleted by another transaction for entity [com.ankurm.hibernatedemo.model.Book with id '1'] the title change from Step 2 survives untouched -- merge() refused to apply a write built on a stale version --- Step 4: refresh() on a MANAGED entity with an unflushed local edit --- Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=? binding parameter (1:BIGINT) <- [1] before refresh(): Book{id=1, title=Clean Code (2nd Edition), author=SOMEONE ELSE ENTIRELY (never flushed), version=1} Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=? binding parameter (1:BIGINT) <- [1] after refresh(): Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- the local edit is gone, no exception was thrown