1818import static org .assertj .core .api .Assertions .*;
1919
2020import jakarta .persistence .EntityManager ;
21+ import jakarta .persistence .OptimisticLockException ;
2122import jakarta .persistence .PersistenceContext ;
2223
2324import java .util .Arrays ;
2425import java .util .Iterator ;
2526import java .util .List ;
27+ import java .util .Map ;
28+
29+ import javax .sql .DataSource ;
2630
2731import org .jetbrains .annotations .NotNull ;
2832import org .junit .jupiter .api .BeforeEach ;
2933import org .junit .jupiter .api .Test ;
3034import org .junit .jupiter .api .extension .ExtendWith ;
35+ import org .springframework .beans .factory .annotation .Autowired ;
3136import org .springframework .data .jpa .domain .sample .PersistableWithIdClass ;
3237import org .springframework .data .jpa .domain .sample .PersistableWithIdClassPK ;
3338import org .springframework .data .jpa .domain .sample .SampleEntity ;
3439import org .springframework .data .jpa .domain .sample .SampleEntityPK ;
40+ import org .springframework .data .jpa .domain .sample .VersionedUser ;
3541import org .springframework .data .jpa .repository .JpaRepository ;
3642import org .springframework .data .repository .CrudRepository ;
43+ import org .springframework .jdbc .core .namedparam .NamedParameterJdbcOperations ;
44+ import org .springframework .jdbc .core .namedparam .NamedParameterJdbcTemplate ;
3745import org .springframework .test .context .ContextConfiguration ;
3846import org .springframework .test .context .junit .jupiter .SpringExtension ;
3947import org .springframework .transaction .annotation .Transactional ;
4654 * @author Jens Schauder
4755 * @author Greg Turnquist
4856 * @author Krzysztof Krason
57+ * @author Yanming Zhou
4958 */
5059@ ExtendWith (SpringExtension .class )
5160@ ContextConfiguration ({ "classpath:infrastructure.xml" })
5261@ Transactional
5362class JpaRepositoryTests {
5463
64+ @ Autowired DataSource dataSource ;
65+
5566 @ PersistenceContext EntityManager em ;
5667
5768 private JpaRepository <SampleEntity , SampleEntityPK > repository ;
5869 private CrudRepository <PersistableWithIdClass , PersistableWithIdClassPK > idClassRepository ;
70+ private JpaRepository <VersionedUser , Long > versionedUserRepository ;
71+ private NamedParameterJdbcOperations jdbcOperations ;
5972
6073 @ BeforeEach
6174 void setUp () {
6275 repository = new JpaRepositoryFactory (em ).getRepository (SampleEntityRepository .class );
6376 idClassRepository = new JpaRepositoryFactory (em ).getRepository (SampleWithIdClassRepository .class );
77+ versionedUserRepository = new JpaRepositoryFactory (em ).getRepository (VersionedUserRepository .class );
78+ jdbcOperations = new NamedParameterJdbcTemplate (dataSource );
6479 }
6580
6681 @ Test
@@ -162,6 +177,48 @@ public Iterator<SampleEntityPK> iterator() {
162177 assertThat (repository .findAll ()).containsExactly (two );
163178 }
164179
180+ @ Test
181+ void deleteDirtyDetachedVersionedEntityShouldRaiseOptimisticLockException () {
182+
183+ VersionedUser entity = new VersionedUser ();
184+ entity .setName ("name" );
185+ versionedUserRepository .save (entity );
186+ versionedUserRepository .flush ();
187+ em .detach (entity );
188+
189+ versionedUserRepository .findById (entity .getId ()).ifPresent (u -> {
190+ u .setName ("new name" );
191+ versionedUserRepository .flush ();
192+ });
193+
194+ assertThatExceptionOfType (OptimisticLockException .class ).isThrownBy (() -> {
195+ versionedUserRepository .delete (entity );
196+ versionedUserRepository .flush ();
197+ });
198+
199+ jdbcOperations .update ("delete from VersionedUser" , Map .of ());
200+ }
201+
202+ @ Test
203+ void deleteDirtyManagedVersionedEntityShouldRaiseOptimisticLockException () {
204+
205+ VersionedUser entity = new VersionedUser ();
206+ entity .setName ("name" );
207+ versionedUserRepository .save (entity );
208+ versionedUserRepository .flush ();
209+
210+
211+ assertThat (jdbcOperations .update ("update VersionedUser set version=version+1 where id=:id" ,
212+ Map .of ("id" , entity .getId ()))).isEqualTo (1 );
213+
214+ assertThatExceptionOfType (OptimisticLockException .class ).isThrownBy (() -> {
215+ versionedUserRepository .delete (entity );
216+ versionedUserRepository .flush ();
217+ });
218+
219+ jdbcOperations .update ("delete from VersionedUser" , Map .of ());
220+ }
221+
165222 private interface SampleEntityRepository extends JpaRepository <SampleEntity , SampleEntityPK > {
166223
167224 }
@@ -170,4 +227,8 @@ private interface SampleWithIdClassRepository
170227 extends CrudRepository <PersistableWithIdClass , PersistableWithIdClassPK > {
171228
172229 }
230+
231+ private interface VersionedUserRepository extends JpaRepository <VersionedUser , Long > {
232+
233+ }
173234}
0 commit comments