1,测试实体类
package org.springblade.sample.test;import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;/*** @Author: 肖扬* @CreateTime: 2025-09-05* @Description: SqlSessionFactoryTest测试* @Version: 1.0*/
@Data
@TableName("session_factory_pojo")
public class SessionFactoryPojo {private Long id;private String name;private String age;
}
2,测试Mapper类
package org.springblade.sample.test;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springblade.sample.entity.PangolinCsvData;import java.util.List;@Mapper
public interface SessionFactoryMapper extends BaseMapper<SessionFactoryPojo> {void insertBatch(@Param("list") List<PangolinCsvData> list);
}
3,测试Xml类
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.springblade.sample.test.SessionFactoryMapper"><insert id="insertBatch">INSERT INTO $TABLE_NAME$ (id,name,age) VALUES<foreach collection="list" item="item" index="index" separator=",">(#{item.id},#{item.name},#{item.age})</foreach></insert>
</mapper>
4,工具类
package org.springblade.sample.utils;import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.StringUtil;import java.sql.Timestamp;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @Author: 肖扬* @CreateTime: 2025-09-05* @Description: MyBatis BoundSql 转可执行 SQL 工具类(支持 list、对象、map)* @Version: 2.0*/
@Slf4j
@UtilityClass
public class SqlBuilderUtil {private final Pattern LIST_PARAM_PATTERN = Pattern.compile("__frch_item_(\\d+)\\.(.+)");/*** 将 BoundSql 转换为可执行 SQL** @param boundSql MyBatis BoundSql* @param paramObject Mapper 参数(支持 list、单对象、Map)* @param ListKey 参数key* @return 可执行 SQL 字符串*/public String buildExecutableSql(BoundSql boundSql, Object paramObject, String ListKey) {String sql = boundSql.getSql();List<ParameterMapping> paramMappings = boundSql.getParameterMappings();if (paramMappings == null || paramMappings.isEmpty()) {return sql;}StringBuilder resultSql = new StringBuilder(sql);for (ParameterMapping p : paramMappings) {String paramName = p.getProperty();Object paramValue = resolveParamValue(paramName, paramObject, ListKey);String replacement = formatSqlValue(paramValue);// 替换第一个 ? 为实际值int qIndex = resultSql.indexOf("?");if (qIndex >= 0) {resultSql.replace(qIndex, qIndex + 1, replacement);}}return resultSql.toString();}/*** 根据参数名解析参数值,支持 list / Map / 普通对象*/private Object resolveParamValue(String paramName, Object paramObject, String listKey) {if (paramObject == null) {return null;}// 1. 支持 list 参数(批量 insert/update)if (paramObject instanceof MapperMethod.ParamMap) {MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap<?>) paramObject;// list 参数if (StringUtil.isNotBlank(listKey) && paramMap.containsKey(listKey)) {List<?> listObj = (List<?>) paramMap.get(listKey);Matcher matcher = LIST_PARAM_PATTERN.matcher(paramName);if (matcher.matches()) {int index = Integer.parseInt(matcher.group(1));String property = matcher.group(2);if (listObj != null && index < listObj.size()) {return BeanUtil.toMap(listObj.get(index)).get(property);}}}// 其他 Map 参数(#{xxx} 这种)if (paramMap.containsKey(paramName)) {return paramMap.get(paramName);}}// 2. 直接传入 Mapif (paramObject instanceof Map) {return ((Map<?, ?>) paramObject).get(paramName);}// 3. 普通对象,通过反射取属性Map<String, Object> objMap = BeanUtil.toMap(paramObject);return objMap.get(paramName);}/*** 参数值转 SQL 字符串*/private String formatSqlValue(Object value) {if (value == null) {return "NULL";}if (value instanceof String) {// 简单转义单引号return "'" + ((String) value).replace("'", "''") + "'";}if (value instanceof Number) {return String.valueOf(value);}if (value instanceof Date) {return "'" + new Timestamp(((Date) value).getTime()) + "'";}return "'" + value.toString().replace("'", "''") + "'";}
}
5,测试Controller
package org.springblade.sample.test;import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.sample.utils.SqlBuilderUtil;
import org.springblade.scheduled.exception.ScheduledException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @Author: 肖扬* @CreateTime: 2025-09-05* @Description: 测试SqlSessionFactoryTest* @Version: 1.0*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/scheduled/")
@Api(tags = "SqlSessionFactoryTest")
@Slf4j
public class SessionFactoryTestController {private final JdbcTemplate jdbcTemplate;private final SqlSessionFactory sqlSessionFactory;private final PlatformTransactionManager transactionManager;@GetMapping( "testSessionFactory")public void testSqlSessionFactory() {List<SessionFactoryPojo> list = new ArrayList<>();list.add(new SessionFactoryPojo(2L, "李四", "25"));String tableName = "session_factory_pojo";String mapperMethod = "org.springblade.sample.test.SessionFactoryMapper.insertBatch";MapperMethod.ParamMap<List<?>> paramMap = new MapperMethod.ParamMap<>();paramMap.put("list",list );//开启事务DefaultTransactionDefinition def = new DefaultTransactionDefinition();def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);TransactionStatus status = transactionManager.getTransaction(def);try (SqlSession sqlSession = sqlSessionFactory.openSession()) {Configuration configuration = sqlSession.getConfiguration();MappedStatement mappedStatement = configuration.getMappedStatement(mapperMethod);BoundSql boundSql = mappedStatement.getBoundSql(paramMap);//buildExecutableSql的listKey和paramMap的key和insertBatch的参数名称和一致String sql = SqlBuilderUtil.buildExecutableSql(boundSql, paramMap, "list");sql = sql.replace("$TABLE_NAME$", tableName);log.info("sql:{}", sql);jdbcTemplate.execute(sql);// 提交事务transactionManager.commit(status);}catch (Exception e){log.error("createTidalTask occurred exception: {}", e.getMessage(), e);// 发生异常时回滚事务transactionManager.rollback(status);throw new ScheduledException("任务执行失败!");}}
}