Mybatis动态SQL生成

当前环境下,mybatis是使用很频繁的一个数据持久层框架。我们很多时候使用xml的方式来配置mybatis的sql,这种方式也称之为mybatis的动态SQL。but,本篇要说的是另一种方式。日常业务中我们可能会遇到很多有关于动态SQL的问题。我们就需要在代码中来编写SQL。

这个时候有的人可能就会想到直接 String sql = "select * from XXX where XXX";对于这种方式我是不推荐的,1、既然是代码,就要有代码的易读性。2、既然是动态sql就要有足够的扩展性。。。。

进入正题:mybatis针对上述问题,提供了一套API来解决我们这个尴尬的窘态。

3.2版本之前:

  SqlBuilder 和 SelectBuilder

 用法:

public String selectBlogsSql() {
  BEGIN(); // Clears ThreadLocal variable
  SELECT("*");
  FROM("BLOG");
  return SQL();
}
private String selectPersonSql() {
  BEGIN(); // Clears ThreadLocal variable
  SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
  SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
  FROM("PERSON P");
  FROM("ACCOUNT A");
  INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
  INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
  WHERE("P.ID = A.ID");
  WHERE("P.FIRST_NAME like ?");
  OR();
  WHERE("P.LAST_NAME like ?");
  GROUP_BY("P.ID");
  HAVING("P.LAST_NAME like ?");
  OR();
  HAVING("P.FIRST_NAME like ?");
  ORDER_BY("P.ID");
  ORDER_BY("P.FULL_NAME");
  return SQL();
}
  

具体相关文档:http://www.mybatis.org/mybatis-3/zh/statement-builders.html

在3.2版本之前,通过实现ThreadLocal变量来掩盖一些导致Java DSL麻烦的语言限制。但这种方式已经废弃了,现代的框架都欢迎人们使用构建器类型和匿名内部类的想法。因此,SelectBuilder 和 SqlBuilder 类都被废弃了。

3.2版本之后:

  实例:

private String selectPersonSql() {
  return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
    SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
    FROM("PERSON P");
    FROM("ACCOUNT A");
    INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
    INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
    WHERE("P.ID = A.ID");
    WHERE("P.FIRST_NAME like ?");
    OR();
    WHERE("P.LAST_NAME like ?");
    GROUP_BY("P.ID");
    HAVING("P.LAST_NAME like ?");
    OR();
    HAVING("P.FIRST_NAME like ?");
    ORDER_BY("P.ID");
    ORDER_BY("P.FULL_NAME");
  }}.toString();
}

————————————————————————————————————————————————————————————————————————————————————————

当然上述都是这些Mybatis本身提供的一些API,要达到动态SQL,我们还差一步,所以来个简单的demo。

public class MybatisSQLTemplate {

    /**
     * 插入数据
     * @param params
     * @return
     */
    public String insert(Map<String,Object> params){
        return new SQL()
                .INSERT_INTO(getTableName(params))
                .VALUES(buildFiled(params),buildValue(params))
                .toString();
    }

    /**
     * 更新数据
     * @param params
     * @return
     */
    public String update(Map<String,Object> params){
        return new SQL()
                .UPDATE(getTableName(params))
                .SET(buildSet(params))
                .WHERE(buildWhere(params))
                .toString();
    }

    /**
     * 获取表名
     * @param params
     * @return
     */
    private String getTableName(Map<String,Object> params){
        if(params == null || params.get(Condition.TABLE_NAME.name()) == null){
            throw new IllegalArgumentException("Table name is required..");
        }else {
            return params.get(Condition.TABLE_NAME.name()).toString();
        }
    }

    /**
     * 构建插入字段
     * @param params
     * @return
     */
    private String buildFiled(Map<String,Object> params){
        params.remove(Condition.TABLE_NAME.name());
        if(params.isEmpty()){
            throw new IllegalArgumentException("Parameter name is required..");
        }
        String columns = params.keySet().toString();
        return columns.substring(1,columns.length()-1);
    }

    /**
     * 构建插入value值
     * @param params
     * @return
     */
    private String buildValue(Map<String,Object> params){
        params.remove(Condition.TABLE_NAME.name());
        if(params.isEmpty()){
            throw new IllegalArgumentException("Parameter value is required..");
        }
        String columns = params.keySet().stream().map(i->"#{"+i+"}").collect(Collectors.toList()).toString();
        return columns.substring(1,columns.length()-1);
    }

    /**
     * 构建更新set数据
     * @param params
     * @return
     */
    private String buildSet(Map<String,Object> params){
        params.remove(Condition.TABLE_NAME.name());
        if(params.isEmpty()){
            throw new IllegalArgumentException("The value to be modified cannot be empty..");
        }
        Object temp = params.remove(Condition.WHERE.name());
        String sets = params.keySet().stream().map(i -> i + "=#{" + i + "}").collect(Collectors.toList()).toString();
        params.put(Condition.WHERE.name(),temp);
        return sets.substring(1,sets.length()-1);
    }


    /**
     * 构建where条件(目前只支持单个条件)
     * @param params
     * @return
     */
    private String buildWhere(Map<String,Object> params){
        params.remove(Condition.TABLE_NAME.name());
        if(params.isEmpty() || params.get(Condition.WHERE.name()) == null){
            throw new IllegalArgumentException("Missing where condition..");
        }
        Object where = params.get(Condition.WHERE.name());
        //可以做多类型判断以兼容更复杂where条件
        if(where instanceof String){
            String temp = (String) where;
            return temp + "= #{" + where +"}";
        }else {
            throw new IllegalArgumentException("Where argument is illegal..");
        }
    }
}

说明一下,上述的代码中使用的参数都是Map。一是为了测试方便,而是降低复杂度。当然你也可以传入自己的db对象。通过反射机制来读取属性,或者通过自定义注解来使整个代码结构更合理。

接下来说明一下,如何使用。

首先了解一下注解@SelectProvider、@InsertProvider、@UpdateProvide、@DeleteProvider,这四个注解分别用于查、插、改、删,都拥有一个type属性以及一个method属性。type属性用来指明生成SQL的类是那个,method用来表明该类下的那个方法。

public interface OrderInfoMapper {
@InsertProvider(type = MybatisSQLTemplate.class,method = "insert")
int insert(Map<String,Object> params);

@UpdateProvider(type = MybatisSQLTemplate.class,method = "update")
int update(Map<String,Object> params);
}

到这里动态SQL就算完成了。这里的Mapper与XML方式对应的Mapper无异。

写在后面:当然MyBatis不仅提供了API方式的SQL,结果集参数等的映射都是可以做到的。而且如果想要自定义Mybatis的一些行为,可以通过编写插件的方式来实现 @Intercepts。

2018-11-08 记

猜你喜欢

转载自www.cnblogs.com/funmans/p/9934353.html
今日推荐