如何利用mybatis的map集合传参来打造我们通用的增删改查

简单说明(所有代码、pom会在最后给出)

相信用过mybatis的各位应该知道各种mybatis的封装框架,比如tkmapper、mybatis-plus等,这些都是在原有的mybatsi框架下进行了进一步的封装,将这些简单的增删改查进行了封装,便于我们进行通用的操作,我这里呢给大家提供一种新思路,应该网上也有很多这种资料,不过,怎么进行封装,根据每个人的思维不同,实现方式也不同,这里利用mybatis的map传参的属性我们就可以进行很好的操作,来封装我们的通用接口,并且,通用接口只需要一次定义,不用继承,不用实现,直接调用即可实现通用功能,满足基本查询,分页,修改,删除等,我这里没有写通用新增方法,如果你看懂了思路,你也可以把这种加进去。下面进入正题吧。

1、map集合的注意事项

首先我们要先弄懂map集合,map集合其实就是key-value键值对的结构,key键不能重名,重名会把key键对应的value值进行覆盖,我们只需要弄懂这个,并且牢记这个即可,因为在sql中,有时候我们查询作为where条件后面可能出现重复的限制,比如age =5 or age = 0,这种,那么如果你用age来作为key,或者age=来作为key,都会对你的查询条件造成影响,这都是需要思考的。同时map中我们用的最多的可能就是hashmap了,但是hashmap有个致命弱点就是存储是无序的,也就是说,你对map添加值并不会是按你添加进map的顺序进行存储,这个对于sql来说肯定是不允许的,比如说sql中的order by排序,这种是有严格顺序要求的,所以这里我们要用的应该是linkhashmap,这样我们在读取时就是按照默认插入的顺序进行读取的,就会符合我们的预期。比如:

Map orderParams = new LinkedHashMap<>();
orderParams.put("statement_account_cargo_fee_difference", "desc");
orderParams.put("statement_account_management_fee_difference", "desc");
orderParams.put("statement_account_product_money", "desc");
orderParams.put("statement_account_market_money", "desc");

2、如何编写通用mapper方法

首先我默认大家已经把基本mybatis框架配置好了,可以连接数据库,可以写sql了,下面要做的就是如何编写。

1)规则制定

where条件制定(map参数):(以下是LinkHashMap的key和value的规则)

常规的写法:

key值:spliceType@columnName@whereSymbol 

value值:columnValue

常规的说明:

spliceType:条件拼接类型,比如and 或者 or

columnName:属性名称,也就是sql表对应的字段名称

whereSymbol :查询条件参数类型,比如:= < > != >= <=

columnValue:属性名称对应的值

一个key-value映射成的的语句就是,比如下面这几条:

and age = 16 ;or name = ‘小红’

如果遇到key值重复怎么办呢,原则是在key值后面加上空格字符串,来避免key键冲突,比如下面这样:

("and@age@=",15)
("or@age@= ",19)

这样就能避免key键值冲突,那么这个条件就可以映射成为:

and age = 15 or age = 19

特殊写法:

key值:"asm_null_"+size

value值:

1、leftLike(左边like查询条件)

"spliceType@columnName like concat('%','columnValue')";

2、rightLike(右边like查询条件)

“spliceType@columnName like concat('columnValue','%')”

3、leftAndRightLike(左右两边like查询条件)

“spliceType@columnName like concat('%','columnValue','%')”

4、in(in条件查询)

“spliceType@columnName in(columnValue)";

5、between(between查询条件)

“”spliceType@columnName between startValue and  endValue";

特殊说明:

key值说明:"asm_null_"固定开头,这里可以替换为任意字符串,size是一个增长不重复的数字,加起来就是这种结构:asm_null_0,asm_null_1等,作用就是保证key键值不冲突重复,保证针对以上like、in、between这几种情况的key键值不冲突,因为key没有意义,我们只是取value值作为条件

spliceType:条件拼接类型,比如and 或者 or

