JAVA jdbc封装 DbUtil 类3.0:DbUtilsTag -使用 #{} 占位符(废弃) 作者:马育民 • 2022-03-11 09:51 • 阅读:10069 # 废弃 本文已废弃,改为:[JAVA jdbc封装 DbUtil 类3.1:DbUtilsTag -使用 #{} 占位符](https://www.malaoshi.top/show_1IX2ymNtgfKD.html "JAVA jdbc封装 DbUtil 类3.1:DbUtilsTag -使用 #{} 占位符") # 说明 在 [JAVA jdbc封装 DbUtil 类2.0-增加事务功能](https://www.malaoshi.top/show_1IX2iXvrIKqt.html "JAVA jdbc封装 DbUtil 类2.0-增加事务功能") 中,已经实现了功能,但 **缺点是**,当 SQL 参数过多时,会有 **n 个 ?**,传入的参数会很多,容易出现错误 # 解决 ### SQL 占位符 使用 `#{}` 占位符,如下: ``` update emp set ename=#{ename},job=#{job},sal=#{sal},comm=#{comm} where empno=#{empno} ``` ``` select empno,ename,job,sal,comm,deptno from emp where #{sal} < sal and sal < #{maxSal} ``` ### 传参 传入 `Map` 类型,`key` 是 `#{}` 中的 值,如下: 上面第一个SQL参数: ``` Map params=new HashMap<>(); params.put("ename", "李雷2"); params.put("job", "程序员2"); params.put("sal", "20000"); params.put("comm", "142"); params.put("empno", "10008"); ``` 上面第二个SQL参数: ``` Map params=new HashMap<>(); params.put("sal", 2500); params.put("maxSal", 4000); ``` # 完整代码 ### 核心类 继承 [JAVA jdbc封装 DbUtil 类2.0-增加事务功能](https://www.malaoshi.top/show_1IX2iXvrIKqt.html "JAVA jdbc封装 DbUtil 类2.0-增加事务功能") 中的类 ``` import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * * 数据库工具类,使用 #{} 占位符 * @author mym * */ public class DbUtilsTag extends DbUtils2{ /** * sql语句使用 #{} 占位符 * @param sql * @param params * @return * @throws SQLException */ public int updateTag(String sql , Map params ) throws SQLException{ TagSQLParser p=new TagSQLParser(sql); String selectSQL=p.parse(); List keywords=p.getKeywords(); PreparedStatement pstm=conn.prepareStatement(selectSQL); for(int i=0; i params) throws SQLException{ TagSQLParser p=new TagSQLParser(sql); String selectSQL=p.parse(); List keywords=p.getKeywords(); PreparedStatement pstm=conn.prepareStatement(selectSQL); for(int i=0; i(); for(int i=0 ; i List queryTag(String sql,Map params,Class clazz) throws SQLException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ TagSQLParser p=new TagSQLParser(sql); String selectSQL=p.parse(); List keywords=p.getKeywords(); PreparedStatement pstm=conn.prepareStatement(selectSQL); for(int i=0; i List handlResultSet(ResultSet rs,Class clazz) throws SQLException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ ResultSetMetaData rsm=rs.getMetaData(); int count=rsm.getColumnCount();//动态返回查询的列数 //ret是return的缩写,表示这个list是要返回的 List retList=new ArrayList(); while(rs.next()){// 判断是否有查询结果,刚学的人,问:为啥这么写,没有原因,java当初就是这么设计的 Object obj=clazz.newInstance(); Method[] setters=getSetterMethods(clazz); for(int i=0 ; i list=new ArrayList<>(); for(int i=0,len=array.length;i keywords; /** * debug模式,格式化后,可以打印sql */ private boolean debug=false; //格式化后的sql,去掉多余的空格、tab、\n private char[] formatSQL=null; private int formatSQLIndex=0;//当前位置 private int formatSQLLen=0; public TagSQLParser(String sql) { srcSQL=sql.toCharArray(); int len=srcSQL.length; if(len==0){ throw new TagSQLSyntaxException("请传入有效的SQL!"); } } /** * 给 formatBuf 填充字符 * @param c */ private void fillFormatBuf(char c){ formatSQL[formatSQLIndex]=c; formatSQLIndex++; } private void format(){ removeWhite(); } /** * 将 tab、\t、\n、\r\n 替换成 空格 * 将 #{ 后面的空格移除, } 前面的空格移除 * 当多个空格时,只保留一个空格 */ private void removeWhite(){ int srcLen=srcSQL.length; formatSQL=new char[srcLen]; char lastC=0; char c=0; int srcIndex=0; do{ c=srcSQL[srcIndex]; srcIndex++; if(c=='\r'){ //如果是windows换行符 \r,就丢弃,只认 \n continue; } if(isSpaceTabLine(lastC) ){ //上一次是空格、tab键、\n if( isSpaceTabLine(c) ){ //本次是空格、tab键、\n,就不填充字符 }else if( c == '}' ){//本次是 } ,就将上一个空格移除,并填充 } formatSQLIndex--; fillFormatBuf(c); }else{ fillFormatBuf(c); } }else if(lastC == '{'){ //上一次是 { if( isSpaceTabLine(c) ){ //本次是空格、tab键、\n,就不填充字符 }else{ fillFormatBuf(c); } }else{ //上一次不是空格、tab键、\n if( c == '\t' || c == '\n'){ //本次是 tab键、\n,就填充空格 fillFormatBuf(' '); }else{ fillFormatBuf(c); } } lastC=c; }while(srcIndexformatSQLLen) endPos=formatSQLLen; String nearErrorInSQL=new String(formatSQL,startPos,endPos-startPos); String msg=" #{} 表达式错误,位置在 "+formatSQLIndex+" ,在 【"+nearErrorInSQL+"】 附近"; return msg; } /** * 从 FormatSQL 取出一个字符,并执行 formatSQLIndex++ * @return */ private char getFromFormatSQL(){ char ret=formatSQL[formatSQLIndex]; formatSQLIndex++; return ret; } /** * 判断是否字母 * @param c * @return */ private boolean isLetter(char c){ return Character.isLetter( c ); } /** * 是否 空格、tab、换行符 \n * 注意:不支持 windows换行符 \r\n * @param c * @return */ private boolean isSpaceTabLine(char c){ if( c==' ' || c=='\t' || c == '\n'){ return true; }else{ return false; } } public void setChecked(boolean isChecked) { this.isChecked = isChecked; } /** * 解析sql,将含有 #{} 表达式的sql,将其中的字符串解析出,放入到keywords中,然后将 #{} 替换成 ? * @param sql * @param keywords * @return 返回 带有 ? 的sql */ public String parse(){ format(); if(isChecked){ check(); } int sqlPos=0; char[] sql=new char[formatSQLLen]; keywords=new ArrayList(); formatSQLIndex=0; char cur=0; int sqlStartPos=0; // 截取sql开始的位置 int tagStartPos=0; //#{的位置 int tagEndPos=0; // }的位置 int keywordStartPos=0; // #{} 中关键字开始的位置 int keywordEndPos=0; // #{} 中关键字结束的位置 do{ cur=getFromFormatSQL(); if( cur == '#'){ //遇到# cur=getFromFormatSQL(); if(cur=='{'){ //遇到 #{ tagStartPos=formatSQLIndex-2; // -2,是把 #{ 给减掉 int len=tagStartPos-sqlStartPos; System.arraycopy(formatSQL, sqlStartPos, sql, sqlPos, len); sqlPos=sqlPos+len; sql[sqlPos]=' '; sqlPos++; sql[sqlPos]='?'; sqlPos++; sql[sqlPos]=' '; sqlPos++; keywordStartPos=formatSQLIndex; // #{} 中关键字开始的位置 do{ cur=getFromFormatSQL(); if(cur=='}'){ keywordEndPos=formatSQLIndex-1; // #{} 中关键字结束的位置 tagEndPos=formatSQLIndex; sqlStartPos=formatSQLIndex; String keyword=new String(formatSQL,keywordStartPos,keywordEndPos-keywordStartPos); keywords.add(keyword); break; } }while(formatSQLIndex getKeywords() { return keywords; } public void setDebug(boolean debug) { this.debug = debug; } class TagSQLSyntaxException extends RuntimeException{ public TagSQLSyntaxException(String msg){ super(msg); } } } ``` # 测试 ``` import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; public class TestDbUtil3 { public void update() throws ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ DbUtilsTag s=new DbUtilsTag(); s.getConn("com.mysql.jdbc.Driver" , "jdbc:mysql://127.0.0.1:3308/scott?useSSL=false&useUnicode=true&characterEncoding=utf-8" ,"root" ,""); Map params=new HashMap<>(); params.put("sal", 2500); params.put("maxSal", 4000); List list=s.queryTag("select empno,ename,job,sal,comm,deptno from emp where #{sal} < sal and sal < #{maxSal}", params,Emp.class); // List list2=s.queryTag("select deptno,dname,loc from dept", null); s.close(); } public void query() throws ClassNotFoundException, SQLException{ DbUtilsTag s=new DbUtilsTag(); s.getConn("com.mysql.jdbc.Driver" , "jdbc:mysql://127.0.0.1:3308/scott?useSSL=false&useUnicode=true&characterEncoding=utf-8" ,"root" ,""); Map params=new HashMap<>(); params.put("ename", "李雷2"); params.put("job", "程序员2"); params.put("sal", "20000"); params.put("comm", "142"); params.put("empno", "10008"); int i=s.updateTag("update emp set ename=#{ename},job=#{job},sal=#{sal},comm=#{comm} where empno=#{empno}",params); // int i=s.update("update emp set ename='测试' where empno=10008 ",null); s.close(); } public static void main(String[] args) throws ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub } } ``` 原文出处:http://www.malaoshi.top/show_1IX2vQk7lbRe.html