時隔三個多月,我終於想起我還有個博客,其實也不是忘了我這個博客,只是平時工作繁忙沒時間去寫博客,故今晚騰出時間來記錄一下上次工作中遇到的一個問題,給園友們分享出來,以免入坑。
上個星期在工作中使用JdbcTemplate執行了一個select * from table where id in (?,?,?) and name = ? 的SQL,這個SQL大家都明白什么意思吧,然后我得給這幾個 "?" 賦值,沒問題吧。可是在執行的時候給我報了一個異常:java.sql.SQLException: No value specified for parameter 3 納尼?這就有點奇怪了,幾個意思?是因為jdbcTemplate這個對象不支持括號內的問號和括號外的問號同時出現,什么意思呢?就是說 where id in (?) 是正確的 where name = ? 也是正確的,但是 where id in (?) and name = ? 就不支持了;需要將 jdbcTemplate 對象換成 NamedParameterJdbcTemplate 即可,下面我會把代碼貼出來,首先創建一個SpringBoot項目,將Web,MySQL,Jdbc,Lombok的依賴導入進去,然后我們開始擼代碼:
我們先來看一下數據庫的數據:
下面是建表語句,有需要的同志們可以復制過去:

CREATE TABLE `t_book` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `isbn` varchar(255) DEFAULT NULL, `publish` varchar(255) DEFAULT NULL, `publisher` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
實體類和表映射的RowMapper實現類的代碼就不貼了,按照表字段去建一個就可以了,這一步跳過
下面是controller和dao的代碼:

@RestController @RequestMapping("/api/v1/book") public class BookRestController { @Resource private ObjectMapper jsonMapper; // 這里為了方便就省略service層,直接將dao注入進來了 @Resource private BookDAO bookDAO; @GetMapping("/list") public JsonNode list(){ ObjectNode respJson = jsonMapper.createObjectNode(); List<Integer> idList = Arrays.asList(1, 2, 3); String name = "mybatis從入門到精通"; List<TBook> bookList = bookDAO.selectByDynamicParams(idList, name); return respJson.putPOJO("data",bookList); } }

@Component("bookDAO") public class BookDAOImpl implements BookDAO { @Autowired private JdbcTemplate jdbcTemplate; @Override public List<TBook> selectByDynamicParams(List<Integer> idList, String name) { StringBuffer sql = new StringBuffer(); sql.append("select * from t_book where id in ( "); // 拼裝? List<String> stringList = new ArrayList<>(); for (int i = 0;i < idList.size();i++){ stringList.add("?"); } sql.append(String.join(",",stringList)); sql.append(" ) and name = ?"); System.err.println(sql.toString()); return jdbcTemplate.query(sql.toString(),new BookMapper(),idList,name); } }
好了,執行一把,看一下結果吧,訪問 localhost:8080/api/v1/book/list ,控制台保如下錯誤:
可以看出來,SQL語句的沒有問題的,那么我們嘗試換一種寫法,使用 NamedParameterJdbcTemplate ,由於spring默認是加載jdbcTemplate的,所以NamedParameterJdbcTemplate 對象需要我們自己去配置,創建一個配置類或者直接在啟動類配置也可以,啟動類其實就是一個配置類,進行如下配置:
// 從容器中將dataSource這個對象注入進來 @Autowired private DataSource dataSource; @Bean public NamedParameterJdbcTemplate namedParameterJdbcTemplate(){ // 要構造NamedParameterJdbcTemplate對象需要依賴dataSource return new NamedParameterJdbcTemplate(dataSource); }
dao層將NamedParameterJdbcTemplate 對象注入進來並執行SQL:
@Component("bookDAO") public class BookDAOImpl implements BookDAO { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private NamedParameterJdbcTemplate namedParameterJdbcTemplate; @Override public List<TBook> selectByDynamicParams(List<Integer> idList, String name) { String sql = "select * from t_book where id in (:idList) and name = :bookName"; // 集合中的key一定要與sql中定義的參數變量相同,這里的集合里的參數不需要進行手動處理,直接將集合作為一個參數對象傳進去即可 Map<String,Object> params = new HashMap<>(2); params.put("idList",idList); params.put("bookName",name); return namedParameterJdbcTemplate.query(sql,params,new BookMapper()); } }
這次再執行一把看一下結果:
這次就執行成功了,控制台也沒有報錯,很舒服,我記得以前使用原生jdbc進行查詢的時候當時使用的是sqlserver數據庫,寫了一條分頁的SQL,當時也是有問號的,同樣會報錯,沒想到spring的這個jdbc跟原生的jdbc一樣坑,只能說技術這種東西學無止境呀。
革命尚未成功,同志仍需努力!