columnName:属性名称,也就是sql表对应的字段名称

columnValue:属性名称对应的值,in特殊,是以“1,2,3,4,5”这种结构存在

startValue :针对between 开始的参数值

endValue:针对between 结束的参数值

举例子:

("asm_null_0","and@name like concat('%','小明')")
("asm_null_1","and@age in(1,15,19)")
("asm_null_2","and@stuNumber between 20140701423 and 20140701490")

映射结果:

and name like concat('%','小明') and age in(1,15,19) and stuNumber between 20140701423 and 20140701490

代码实现(where条件映射)

java代码:

用到了commons.lang3、lombox、springframework,因为我用了其中的一些工具包,很简单工具包,即使看名字你也能明白

import com.xx.common.utils.ArrayUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.*;

/**该工具类new的对象就一个表示一次sql条件,不可以重复,你看whereColumn只是对象参数
 * 所以需要在构造完成一次查询条件之后,必须把whereColumn清空或者重新new一个,不然你
 * 再进行下一条sql执行的时候,之前的条件也会追加上,懂
 * 总结:每条sql对应一个单独的AutoSqlUtil对象,这样就不会在同一个方法中就不会出现条
 * 件紊乱,比如同一个方法中既要进行修改又要进行删除
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
public class AutoSqlUtil {
    /**
     * 查询条件参数类型
     */
    public final static String eq = "=";//等于
    public final static String ne = "!=";//不等于
    public final static String lt = "<";//小于
    public final static String gt = ">";//大于
    public final static String ltAndEq = "<=";//小于等于
    public final static String gtAndEq = ">=";//大于等于
    public final static String in = "in";//in
    public final static String leftLike = "left like";//左模糊查询
    public final static String rightLike = "right like";//右模糊查询
    public final static String leftAndRightLike = "leftAndRight like";//两边模糊查询
    public final static String between = "between";//范围查询

    /**
     * 条件拼接类型
     */
    public final static String and = "and";//and拼接条件
    public final static String or = "or";//or拼接条件

    /**
     * 拼装where条件
     */
    private Map whereColumn;

    /**
     * 对普通list集合进行链式构造
     */
    private List list;

    public static AutoSqlUtil create() {
        return new AutoSqlUtil();
    }

    /**
     * 对list集合进行封装,实现链式调用
     * @param object 向集合中加入的值
     * @return 本工具类
     */
    public AutoSqlUtil addList(Object object){
        if(CollectionUtils.isEmpty(this.list)){
            this.list = new ArrayList<>();
        }
        List list = this.list;
        list.add(object);
        return this;
    }

    /**
     * 得到构造好的between条件
     * @param start 起始值
     * @param end 结束值
     * @return 集合
     */
    public static List getBetweenAndList(Object start,Object end){
        if(ObjectUtils.isEmpty(start) || ObjectUtils.isEmpty(end)){
            return null;
        }
        ArrayList objects = new ArrayList<>();
        objects.add(0,start);
        objects.add(1,end);
        return objects;
    }

    /**
     * 追加where条件
     *
     * @param columnName  数据库字段名称
     * @param whereSymbol 查询条件符号 = like 等,如果whereSymbol条件是in、between,传入值columnValue必须是list集合,否则报错
     * @param columnValue 对应的查询条件字段值
     * @param spliceType 每个条件之间拼接类型,and or ,如果为空,默认以and拼接
     * @return 返回本工具对象,实现链式写法
     */
    public AutoSqlUtil addWhereColumn(String columnName, String whereSymbol, Object columnValue,String spliceType) {
        if (StringUtils.isBlank(columnName) || StringUtils.isBlank(whereSymbol) || ObjectUtils.isEmpty(columnValue)) {
            return this;
        }
        if(columnValue instanceof String && StringUtils.isBlank((String)columnValue)){
            return this;
        }
        if (ObjectUtils.isEmpty(this.whereColumn)) {
            this.whereColumn = new LinkedHashMap<>();
        }
        String spliceTypeCopy = "and";
        if(!StringUtils.isBlank(spliceType)){
            spliceTypeCopy = spliceType;
        }
        Map whereColumn = this.whereColumn;
        String value;
        if (AutoSqlUtil.leftLike.equals(whereSymbol)) {
            value = spliceTypeCopy+"@"+columnName + " like concat('%','" + columnValue + "')";
            whereColumn.put("asm_null_"+whereColumn.size(), value);
        } else if (AutoSqlUtil.rightLike.equals(whereSymbol)) {
            value = spliceTypeCopy+"@"+columnName + " like concat('" + columnValue + "','%')";
            whereColumn.put("asm_null_"+whereColumn.size(), value);
        } else if (AutoSqlUtil.leftAndRightLike.equals(whereSymbol)) {
            value = spliceTypeCopy+"@"+columnName + " like concat('%','" + columnValue + "','%')";
            whereColumn.put("asm_null_"+whereColumn.size(), value);
        } else if (AutoSqlUtil.in.equals(whereSymbol)) {
            String s = ArrayUtil.array2StrAutoSql((List) columnValue, ",");
            value = spliceTypeCopy+"@"+columnName + " in(" + s + ")";
            whereColumn.put("asm_null_"+whereColumn.size(), value);
        } else if (AutoSqlUtil.between.equals(whereSymbol)) {
            List objects = (List) columnValue;
            if(objects.get(0) instanceof String){
                value = spliceTypeCopy+"@"+columnName + " between '" + objects.get(0) + "' and '"+objects.get(1)+"'";
            }else {
                value = spliceTypeCopy+"@"+columnName + " between " + objects.get(0) + " and "+objects.get(1);
            }
            whereColumn.put("asm_null_"+whereColumn.size(), value);
        } else {
            String key =spliceTypeCopy+"@"+ columnName + "@" + whereSymbol;
            if(whereColumn.containsKey(key)){
                Integer t = 1;
                while (true){
                    key =  spliceTypeCopy+ addSpaceLeft("@",t)+ columnName + "@" + whereSymbol;
                    if(!whereColumn.containsKey(key)){
                        break;
                    }
                    t++;
                }
            }
            whereColumn.put(key, columnValue);
        }
        return this;
    }

    private String addSpaceLeft(String str,Integer count){
        if(ObjectUtils.isEmpty(count) || count <= 0){
            return  str;
        }
        StringBuilder strCopy = new StringBuilder(str);
        for(int i=0;i 
  

看一下代码,就是按照上面给出的规则进行代码构造的,直接这样写结构肯定比较繁琐呀,看的人也不一定能马上看懂,用方法来进行构造,addWhereColumn方法中会将在空值过滤掉,包括属性对应的值为空也会过滤,因为这比较符合条件,我是没有遇见什么情况下需要去查询某个属性为空的数据的情况的,如果你觉得不需要可以改造,我说了,这是提供一种思路,addWhereColumn方法中有个ArrayUtil.array2StrAutoSql,代码如下,sql中对于字符数据和其他数据要区分,字符的话必须加上‘’引号,数字的话不需要,还有时间直接可以用引号包起来作为字符串查询,比如:createTime > ‘2018-01-30 12:00:00’

    public static String array2StrAutoSql(List strings,String chart){
        StringBuilder strReturn = new StringBuilder();
        if(!CollectionUtils.isEmpty(strings)){
            int sign = 0;
            for(Object str:strings){
                if(sign == 0){
                    if(str instanceof String){
                        strReturn.append("'").append(str).append("'");
                    }else {
                        strReturn.append(str);
                    }
                }else {
                    if(str instanceof String){
                        strReturn.append(chart).append("'").append(str).append("'");
                    }else {
                        strReturn.append(chart).append(str);
                    }
                }
                sign ++ ;
            }
        }
        return strReturn.toString();
    } 
  

构造使用where条件:

//创建工具类实例
AutoSqlUtil autoSqlUtil = AutoSqlUtil.create();

//添加where条件并得到where条件map集合
Map whereColumn = autoSqlUtil.addWhereColumn("name", AutoSqlUtil.leftAndRightLike, "测试名称撒",null)
                .addWhereColumn("age", AutoSqlUtil.eq, 15,null)
                .addWhereColumn("id",AutoSqlUtil.in,autoSqlUtil.addList(15)
                        .addList(16)
                        .addList(22)
                        .getList() ,AutoSqlUtil.or)
                .addWhereColumn("hobbi",AutoSqlUtil.between,AutoSqlUtil.getBetweenAndList("打篮球","打羽毛球"),null)
                .getWhereColumn();

System.out.println(whereColumn);

打印出来的结果:

{asm_null_0=and@name like concat('%','测试名称撒','%'), and@age@==15, asm_null_2=or@id in(15,16,22), asm_null_3=and@hobbi between '打篮球' and '打羽毛球'}

mybitis的xml的解析:

比如,有个方法叫做:

/**
  * 按条件删除数据
  *
  * @param params 需要删除的数据条件
  * @return 结果
  */
public int deleteForParams(@Param("params") Map params);

需要指定parameterType为Map,看标签中如何解析map的呢,根据上面给出的key值和value值的规则,直接解析出来,解析成能够识别的sql语句,标签会把开头和末尾的and 或者 or直接去掉,逻辑应该都能看懂吧,通过@拆分,最后拼接成mybatis识别的构造,mybstis的#取值和$取值的区别是$是直接替换,#不是,#会进行预编译,也就是#的位置不会直接替换成字符串,而是预编译过替换,#会对字符串数据加上引号,而$不会,$说了是直接替换,是什么就替换成什么样

    
        delete from bs_polling_warn
        
            
                
                    
                        
                            
                                ${item}
                            
                            #{value}
                        
                        
                            
                                ${item}
                            
                        
                    
                
            
        
    

order by 条件制定(map参数):(以下是LinkHashMap的key和value的规则)

写法:

key值:columnName

value值:orderType

说明:

columnName:属性名称,也就是sql表对应的字段名称

orderType:排序类型,也就是desc、asc

举例子:

("age","desc")
("stuNumber","asc")

映射结果:

age desc,stuNumber asc

代码实现(order by条件映射)

java代码:

import com.xx.common.utils.ArrayUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.*;

/**该工具类new的对象就一个表示一次sql的条件,不可以重复,你看orderColumn只是对象参数
 * 所以需要在构造完成一次查询条件之后,必须把orderColumn清空或者重新new一个,不然你
 * 再进行下一条sql执行的时候,之前的条件也会追加上,懂
 * 总结:每条sql对应一个单独的AutoSqlUtil对象,这样就不会在同一个方法中就不会出现条
 * 件紊乱,比如同一个方法中既要进行两次查询不同的表
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
public class AutoSqlUtil {

    /**
     * 排序参数类型
     */
    public final static String desc = "desc";//降序排序
    public final static String asc = "asc";//升序排序

    /**
     * 拼装排序条件
     */
    private Map orderColumn;

    /** 追加排序条件
     *
     * @param columnName 数据库中字段名称
     * @param orderType  排序的类型,asc和desc
     * @return 本工具类,实现链式写法
     */
    public AutoSqlUtil addOrderColumn(String columnName, String orderType) {
        if (StringUtils.isBlank(columnName) || StringUtils.isBlank(orderType)) {
            return this;
        }
        if (ObjectUtils.isEmpty(this.orderColumn)) {
            this.orderColumn = new LinkedHashMap<>();
        }
        Map orderColumn = this.orderColumn;
        orderColumn.put(columnName, orderType);
        return this;
    }


}

构造order by 条件:

//创建工具类实例
AutoSqlUtil autoSqlUtil = AutoSqlUtil.create();

//添加并得到排序条件map集合
Map orderColumn = autoSqlUtil.addOrderColumn("name", AutoSqlUtil.desc)
                .addOrderColumn("age", AutoSqlUtil.asc)
                .getOrderColumn();

System.out.println(orderColumn);

打印出来的结果:

{name=desc, age=asc}

mybitis的xml的解析:

比如,有个方法叫做:

/**
    *  通过条件查询出相应数据 可通过AutoSqlUtil构造条件
    * @param whereParams where条件查询map集合
    * @param orderParams 排序条件map集合
    * @param limitStr 限制条件语句字符串
    * @return  查询到集合
    */
public List selectAutoParams(@Param("whereParams") Map whereParams, @Param("orderParams") Map orderParams, @Param("limitStr") String limitStr);

需要指定parameterType为Map,看orderParams处如何解析map的呢,根据上面给出的key值和value值的规则,直接解析出来,解析成能够识别的sql语句,只需要关注orderParams处的解析即可,这里讲的是排序的解析

    

limit限制条件制定(String参数):

这个就比较简单了啊,限制条件格式:limit 0,100 这种

java代码:

import com.xx.common.utils.ArrayUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.*;

@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
public class AutoSqlUtil {

    /**
     * 得到limit限制条件语句
     *
     * @param pageIndex 起始页的页码(最小为1,也就是第一页)
     * @param rows      每页需要查询的数据条数
     * @return limit 限制语句
     */
    public String getLimitColumn(Long pageIndex, Long rows) {
        if (ObjectUtils.isEmpty(pageIndex) || ObjectUtils.isEmpty(rows)) {
            return null;
        }
        //计算数据获取的起始位置
        Long index = (pageIndex - 1) * rows;
        String limitStr = index + "," + rows;
        return limitStr;
    }

}

limit限制条件构造:

//得到limit限制条件语句
String limitColumn = autoSqlUtil.getLimitColumn(1L, 6L);
System.out.println(limitColumn);

打印结果显示:

0,6

mybitis的xml的解析:

比如,有个方法叫做selectAutoParams,就是上面的例子:

/**
    *  通过条件查询出相应数据 可通过AutoSqlUtil构造条件
    * @param whereParams where条件查询map集合
    * @param orderParams 排序条件map集合
    * @param limitStr 限制条件语句字符串
    * @return  查询到集合
    */
public List selectAutoParams(@Param("whereParams") Map whereParams, @Param("orderParams") Map orderParams, @Param("limitStr") String limitStr);

可以看出这里就是将限制条件传进来做了替换而已,现在这里讲的是限制条件,所以只需要看limit限制条件,也就是limitStr那个位置

    

update set 字段制定(map参数):(以下是LinkHashMap的key和value的规则)

写法:

key值:columnName

value值:columnValue

说明:

columnName:属性名称,也就是sql表对应的字段名称

columnValue:属性对应的值,也就是要修改的值

举例子:

("age",19)
("stuNumber",2014070213)
映射结果:
 
age = 19,stuNumber = 2014070213

代码实现(update字段映射)

java代码:

import com.xx.common.utils.ArrayUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.*;

@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
public class AutoSqlUtil {

    /**
     * 需要修改的数据库的字段信息集合
     */
    private Map upDbData;

    /**
     * 添加需要修改的字段信息
     * @param columnName 数据库字段名称
     * @param columnValue 数据库字段值
     * @return 本工具类,实现链式调用
     */
    public AutoSqlUtil addUpdateDbData(String columnName, Object columnValue){
        if (StringUtils.isBlank(columnName) || ObjectUtils.isEmpty(columnValue)) {
            return this;
        }
        if (ObjectUtils.isEmpty(this.upDbData)) {
            this.upDbData = new LinkedHashMap<>();
        }
        Map upDbData = this.upDbData;
        upDbData.put(columnName, columnValue);
        return this;
    }

}

update待修改字段值构造:

//创建工具类实例
AutoSqlUtil autoSqlUtil = AutoSqlUtil.create();

//添加需要修改的字段值并得到map集合
Map upDbData = autoSqlUtil.addUpdateDbData("name", "张三")
                .addUpdateDbData("age", 19)
                .getUpDbData();

System.out.println(upDbData);

打印结果:

{name=张三, age=19}

mybitis的xml的解析:

比如,有个方法叫做updateForParams:

/**
  * 按条件修改相应数据的值
  * @param columns 需要改变的数据库字段及其对应的值的map集合
  * @param params 查询where条件的map集合
  * @return 影响行数
  */
public int updateForParams(@Param("columns") Map columns, @Param("params") Map params);

需要指定parameterType为Map,看SET处如何解析map的呢,根据上面给出的key值和value值的规则,直接解析出来,解析成能够识别的sql语句,只需要关注SET处的解析即可,这里讲的是SET的解析

    
        update bs_polling_warn
        
            
                
                    ${key}=#{value}
                
            
        
        
            
                
                    
                        
                            
                                ${item}
                            
                            #{value}
                        
                        
                            
                                ${item}
                            
                        
                    
                
            
        
    

2)思考

