利用spring改造傳統jdbc使其支援命名引數形式的SQL
阿新 • • 發佈:2018-11-10
傳統JDBC SQL呼叫形式一般為
update customer set customerName=?, email=? where id=?
insert into customer (customerName, email ) values(?, ?)
這種由於?與引數關係的不直觀,帶來的修改bug以及維護麻煩折磨著後續程式猿。那麼,有沒有可能通過對程式碼的改造,實現類似於命名引數SQL支援呢?
update customer set customerName=:customerName, email=:email where id=:id insert into customer (customerName, email ) values(:customerName, :email)
答案是肯定的, 本文通過對 NamedParameterJdbcTemplate 實現機制原始碼的解讀,借鑑了NamedParameterUtils 原始碼的方法對上面需求的實現做了可行性驗證。
關鍵程式碼
import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.TreeMap; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.MapListHandler; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterUtils; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; /** * * 將namedParamSQL轉換為帶引數列表的傳統SQL執行 * * @author 00fly * @version [版本號, 2018年11月10日] * @see [相關類/方法] * @since [產品/模組版本] */ public class ConvertSQLTest { private static final Logger logger = LoggerFactory.getLogger(ConvertSQLTest.class); private static QueryRunner queryRunner; static { MysqlDataSource dataSource = new MysqlDataSource(); ResourceBundle config = ResourceBundle.getBundle("jdbc"); dataSource.setUrl(config.getString("jdbc.url")); dataSource.setUser(config.getString("jdbc.username")); dataSource.setPassword(config.getString("jdbc.password")); queryRunner = new QueryRunner(dataSource); logger.info("QueryRunner = {}", queryRunner); } @Test public void test1() throws SQLException { // 將namedParamSQL轉換為帶引數列表的傳統SQL String namedParamSQL = "select id, name from student where id<>:id and (id=:id1 or id=:id2)"; Map<String, Object> paramMap = new TreeMap<>(); paramMap.put("id", 1); paramMap.put("id1", 2); paramMap.put("id2", 3); paramMap.put("name", "001"); paramMap.put("name1", "002"); paramMap.put("name2", "003"); String realSql = buildRealSQL(namedParamSQL, paramMap); Object[] paramArr = buildValueArray(namedParamSQL, paramMap); List<Map<String, Object>> list = queryRunner.query(realSql, new MapListHandler(), paramArr); logger.info("★★★★ before: namedParamSQL = {}", namedParamSQL); logger.info("★★★★ before: paramMap = {}", paramMap); logger.info("★★★★ execute: realSql = {}", realSql); logger.info("★★★★ execute: paramArr = {}", Arrays.asList(paramArr)); logger.info("★★★★ execute: result = {}", list); } @Test public void test2() throws SQLException { // IN條件 String namedParamSQL = "select id, name from student where id in (:ids)"; Object[] ids = new Integer[] {1, 2, 3, 4, 5}; Map<String, Object> paramMap = new TreeMap<>(); paramMap.put("ids", Arrays.asList(ids)); String realSql = buildRealSQL(namedParamSQL, paramMap); Object[] paramArr = buildValueArray(namedParamSQL, paramMap); List<Map<String, Object>> list = queryRunner.query(realSql, new MapListHandler(), paramArr); logger.info("★★★★ before: namedParamSQL = {}", namedParamSQL); logger.info("★★★★ before: paramMap = {}", paramMap); logger.info("★★★★ execute: realSql = {}", realSql); logger.info("★★★★ execute: paramArr = {}", Arrays.asList(paramArr)); logger.info("★★★★ execute: result = {}", list); } @Test public void test3() throws SQLException { // IN條件&&其他條件 String namedParamSQL = "select id, name from student where id=:id or id in (:ids)"; Object[] ids = new Integer[] {1, 2, 3, 4, 5}; Map<String, Object> paramMap = new TreeMap<>(); paramMap.put("id", 1); paramMap.put("ids", Arrays.asList(ids)); String realSql = buildRealSQL(namedParamSQL, paramMap); Object[] paramArr = buildValueArray(namedParamSQL, paramMap); List<Map<String, Object>> list = queryRunner.query(realSql, new MapListHandler(), paramArr); logger.info("★★★★ before: namedParamSQL = {}", namedParamSQL); logger.info("★★★★ before: paramMap = {}", paramMap); logger.info("★★★★ execute: realSql = {}", realSql); logger.info("★★★★ execute: paramArr = {}", Arrays.asList(paramArr)); logger.info("★★★★ execute: result = {}", list); } /** * 將namedParamSQL轉換為帶?的傳統SQL * * @param namedParamSQL 命名引數SQL * @param paramMap Map引數 * @return * @see [類、類#方法、類#成員] */ private String buildRealSQL(String namedParamSQL, Map<String, Object> paramMap) { return NamedParameterUtils.substituteNamedParameters(NamedParameterUtils.parseSqlStatement(namedParamSQL), new MapSqlParameterSource(paramMap)); } /** * 濾除無效引數列表後,以Object[]返回 * * @param namedParamSQL 命名引數SQL * @param paramMap Map引數 * @return * @see [類、類#方法、類#成員] */ private Object[] buildValueArray(String namedParamSQL, Map<String, Object> paramMap) { Object[] params = NamedParameterUtils.buildValueArray(namedParamSQL, paramMap); List<Object> paramList = new ArrayList<>(); for (Object obj : params) { if (List.class.isInstance(obj)) { paramList.addAll((List<?>)obj); } else { paramList.add(obj); } } return paramList.toArray(); } }
執行結果
2018-11-10 13:33:24 |INFO |main|★★★★ before: namedParamSQL = select id, name from student where id<>:id and (id=:id1 or id=:id2)| 2018-11-10 13:33:24 |INFO |main|★★★★ before: paramMap = {id=1, id1=2, id2=3, name=001, name1=002, name2=003}| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: realSql = select id, name from student where id<>? and (id=? or id=?)| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: paramArr = [1, 2, 3]| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: result = [{id=2, name=Phil}, {id=3, name=Jenny}]| 2018-11-10 13:33:24 |INFO |main|★★★★ before: namedParamSQL = select id, name from student where id in (:ids)| 2018-11-10 13:33:24 |INFO |main|★★★★ before: paramMap = {ids=[1, 2, 3, 4, 5]}| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: realSql = select id, name from student where id in (?, ?, ?, ?, ?)| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: paramArr = [1, 2, 3, 4, 5]| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: result = [{id=1, name=Jack}, {id=2, name=Phil}, {id=3, name=Jenny}]| 2018-11-10 13:33:24 |INFO |main|★★★★ before: namedParamSQL = select id, name from student where id=:id or id in (:ids)| 2018-11-10 13:33:24 |INFO |main|★★★★ before: paramMap = {id=1, ids=[1, 2, 3, 4, 5]}| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: realSql = select id, name from student where id=? or id in (?, ?, ?, ?, ?)| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: paramArr = [1, 1, 2, 3, 4, 5]| 2018-11-10 13:33:24 |INFO |main|★★★★ execute: result = [{id=1, name=Jack}, {id=2, name=Phil}, {id=3, name=Jenny}]|
完整的程式碼請參考: 完整的專案程式碼請參考:
https://gitee.com/00fly/java-code-frame/tree/master/jdbc