為什麼要具名參數?
- 傳統
? 佔位符**強依賴位置**,SQL 調整順序需同步修改綁定代碼,維護成本高。
- 具名參數(
:name)**按名稱綁定**,順序無關,可讀性強,重構安全。
核心類:NamedParameterJdbcTemplate
- 內部封裝
JdbcTemplate,繼承其所有能力,新增命名參數支持。
- 構造器需傳入
DataSource 或 JdbcTemplate。
Spring 配置
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
參數綁定兩大方式
| 方式 | 實現類 | 適用場景 |
| Map | MapSqlParameterSource / 普通 Map | 動態參數、非實體類場景 |
| JavaBean | BeanPropertySqlParameterSource | 實體類屬性直接映射參數名 |
BaseService 封裝範例
public class BaseService {
@Autowired protected NamedParameterJdbcTemplate npjt;
// 查詢列表:BeanPropertyRowMapper 自動映射列名→屬性名
public List> query(String sql, Object paramObj) {
SqlParameterSource source = new BeanPropertySqlParameterSource(paramObj);
return npjt.query(sql, source, new BeanPropertyRowMapper<>(paramObj.getClass()));
}
// 增刪改:支援 Map 或 Bean
public int execute(String sql, Object paramObj) {
if (paramObj == null) return npjt.getJdbcOperations().update(sql);
if (paramObj instanceof Map) return npjt.update(sql, (Map) paramObj);
return npjt.update(sql, new BeanPropertySqlParameterSource(paramObj));
}
// 查詢單值(如 count)
public int queryCount(String sql, Object paramObj) {
SqlParameterSource source = paramObj != null ? new BeanPropertySqlParameterSource(paramObj) : null;
return npjt.queryForObject(sql, source, Integer.class);
}
}
實體類與 Service 實現
public class User {
private String username, password, authorize;
private Date createtime;
private Boolean enabled;
// getter/setter
}
@Service
public class UserServiceImpl extends BaseService implements UserService {
public List queryUsers() {
String sql = "SELECT suser AS username, spassword AS password, tcreatetime AS createtime, cisenabled AS enabled, cauthorize AS authorize FROM sys_user";
return (List) query(sql, new User());
}
public MsgBean addUser(User u) {
String sql = "INSERT INTO sys_user(suser,spassword,tcreatetime,cisenabled,cauthorize) VALUES(:username,:password,:createtime,:enabled,:authorize)";
return execute(sql, u) > 0 ? success() : fail();
}
public MsgBean updateUser(User u) {
String sql = "UPDATE sys_user SET spassword=:password, tcreatetime=:createtime, cisenabled=:enabled, cauthorize=:authorize WHERE suser=:username";
return execute(sql, u) > 0 ? success() : fail();
}
public MsgBean deleteUser(String username) {
String sql = "DELETE FROM sys_user WHERE suser = :username";
return execute(sql, Collections.singletonMap("username", username)) > 0 ? success() : fail();
}
}
Controller 暴露 REST API
@RestController @RequestMapping("/user")
public class UserController {
@Autowired UserService userService;
@GetMapping("/list") public Object list() { return userService.queryUsers(); }
@PostMapping("/add") public MsgBean add(@RequestBody User u) { return userService.addUser(u); }
@PutMapping("/update") public MsgBean update(@RequestBody User u) { return userService.updateUser(u); }
@DeleteMapping("/delete") public MsgBean delete(@RequestParam String username) { return userService.deleteUser(username); }
}
關鍵優勢總結
- SQL 可讀性大幅提升:
:username 直觀語義化。
- 參數順序解耦:調整 SQL 字段順序無需動 Java 代碼。
- 實體類屬性自動映射:
BeanPropertySqlParameterSource 零樣板代碼。
- 兼容
Map 靈活場景:動態構建參數無需實體類。