mybatis字符#與字符$的區別
問題:使用in查詢查詢出一批數據,in查詢的參數是字符串拼接的。調試過程中,把mybatis輸出的sql復制到navicat中,在控制台將sql的參數也復制出來,替換到sql的字符 '?' 的位置,執行sql,能查詢到數據,但是java程序無法查詢到數據。
原因:因為mybatis的參數占位符以#字符開頭的參數,在處理過程中會自動給參數加引號,及一些字符過濾處理(例如防止sql注入等等)
解決方式: in查詢的參數占位符換成字符$開頭,因為mybatis在處理 $ 開頭的參數占位符時候不會給參數加引號及其他額外處理(例如sql注入字符過濾),使用的是參數原值。
mybatis 參數查詢修改前片段
AND a.source in (#{source})
mybatis 參數查詢修改后片段
AND a.source in (${source})
其他: 因為mybatis的參數占位符以#字符開頭的參數,在處理過程中會自動給參數加引號,那么是加單引號還是雙引號呢?
實際上mybatis對這個#開頭的參數參數進行了參數化處理,防止注入。
如果參數拼接 top_news','net_music','knowledge_sns','wb ,如果#開頭參數處理是單純加單引號,那么sql條件語句如果變成如下這樣,是可以查詢到數據的,但是結果是沒有查詢到數據,說明,時間拼接的數據不是如下的結果,結果是#開頭的參數不是單純的加單引號處理
AND a.source in ('top_news','net_music','knowledge_sns','wb')
如果參數拼接使用雙引號拼接如下 top_news","net_music","knowledge_sns","wb ,如果#開頭參數處理是單純加雙引號,那么sql條件語句如果變成如下這樣,是可以查詢到數據的,但是結果是沒有查詢到數據,說明,實際拼接的數據不是如下的結果,結果是#開頭的參數不是單純的加雙引號處理
AND a.source in ("top_news","net_music","knowledge_sns","wb")
至於#開頭的參數到底怎么處理的,既不是單純加單引號也不是單純加雙引號,具體做了哪些處理,請閱讀源碼。反正mybatis進行in查詢時,參數是拼接好的字符串的時候,參數占位符使用$,而不使用#,在使用$做參數占位符時候,給參數賦值前確保代碼里做了防注入處理或者已知的代碼是安全的不存在sql注入的,可以直接使用$作為參數占位符。
mybatis xml 文件(修改前),in查詢參數使用占位符使用字符#
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.thinkgem.jeesite.modules.backend.dao.ChannelDao"> <sql id="channelColumns"> a.id AS "id", a.name AS "name", a.source AS "source", a.url AS "url", a.create_by AS "createBy.id", a.update_by AS "updateBy.id", a.update_date AS "updateDate", a.create_date AS "createDate", a.remarks AS "remarks", a.del_flag AS "delFlag" </sql> <sql id="channelJoins"> </sql> <select id="findList" resultType="Channel"> SELECT <include refid="channelColumns"/> FROM mkt_channel a <include refid="channelJoins"/> <where> a.del_flag = #{DEL_FLAG_NORMAL} <if test="source != null and source!=''"> AND a.source in (#{source}) </if> <if test="createBy != null"> AND a.create_by = #{createBy.id} </if> </where> <choose> <when test="page !=null and page.orderBy != null and page.orderBy != ''"> ORDER BY ${page.orderBy} </when> <otherwise> ORDER BY a.update_date DESC </otherwise> </choose> </select> </mapper>
ChannelController.java
@Controller @RequestMapping(value = "${a}/backend/channel") public class ChannelController extends BaseController { @Autowired private ChannelService channelService; @Autowired private SystemService systemService; @RequiresPermissions("backend:channel:view") @RequestMapping(value = {"list", ""}) public String list(Channel channel, HttpServletRequest request, HttpServletResponse response, Model model) { String sourceName= request.getParameter("sourceName"); String srcStr = null; boolean srcFindDo = false; if(StringUtils.isNotEmpty(sourceName)) { Map<String, Object> findedChannelMap = SourceUtils.getInstance().searchSourceList(sourceName); srcStr = (String) findedChannelMap.get("srcStr"); srcFindDo = true; } String createBy = request.getParameter("createBy"); channel.setCreateBy(null); createBy = XssFilter.getInstance().cleanXssChars(createBy); if(StringUtils.isNotEmpty(createBy)) { User user = systemService.getUserById(createBy); if(null != user) { channel.setCreateBy(user); } } Page<Channel> page = new Page<>(); if(srcFindDo && StringUtils.isEmpty(srcStr)){ page.setList(new ArrayList<>()); page.setPageNo(0); page.setPageSize(0); page.setCount(0); page.setMessage("沒有找到數據"); } else { channel.setUtmSource(labelStr); page = channelService.findPage(new Page<Channel>(request, response), channel); } model.addAttribute("page", page); return "modules/backend/channelList"; } }
SourceUtils.java
package com.thinkgem.jeesite.common.utils; import com.thinkgem.jeesite.modules.backend.entity.ChannelSource; import com.thinkgem.jeesite.modules.sys.entity.Dict; import com.thinkgem.jeesite.modules.sys.utils.DictUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.*; public class SourceUtils { private Logger logger = LogManager.getLogger(SourceUtils.class); private ChannelSourceFacade channelSourceFacade; private static SourceUtils instance; private SourceUtils() { channelSourceFacade = SpringContextHolder.getBean(ChannelSourceFacade.class); } public static SourceUtils getInstance() { if (null == instance) { instance = new SourceUtils(); } return instance; } /** * 獲取所有的來源列表 * @return */ public List<ChannelSource> getAllSource() { List<ChannelSource> dataList = new ArrayList<>(); Response<List<ChannelSource>> response = channelSourceFacade.getChannelSourceList(); dataList = response.getData(); return dataList; } /** * 根據來源名稱模糊查找渠道 * @return */ public Map<String,Object> searchChannelList(String label,String desc,String code) { Map<String,Object> dictMap = new HashMap<>(); String labelStr = ""; List<ChannelSource> findedList = new ArrayList<>(); List<ChannelSource> srcList = getAllChannelDict(); if(null != srcList && srcList.size() > 0) { for (ChannelSource item : srcList) { if (dictMap.containsKey(item.getLabel())) { continue; } if(channelMatch(name,item)) { findedList.add(item); srcStr = String.format("%s'%s',",srcStr,item.getLabel()); } } } if(srcStr.length() > 1) { //移除最后一個逗號和2個引號 srcStr = srcStr.substring(0, srcStr.length() - 1); } dictMap.put("srcStr",srcStr); dictMap.put("findedList",findedList); return dictMap; } private boolean channelMatch(String sourceName,Dict item) { boolean result = true; name = null == name ? "" : name; if (StringUtils.isNotEmpty(name)) { if (dict.getDescription().indexOf(desc) == -1) { result = false; return result; } } return result; } }