package com.vincent.rsf.server.manager.mapper; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.vincent.rsf.server.manager.entity.Loc; import com.vincent.rsf.server.manager.entity.Task; import com.vincent.rsf.server.manager.entity.WaitPakin; import com.vincent.rsf.server.manager.entity.WkOrder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.util.Date; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MybatisPlusOptimisticLockIntegrationTest.TestConfig.class) class MybatisPlusOptimisticLockIntegrationTest { @Autowired private WaitPakinMapper waitPakinMapper; @Autowired private LocMapper locMapper; @Autowired private AsnOrderMapper asnOrderMapper; @Autowired private TaskMapper taskMapper; @Autowired private JdbcTemplate jdbcTemplate; @BeforeEach void setUpSchema() { jdbcTemplate.execute("drop table if exists man_wait_pakin"); jdbcTemplate.execute("drop table if exists man_loc"); jdbcTemplate.execute("drop table if exists man_asn_order"); jdbcTemplate.execute("drop table if exists man_task"); jdbcTemplate.execute(""" create table man_wait_pakin ( id bigint auto_increment primary key, code varchar(64), barcode varchar(64), anfme double, io_status smallint, flag_defect smallint, status integer, deleted integer, tenant_id integer, create_by bigint, create_time timestamp, update_by bigint, update_time timestamp, memo varchar(255), version integer not null default 0 ) """); jdbcTemplate.execute(""" create table man_loc ( id bigint auto_increment primary key, area_id bigint, code varchar(64), warehouse_id bigint, `type` varchar(64), flag_logic smallint, fuc_atrrs varchar(255), barcode varchar(64), unit varchar(64), `length` double, height double, width double, `row` integer, device_no integer, col integer, lev integer, `channel` integer, max_parts integer, max_pack integer, use_status varchar(8), flag_label_mange smallint, loc_attrs varchar(255), status integer, deleted integer, tenant_id integer, create_by bigint, create_time timestamp, update_by bigint, update_time timestamp, memo varchar(255), version integer not null default 0 ) """); jdbcTemplate.execute(""" create table man_asn_order ( id bigint auto_increment primary key, code varchar(64), po_code varchar(64), po_id bigint, `type` varchar(64), wk_type varchar(64), check_type integer, anfme double, work_qty double, qty double, logis_no varchar(64), wave_id bigint, arr_time timestamp, nty_status integer, report_once integer, rle_status smallint, exce_status smallint, status integer, deleted integer, tenant_id integer, create_by bigint, create_time timestamp, update_by bigint, update_time timestamp, version integer not null default 0, memo varchar(255), warehouse_id bigint, ware_area_id bigint, business_time timestamp, station_id varchar(64), shipper_id bigint, order_internal_code varchar(64), stock_direct varchar(64), customer_id varchar(64), customer_name varchar(255), supplier_id varchar(64), supplier_name varchar(255), stock_org_id varchar(64), stock_org_name varchar(255), purchase_org_id varchar(64), purchase_org_name varchar(255), purchase_user_id varchar(64), purchase_user_name varchar(255), prd_org_id varchar(64), prd_org_name varchar(255), sale_org_id varchar(64), sale_org_name varchar(255), sale_user_id varchar(64), sale_user_name varchar(255) ) """); jdbcTemplate.execute(""" create table man_task ( id bigint auto_increment primary key, task_code varchar(64), resource smallint, task_status integer, parent_id bigint, wareh_type varchar(64), task_type integer, org_loc varchar(64), targ_loc varchar(64), org_site varchar(64), targ_site varchar(64), barcode varchar(64), robot_code varchar(64), exce_status smallint, exp_desc varchar(255), sort integer, exp_code varchar(64), start_time timestamp, end_time timestamp, status integer, task_origin varchar(64), deleted integer, tenant_id integer, create_by bigint, create_time timestamp, update_by bigint, update_time timestamp, version integer not null default 0, memo varchar(255), device_site_id bigint, sou_step varchar(64), end_step varchar(64), targ_site_area varchar(255), targ_loc_area varchar(255), targ_site_area_now varchar(255) ) """); } @Test void updateByIdRejectsStaleSnapshot() { WaitPakin seed = waitPakin(); waitPakinMapper.insert(seed); WaitPakin firstSnapshot = waitPakinMapper.selectById(seed.getId()); WaitPakin secondSnapshot = waitPakinMapper.selectById(seed.getId()); firstSnapshot.setIoStatus((short) 1); firstSnapshot.setUpdateBy(2L); firstSnapshot.setUpdateTime(new Date()); assertEquals(1, waitPakinMapper.updateById(firstSnapshot)); assertEquals(1, firstSnapshot.getVersion()); secondSnapshot.setIoStatus((short) 2); secondSnapshot.setUpdateBy(3L); secondSnapshot.setUpdateTime(new Date()); assertEquals(0, waitPakinMapper.updateById(secondSnapshot)); WaitPakin latest = waitPakinMapper.selectById(seed.getId()); assertEquals((short) 1, latest.getIoStatus()); assertEquals(1, latest.getVersion()); } @Test void updateWithEntityAndWrapperRejectsStaleSnapshot() { WaitPakin seed = waitPakin(); waitPakinMapper.insert(seed); WaitPakin firstSnapshot = waitPakinMapper.selectById(seed.getId()); WaitPakin secondSnapshot = waitPakinMapper.selectById(seed.getId()); WaitPakin firstUpdate = new WaitPakin(); firstUpdate.setId(firstSnapshot.getId()); firstUpdate.setVersion(firstSnapshot.getVersion()); firstUpdate.setIoStatus((short) 1); firstUpdate.setUpdateBy(2L); firstUpdate.setUpdateTime(new Date()); assertEquals(1, waitPakinMapper.update(firstUpdate, new LambdaUpdateWrapper().eq(WaitPakin::getId, firstSnapshot.getId()))); assertEquals(1, firstUpdate.getVersion()); WaitPakin secondUpdate = new WaitPakin(); secondUpdate.setId(secondSnapshot.getId()); secondUpdate.setVersion(secondSnapshot.getVersion()); secondUpdate.setIoStatus((short) 2); secondUpdate.setUpdateBy(3L); secondUpdate.setUpdateTime(new Date()); assertEquals(0, waitPakinMapper.update(secondUpdate, new LambdaUpdateWrapper().eq(WaitPakin::getId, secondSnapshot.getId()))); WaitPakin latest = waitPakinMapper.selectById(seed.getId()); assertEquals((short) 1, latest.getIoStatus()); assertEquals(1, latest.getVersion()); } @Test void locUpdateWithEntityAndWrapperRejectsStaleSnapshot() { Loc seed = loc(); locMapper.insert(seed); Loc firstSnapshot = locMapper.selectById(seed.getId()); Loc secondSnapshot = locMapper.selectById(seed.getId()); Loc firstUpdate = new Loc(); firstUpdate.setId(firstSnapshot.getId()); firstUpdate.setVersion(firstSnapshot.getVersion()); firstUpdate.setUseStatus("S"); firstUpdate.setBarcode("BC-LOC-001"); firstUpdate.setUpdateBy(2L); firstUpdate.setUpdateTime(new Date()); assertEquals(1, locMapper.update(firstUpdate, new LambdaUpdateWrapper().eq(Loc::getId, firstSnapshot.getId()))); assertEquals(1, firstUpdate.getVersion()); Loc secondUpdate = new Loc(); secondUpdate.setId(secondSnapshot.getId()); secondUpdate.setVersion(secondSnapshot.getVersion()); secondUpdate.setUseStatus("F"); secondUpdate.setBarcode("BC-LOC-002"); secondUpdate.setUpdateBy(3L); secondUpdate.setUpdateTime(new Date()); assertEquals(0, locMapper.update(secondUpdate, new LambdaUpdateWrapper().eq(Loc::getId, secondSnapshot.getId()))); Loc latest = locMapper.selectById(seed.getId()); assertEquals("S", latest.getUseStatus()); assertEquals("BC-LOC-001", latest.getBarcode()); assertEquals(1, latest.getVersion()); } @Test void wkOrderUpdateByIdRejectsStaleSnapshot() { WkOrder seed = wkOrder(); asnOrderMapper.insert(seed); WkOrder firstSnapshot = asnOrderMapper.selectById(seed.getId()); WkOrder secondSnapshot = asnOrderMapper.selectById(seed.getId()); firstSnapshot.setExceStatus((short) 1); firstSnapshot.setUpdateBy(2L); firstSnapshot.setUpdateTime(new Date()); assertEquals(1, asnOrderMapper.updateById(firstSnapshot)); assertEquals(1, firstSnapshot.getVersion()); secondSnapshot.setExceStatus((short) 2); secondSnapshot.setUpdateBy(3L); secondSnapshot.setUpdateTime(new Date()); assertEquals(0, asnOrderMapper.updateById(secondSnapshot)); WkOrder latest = asnOrderMapper.selectById(seed.getId()); assertEquals((short) 1, latest.getExceStatus()); assertEquals(1, latest.getVersion()); } @Test void wkOrderWorkQtyUpdateRejectsStaleSnapshot() { WkOrder seed = wkOrder(); asnOrderMapper.insert(seed); WkOrder firstSnapshot = asnOrderMapper.selectById(seed.getId()); WkOrder secondSnapshot = asnOrderMapper.selectById(seed.getId()); firstSnapshot.setWorkQty(2.5); firstSnapshot.setUpdateBy(2L); firstSnapshot.setUpdateTime(new Date()); assertEquals(1, asnOrderMapper.updateById(firstSnapshot)); assertEquals(1, firstSnapshot.getVersion()); secondSnapshot.setWorkQty(1.0); secondSnapshot.setUpdateBy(3L); secondSnapshot.setUpdateTime(new Date()); assertEquals(0, asnOrderMapper.updateById(secondSnapshot)); WkOrder latest = asnOrderMapper.selectById(seed.getId()); assertEquals(2.5, latest.getWorkQty()); assertEquals(1, latest.getVersion()); } @Test void taskUpdateByIdRejectsStaleSnapshot() { Task seed = task(); taskMapper.insert(seed); Task firstSnapshot = taskMapper.selectById(seed.getId()); Task secondSnapshot = taskMapper.selectById(seed.getId()); firstSnapshot.setTaskStatus(1); firstSnapshot.setUpdateBy(2L); firstSnapshot.setUpdateTime(new Date()); assertEquals(1, taskMapper.updateById(firstSnapshot)); assertEquals(1, firstSnapshot.getVersion()); secondSnapshot.setTaskStatus(2); secondSnapshot.setUpdateBy(3L); secondSnapshot.setUpdateTime(new Date()); assertEquals(0, taskMapper.updateById(secondSnapshot)); Task latest = taskMapper.selectById(seed.getId()); assertEquals(1, latest.getTaskStatus()); assertEquals(1, latest.getVersion()); } private WaitPakin waitPakin() { return new WaitPakin() .setCode("PK-001") .setBarcode("BC-001") .setAnfme(1.0) .setIoStatus((short) 0) .setFlagDefect((short) 0) .setStatus(1) .setDeleted(0) .setTenantId(1) .setCreateBy(1L) .setCreateTime(new Date()) .setUpdateBy(1L) .setUpdateTime(new Date()) .setMemo("seed"); } private Loc loc() { return new Loc() .setCode("LOC-001") .setBarcode(null) .setUseStatus("O") .setStatus(1) .setDeleted(0) .setTenantId(1) .setCreateBy(1L) .setCreateTime(new Date()) .setUpdateBy(1L) .setUpdateTime(new Date()) .setMemo("seed"); } private WkOrder wkOrder() { return new WkOrder() .setCode("ASN-001") .setType("CHECK") .setWkType("IN") .setAnfme(1.0) .setWorkQty(0.0) .setQty(0.0) .setRleStatus((short) 0) .setExceStatus((short) 0) .setStatus(1) .setDeleted(0) .setTenantId(1) .setCreateBy(1L) .setCreateTime(new Date()) .setUpdateBy(1L) .setUpdateTime(new Date()) .setMemo("seed"); } private Task task() { return new Task() .setTaskCode("TASK-001") .setTaskStatus(0) .setTaskType(1) .setOrgLoc("ORG-001") .setTargLoc("TARG-001") .setBarcode("BC-TASK-001") .setExceStatus((short) 0) .setStatus(1) .setDeleted(0) .setTenantId(1) .setCreateBy(1L) .setCreateTime(new Date()) .setUpdateBy(1L) .setUpdateTime(new Date()) .setMemo("seed"); } @Configuration @MapperScan(basePackageClasses = {WaitPakinMapper.class, LocMapper.class, AsnOrderMapper.class, TaskMapper.class}) static class TestConfig { @Bean DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.h2.Driver"); dataSource.setUrl("jdbc:h2:mem:optimistic-lock;MODE=MySQL;DB_CLOSE_DELAY=-1"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } @Bean MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource, MybatisPlusInterceptor mybatisPlusInterceptor) { MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setPlugins(mybatisPlusInterceptor); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setMapUnderscoreToCamelCase(true); factoryBean.setConfiguration(configuration); return factoryBean; } } }