根据上面我举的例子,应该有了一个概念,那就是根据sql的特点,将sql根据关键字进行拆分,拆分为各个条件,通过在mybatis.xml中对各个条件进行拼接就可以实现通用的写法,上面已经列出了where、order by 、limit  、update set ,如果你想更加通用,你也可以加上分组group by条件,have 、select 嵌入、表名嵌入等等,这些都可以去实现,然后集成在同一个工具类中,再在xml中去提供一些方法等等,因为我之前的项目在运用这个通用工具类的时候还同时用了代码自动生成,所以,在xml中你可以看到并没有这么通用,这些通用方法直接都是通过模板生成的,比如下面这样的:





    
        
        
        
        
        
        
        

        
        
        
    

    
        select polling_warn_id, task_time, u.place_id as place_id, accept_person_id, group_id, polling_task_id,
        u.create_time as create_time,
        k.parent_ids as place_parent_ids,place_name,place_names
        from bs_polling_warn u
        left join bs_place k on u.place_id = k.place_id
    

    

    


可以看到result返回字段映射和form条件和表明都还是根据每个不同表再写出来的,只有上面我列出来的通用条件构造做了通用的设计,这是因为整个是代码自动生成模板生成的,用过代码自生成都知道,在Java代码中配置代码自动生成会根据你的代码模板生成xml、java类这些文件,所以,之前结合这个,就没有做全通用设计,不过,要做这个也很简单的,只需要把需要的在做一遍就可以了呀,每个拆分出来的条件,比如上面说的where、order by、limit 、update set ,代码都是一样的,所以就可以用mybatis的标签分别包起来,然后再你的各个不同的方法中去引入就可以了,比如,下面这样:

/**
  * 通过条件查询出满足条件的数据条数 可通过AutoSqlUtil构造条件
  * @param tableName 表名
  * @param whereParams where条件查询map集合
  * @param orderParams 排序条件map集合
  * @param limitStr 限制条件语句字符串
  * @return 查询到的数据条数
  */
public Long findAutoCount(@Param("tableName") String tableName,@Param("whereParams") Map whereParams, @Param("orderParams") Map orderParams, @Param("limitStr") String limitStr);




    

    
        
            
                
                    
                        
                            
                                
                                    ${item}
                                
                            
                            #{value}
                        
                        
                            
                                ${item}
                            
                        
                    
                
            
        
    

    
        
            order by
            
                
                    ${key} ${value}
                
            
        
    

    
        
            limit ${limitStr}
        
    

所以,这个思路就是这个,完整的代码呢,我觉得之前的不太通用,等我再写个通用的demo,再贴在这后面,先发布吧!!!

 

-------------------等待后续--------------------

 

 

你可能感兴趣的:(mybatis框架,mybatis,mybatis中的map传参,通用mapper,mybatis通用方法)