refactor(manager-api): consolidate mybatis-plus 3.5.17 integration

This commit is contained in:
Tyke Chen
2026-07-20 10:31:08 +08:00
parent 7e8e5d34b8
commit 10a67c772f
22 changed files with 247 additions and 216 deletions
@@ -1,47 +0,0 @@
package xiaozhi.common.mybatisplus;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.Test;
class MybatisPlusBatchHelperTest {
@Test
void executesBatchThroughSelectedVersionAdapter() {
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
SqlSession sqlSession = mock(SqlSession.class);
Log log = mock(Log.class);
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
BatchResult batchResult = new BatchResult(mock(MappedStatement.class), "INSERT");
batchResult.setUpdateCounts(new int[] { 1, 1 });
when(sqlSession.flushStatements()).thenReturn(List.of(batchResult));
List<String> processed = new ArrayList<>();
boolean result = MybatisPlusBatchHelper.executeBatch(sqlSessionFactory, log, List.of("first", "second"), 2,
(session, entity) -> {
assertSame(sqlSession, session);
processed.add(entity);
});
assertTrue(result);
assertEquals(List.of("first", "second"), processed);
verify(sqlSession).flushStatements();
verify(sqlSession).commit(true);
verify(sqlSession).close();
}
}
@@ -0,0 +1,200 @@
package xiaozhi.common.service.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
class BaseServiceImplTest {
private static final String INSERT_STATEMENT = TestMapper.class.getName() + ".insert";
private static final String UPDATE_STATEMENT = TestMapper.class.getName() + ".updateById";
private final TestService service = new TestService();
@AfterEach
void clearTransactionSynchronization() {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.clearSynchronization();
}
}
@Test
void insertBatchFlushesAtTheRequestedBatchSizeAndCommitsWithoutATransaction() {
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
SqlSession sqlSession = mock(SqlSession.class);
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
when(sqlSession.flushStatements())
.thenReturn(List.of(batchResult(1, 1)), List.of(batchResult(1)));
TestEntity first = new TestEntity(1L);
TestEntity second = new TestEntity(2L);
TestEntity third = new TestEntity(3L);
try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
assertTrue(service.insertBatch(List.of(first, second, third), 2));
}
verify(sqlSession).insert(INSERT_STATEMENT, first);
verify(sqlSession).insert(INSERT_STATEMENT, second);
verify(sqlSession).insert(INSERT_STATEMENT, third);
verify(sqlSession, times(2)).flushStatements();
verify(sqlSession).commit(true);
verify(sqlSession).close();
}
@Test
void updateBatchByIdPassesEachEntityAndReturnsSuccessfulFlushResult() {
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
SqlSession sqlSession = mock(SqlSession.class);
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
when(sqlSession.update(eq(UPDATE_STATEMENT), any())).thenReturn(1);
when(sqlSession.flushStatements()).thenReturn(List.of(batchResult(1, 1)));
TestEntity first = new TestEntity(1L);
TestEntity second = new TestEntity(2L);
try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
assertTrue(service.updateBatchById(List.of(first, second), 10));
}
ArgumentCaptor<Object> params = ArgumentCaptor.forClass(Object.class);
verify(sqlSession, times(2)).update(eq(UPDATE_STATEMENT), params.capture());
assertSame(first, entityFrom(params.getAllValues().get(0)));
assertSame(second, entityFrom(params.getAllValues().get(1)));
verify(sqlSession).flushStatements();
}
@Test
void batchCallbacksReturnActualAffectedRowCounts() {
SqlSession sqlSession = mock(SqlSession.class);
when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(3);
when(sqlSession.update(eq(UPDATE_STATEMENT), any())).thenReturn(2);
CallbackCapturingTestService capturingService = new CallbackCapturingTestService(sqlSession);
TestEntity entity = new TestEntity(1L);
assertTrue(capturingService.insertBatch(List.of(entity), 7));
assertEquals(7, capturingService.batchSize);
assertEquals(List.of(3), capturingService.affectedRows);
assertTrue(capturingService.updateBatchById(List.of(entity), 9));
assertEquals(9, capturingService.batchSize);
assertEquals(List.of(2), capturingService.affectedRows);
}
@Test
void emptyBatchesReturnFalseWithoutOpeningABatchSession() {
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
assertFalse(service.insertBatch(List.of(), 10));
assertFalse(service.updateBatchById(List.of(), 10));
}
verifyNoInteractions(sqlSessionFactory);
}
@Test
void activeTransactionSynchronizationUsesTransactionAwareCommitAndLifecycle() {
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
SqlSession sqlSession = mock(SqlSession.class);
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
when(sqlSession.flushStatements()).thenReturn(List.of(batchResult(1)));
TransactionSynchronizationManager.initSynchronization();
try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
assertTrue(service.insertBatch(List.of(new TestEntity(1L)), 1));
}
verify(sqlSession).flushStatements();
verify(sqlSession).commit(false);
verify(sqlSession, never()).commit(true);
verify(sqlSession, never()).close();
}
@SuppressWarnings("deprecation")
private static MockedStatic<SqlHelper> sqlHelperUsing(SqlSessionFactory sqlSessionFactory) {
MockedStatic<SqlHelper> sqlHelper = mockStatic(SqlHelper.class, CALLS_REAL_METHODS);
sqlHelper.when(() -> SqlHelper.sqlSessionFactory(TestEntity.class)).thenReturn(sqlSessionFactory);
return sqlHelper;
}
private static BatchResult batchResult(int... updateCounts) {
BatchResult result = new BatchResult(mock(MappedStatement.class), "batch");
result.setUpdateCounts(updateCounts);
return result;
}
private static Object entityFrom(Object parameter) {
assertTrue(parameter instanceof MapperMethod.ParamMap<?>);
return ((Map<?, ?>) parameter).get(Constants.ENTITY);
}
private interface TestMapper extends BaseMapper<TestEntity> {
}
private record TestEntity(Long id) {
}
private static class TestService extends BaseServiceImpl<TestMapper, TestEntity> {
@Override
protected String getSqlStatement(SqlMethod sqlMethod) {
return switch (sqlMethod) {
case INSERT_ONE -> INSERT_STATEMENT;
case UPDATE_BY_ID -> UPDATE_STATEMENT;
default -> throw new IllegalArgumentException("Unexpected SQL method: " + sqlMethod);
};
}
}
private static class CallbackCapturingTestService extends TestService {
private final SqlSession sqlSession;
private int batchSize;
private List<Integer> affectedRows;
private CallbackCapturingTestService(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
protected <E> boolean executeBatch(Collection<E> list, int batchSize,
BiFunction<SqlSession, E, Integer> operation) {
this.batchSize = batchSize;
this.affectedRows = list.stream().map(entity -> operation.apply(sqlSession, entity)).toList();
return true;
}
}
}
@@ -19,6 +19,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.repository.IRepository;
import com.fasterxml.jackson.core.type.TypeReference;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
@@ -1172,7 +1173,7 @@ class AgentSnapshotServiceImplTest {
&& "普通话".equals(agent.getTtsLanguage())
&& "".equals(agent.getSummaryMemory())
&& Integer.valueOf(0).equals(agent.getChatHistoryConf())));
inOrder.verify(pluginMappingService).saveBatch(any());
inOrder.verify(pluginMappingService).saveBatch(any(), eq(IRepository.DEFAULT_BATCH_SIZE));
inOrder.verify(snapshotService).createSnapshot(agentId, "initial");
}
@@ -1407,7 +1408,7 @@ class AgentSnapshotServiceImplTest {
return mapping != null
&& "plugin".equals(mapping.getPluginId())
&& mapping.getParamInfo().contains("target");
}));
}), eq(IRepository.DEFAULT_BATCH_SIZE));
verify(snapshotDao).insertWithNextVersion(argThat(snapshot -> "restore".equals(snapshot.getSource())));
}