Testing Optimistic Locking

Two Transactions in One Test

@DataJpaTest wraps the whole test in one transaction, so you cannot simulate two users. Turn that off and drive transactions yourself with TransactionTemplate:

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(PostgresTestConfig.class)
@Transactional(propagation = Propagation.NOT_SUPPORTED)   // no test-wide transaction
class ProductLockingTest {

    @Autowired ProductRepository products;
    @Autowired PlatformTransactionManager txManager;

    @Test
    void secondStaleUpdateFails() {
        TransactionTemplate tx = new TransactionTemplate(txManager);
        Long id = tx.execute(s -> products.save(new Product("Mixer Grinder", new BigDecimal("3499"), 10)).getId());

        Product asha = tx.execute(s -> products.findById(id).orElseThrow());   // version 0, detached
        Product ravi = tx.execute(s -> products.findById(id).orElseThrow());   // version 0, detached

        asha.setStock(9);
        tx.executeWithoutResult(s -> products.save(asha));                      // version -> 1

        ravi.setStock(8);
        assertThatThrownBy(() -> tx.executeWithoutResult(s -> products.save(ravi)))
                .isInstanceOf(ObjectOptimisticLockingFailureException.class);

        assertThat(tx.execute(s -> products.findById(id).orElseThrow().getStock())).isEqualTo(9);
        products.deleteAll();                                                   // no automatic rollback now
    }
}

Note: Without the automatic rollback, clean up your data (delete it, or truncate tables in an @AfterEach) so tests stay independent.

What to Test in the Persistence Layer

TestWhy
Custom JPQL / native queriessyntax and results on the real database
Migrations + ddl-auto=validatescripts and entities agree
Query counts on hot pathsN+1 does not come back
Locking / version conflictsconcurrency behaviour is as designed
Soft delete and tenant filtersno hidden or foreign rows leak
Plain derived methods (findById)usually not needed: Spring Data already tests these