JfinalUIB 代码笔记 (4)--- 高仿ibatis(mybatis)实现sql的集中管理

实现sql的集中管理,简单的把一些固定长度的sql移植进xml很简单,这没有什么好多说的,关键问题是我们平时处理的sql,有大量是动态长度的,比如说最常见的就是多条件的分页查询,往往我们会在代码中写大量的if else,想把这些移植进xml就比较困难了,完全仿制ibatis来做xml标签工作量太大,最省事的处理方法就是能不能直接把Java代码的逻辑处理方式移植进xml,然后对逻辑代码进行解析,绕开那一大堆的xml标签定义,下面就是jfinaluib中的处理方式:

1.0 暂时还是用的拼接,没有预处理,难点就是不是做到解析sql中表字段的类型对应的Java数据类型

<?xml version="1.0" encoding="UTF-8"?>
<sql-statement namespace="pingtai.user"> 
<!-- 动态SQL处理 -->
<sql id="splitPage">
  <![CDATA[ 
         from pt_user u 
   left join pt_userinfo ui on u.userinfoids = ui.ids 
   left join pt_department d on u.departmentids = d.ids 
   where 1=1 
   <% if(!isEmpty(userClass)){ %>
     and u.userClass = '#userClass#'
   <% } %>
   
   <% if(!isEmpty(userName)){ %>
     and u.userName like '%#userName#%'
   <% } %>
   
   <% if(!isEmpty(names)){ %>
     and ui.names like '%#names#%'
   <% } %>
     ]]>
</sql>
</sql-statement>

下面是调用xml中sql进行解析的方法,语法解析是用的beetl模板进行的,当然你可以换成freemark或者velocity也行,语法按他们的来就行

    /**
     * 获取SQL,动态SQL
     * @param sqlId
     * @param param
     * @return
     */
    public static String getSql(String sqlId, Map<String, Object> param) {
     String sqlTemplete = sqlMap.get(sqlId);
     if(null == sqlTemplete || sqlTemplete.isEmpty()){
   log.error("sql语句不存在:sql id是" + sqlId);
     }
     String sql = BeetlKit.render(sqlTemplete, param);
  
  Set<String> keySet = param.keySet();
  for (String key : keySet) {
   String value = (String) param.get(key);
     sql = sql.replace("#" + key + "#", value);
  }
  
        return sql.trim();
    }

下面是调用方式

 Map<String, Object> param = new HashMap<String, Object>();
  param.put("userClass", "0");
  param.put("userName", "huajian");
  String sql = ToolSqlXml.getSql("pingtai.user.splitPage", param);

输出sql:

from pt_user u
   left join pt_userinfo ui on u.userinfoids = ui.ids
   left join pt_department d on u.departmentids = d.ids
   where 1=1 and u.userClass = '0' and u.userName like '%huajian%'

这里存在sql注入的问题:只能用不友好的处理方式来处理,字符串的验证和过滤了,可以查看ToolSqlXml中的stringVali方法

2.0 正在测试中的方法,由于很难得到sql中字典的数据类型,所以做预处理存在难度,但是下面的方法几乎已经验证是可行的,能做到预处理方法,

此方法存在唯一不足的是对整形的处理,直接用的拼接,然后字符串使用预处理,不用担心存在sql注入,因为中间对整形的数据值做了数据类型转换的测试,

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.beetl.core.BeetlKit;
public class Sql {
 public static void main(String[] args) {
  
  String sqlTemplete = "from pt_user u left join pt_userinfo ui on u.userinfoids = ui.ids left join pt_department d on u.departmentids = d.ids " +
  " where 1=1 " +
  "<% if(!isEmpty(userClass)){ %>"+
  "  and u.userClass = #'$userClass$'# "+
  "<% } %>"+
  
  "<% if(!isEmpty(userName)){ %>"+
  "  and u.userName like #'%$userName$%'# "+
  "<% } %>"+
  
  "<% if(!isEmpty(names)){ %>"+
  "  and ui.names like #'%$names$%'# "+
  "<% } %>"+ 
  
  "<% if(!isEmpty(status)){ %>"+
  "  and ui.status = #$status$# "+
  "<% } %>";
  Map<String, Object> paramMap = new HashMap<String, Object>();
  paramMap.put("userClass", "0");
  paramMap.put("userName", "aa");
  paramMap.put("names", "bb");
  paramMap.put("status", "1");
  
  String sql = BeetlKit.render(sqlTemplete, paramMap);
  
  Pattern pattern = Pattern.compile("#[\\w\\d\\$\\'\\%\\_]+#"); //#[\\w\\d]+#    \\$
  Pattern pattern2 = Pattern.compile("\\$[\\w\\d\\_]+\\$"); //#[\\w\\d]+#    \\$
  
  Matcher matcher = pattern.matcher(sql);
  
  List<String> listPram = new LinkedList<>();
  
  while (matcher.find()) {
   String clounm = matcher.group(0); // #'%$names$%'#
   
   Matcher matcher2 = pattern2.matcher(clounm);
   matcher2.find();
   String clounm2 = matcher2.group(0); // $names$
   
   if(clounm.equals("#"+clounm2+"#")){//整形
    String clounm3 = clounm2.replace("$", "");
    try {
     String val = (String) paramMap.get(clounm3);
     Integer.parseInt(val);
     sql = sql.replace(clounm, val);
     //listPram.add(val);
    } catch (NumberFormatException e) {
     throw new RuntimeException("查询参数值异常");
    }
   }else{//字符串
    String clounm3 = clounm2.replace("$", "");
    String val = (String) paramMap.get(clounm3);
    
    String clounm4 = clounm.replace("#", "").replace("'", "").replace(clounm2, val);
    listPram.add(clounm4);
    
    sql = sql.replace(clounm, "?");
   }
  }
  
  System.out.println("预处理sql:" + sql);
  
  for (String param : listPram) {
   System.out.println("预处理sql参数:" + param);
  }
 }
}

最后输出的结果是:

预处理sql:from pt_user u left join pt_userinfo ui on u.userinfoids = ui.ids left join pt_department d on u.departmentids = d.ids  where 1=1   and u.userClass = ?   and u.userName like ?   and ui.names like ?   and ui.status = 1
预处理sql参数:0
预处理sql参数:%aa%
预处理sql参数:%bb%

此方法存在的不足就是对整形的数据没做预处理,就是ui.status = 1,但是通过#$status$#能确定他是整形,却不知道到底是int、long、bigdecimal

userClass是char

userName是varchar

names是varchar

JfinalUIB中的实现类 ToolSqlXml.java

你可能感兴趣的:(动态sql,预处理,jFinal)