如何优化PreparedStatement的INSERT语句,避免使用多个占位符问号?
避免PreparedStatement INSERT语句中多问号占位符的几种方案
如果不想在原生JDBC的PreparedStatement里写一堆问号占位符,有以下几种实用方案:
1. 借助Spring JDBC的命名参数支持
JDBC原生不支持命名参数,但Spring JDBC的NamedParameterJdbcTemplate可以直接用:参数名替代问号,可读性拉满,不用再记参数位置序号:
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import java.util.HashMap; import java.util.Map; public static int insert(int id, String name, String address, String phone, int age) { String SQL = "INSERT INTO some_table (ID, Name, Address, Phone_Number, Age) VALUES (:id, :name, :address, :phone, :age)"; Map<String, Object> paramMap = new HashMap<>(); paramMap.put("id", id); paramMap.put("name", name); paramMap.put("address", address); paramMap.put("phone", phone); paramMap.put("age", age); NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(jdbc.connection); return jdbcTemplate.update(SQL, paramMap); }
2. 自己封装简单的命名参数工具类
如果不想引入Spring依赖,可以写个轻量工具类,用正则把命名参数替换成问号,再自动绑定参数:
import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class NamedParamHelper { public static ParsedResult parseSql(String sql) { Pattern pattern = Pattern.compile(":([a-zA-Z0-9_]+)"); Matcher matcher = pattern.matcher(sql); StringBuilder parsedSql = new StringBuilder(); String[] paramNames = new String[matcher.groupCount()]; int idx = 0; while (matcher.find()) { paramNames[idx++] = matcher.group(1); matcher.appendReplacement(parsedSql, "?"); } matcher.appendTail(parsedSql); return new ParsedResult(parsedSql.toString(), paramNames); } public static void setParams(PreparedStatement ps, ParsedResult result, Map<String, Object> params) throws SQLException { for (int i = 0; i < result.paramNames.length; i++) { Object value = params.get(result.paramNames[i]); if (value instanceof Integer) { ps.setInt(i + 1, (Integer) value); } else if (value instanceof String) { ps.setString(i + 1, (String) value); } // 按需扩展其他数据类型的支持 } } public static class ParsedResult { public final String sql; public final String[] paramNames; public ParsedResult(String sql, String[] paramNames) { this.sql = sql; this.paramNames = paramNames; } } } // 使用示例 public static int insert(int id, String name, String address, String phone, int age) throws SQLException { String rawSql = "INSERT INTO some_table (ID, Name, Address, Phone_Number, Age) VALUES (:id, :name, :address, :phone, :age)"; NamedParamHelper.ParsedResult result = NamedParamHelper.parseSql(rawSql); PreparedStatement ps = jdbc.connection.prepareStatement(result.sql); Map<String, Object> params = new HashMap<>(); params.put("id", id); params.put("name", name); params.put("address", address); params.put("phone", phone); params.put("age", age); NamedParamHelper.setParams(ps, result, params); return ps.executeUpdate(); }
3. 用ORM框架彻底简化代码
像MyBatis这类ORM框架本身就支持命名参数,完全不用手动处理PreparedStatement:
先定义Mapper接口:
import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; public interface SomeTableMapper { @Insert("INSERT INTO some_table (ID, Name, Address, Phone_Number, Age) VALUES (#{id}, #{name}, #{address}, #{phone}, #{age})") int insert(@Param("id") int id, @Param("name") String name, @Param("address") String address, @Param("phone") String phone, @Param("age") int age); }
使用时直接调用Mapper的insert方法即可,参数绑定、SQL执行都由框架自动完成。
4. 基于实体类自动生成SQL和参数绑定
如果用实体类封装参数,可以通过反射自动拼接SQL和绑定参数,彻底告别手动写问号:
先定义实体类:
public class SomeEntity { private int id; private String name; private String address; private String phone; private int age; // getter、setter省略 }
然后编写通用插入方法:
import java.sql.PreparedStatement; import java.sql.SQLException; import java.lang.reflect.Field; public static int insert(SomeEntity entity) throws SQLException, IllegalAccessException { StringBuilder columns = new StringBuilder("INSERT INTO some_table ("); StringBuilder values = new StringBuilder(" VALUES ("); Field[] fields = entity.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; field.setAccessible(true); columns.append(field.getName()); values.append("?"); if (i != fields.length - 1) { columns.append(", "); values.append(", "); } } columns.append(")"); values.append(")"); String sql = columns.toString() + values.toString(); PreparedStatement ps = jdbc.connection.prepareStatement(sql); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; field.setAccessible(true); Object value = field.get(entity); if (value instanceof Integer) { ps.setInt(i + 1, (Integer) value); } else if (value instanceof String) { ps.setString(i + 1, (String) value); } // 按需扩展其他类型 } return ps.executeUpdate(); }
内容的提问来源于stack exchange,提问作者Kyle
相关产品推荐
相关产品推荐

