动态构建INSERT语句必须只拼字段名和占位符、值全走PreparedStatement绑定;Java反射需跳过null/空字符串字段,统一转换字段名,剔除自增主键;MyBatis用+处理动态字段;SQL Server导出需过滤identity/computed列、显式类型转换、控制批量行数。

直接拼 INSERT 语句字符串是危险且不可维护的,真正可行的动态构建必须满足两个前提:不拼接值(只拼字段名和占位符),所有值走 PreparedStatement 参数绑定。否则 SQL 注入、类型错乱、空值处理失败等问题会立刻暴露。
Java 反射遍历非 null 字段生成 INSERT
核心是跳过 null 或空字符串字段,只把有值的属性映射为列名 + ? 占位符。关键点在于:
-
PropertyDescriptor获取读方法后,用getReadMethod().invoke(entity)安全取值,避免 NPE - 字段名转换需统一走
NameHandler(如驼峰转下划线),否则表结构不匹配 - 不能忽略主键自增场景:若实体含
id且数据库设为IDENTITY,该字段必须从插入字段列表中剔除,否则报错 -
StringBuilder拼接时注意末尾逗号处理——用deleteCharAt()比正则或条件判断更稳
MyBatis 的 动态标签怎么写才不漏字段
MyBatis 原生支持字段级动态,但容易写成“全字段必填”或“硬编码字段”。正确写法依赖 和 组合:
<insert id="insertSelective" parametertype="User">
INSERT INTO user
<trim prefix="(" suffix=")" suffixoverrides=","><if test="userName != null and userName != ''">user_name,</if><if test="email != null and email != ''">email,</if><if test="phone != null and phone != ''">phone,</if></trim>
VALUES
<trim prefix="(" suffix=")" suffixoverrides=","><if test="userName != null and userName != ''">#{userName},</if><if test="email != null and email != ''">#{email},</if><if test="phone != null and phone != ''">#{phone},</if></trim></insert>
注意:suffixOverrides="," 是关键,它能自动删掉最后多出的逗号;test 表达式里必须同时判 null 和空字符串,否则插入空串导致业务异常。
SQL Server 批量生成表级 INSERT 脚本的坑
当需要导出整个库的 INSERT 语句(比如跨环境同步),直接用 SSMS 导出常失败,尤其遇到 IDENTITY 列或 computed 列。可靠做法是用系统视图动态拼:
- 查
sys.columns过滤掉is_identity = 1和is_computed = 1的列 - 对
datetime、uniqueidentifier等类型字段,SELECT 时必须显式转成字符串格式(如CONVERT(varchar, getdate(), 120)),否则脚本执行报类型不匹配 - 生成的脚本开头要加
SET IDENTITY_INSERT [table] ON,结尾加OFF,否则无法插入自增列 - 单条
INSERT最多支持 1000 行 VALUES,超量必须拆成多个语句,不然报错
动态构建 INSERT 最难的不是拼语法,而是边界控制:哪些字段该进、哪些值该绑、哪些类型要转、哪些约束要绕开。漏掉任意一环,生成的语句在测试环境跑得通,上线就挂。










