本文主要是介绍模拟mybatis-plus使用lambda多表联查,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
说明:此代码的封装并不适用于mybatis,只是模拟了mybatis-plus的LambdaQueryWrapper进行多表联查,最终生成的sql采用?号占位符,占位符的值封装在一个List<Object>中,可适用于jdbcTemplate。
如果使用过mybatis-plus的LambdaQueryWrapper进行过查询的话,LambdaSqlBuilder的条件查询、排序、分组使用是保持一致的。
AbstractSqlBuilder是LambdaSqlBuilder 和StringSqlBuilder 的抽象父类,主要包含一些公共成员变量,and嵌套和or嵌套,嵌套用法和LambdaQueryWrapper一致
package com.zl.model.sql;import lombok.Getter;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;/*** @author zhangliang* @date 2020/7/7.*/
public abstract class AbstractSqlBuilder<C extends AbstractSqlBuilder> {/*** 占位符*/final C typedThis = ( C ) this;static final String OR = " or ";static final String AND = " and ";String connect = AND;boolean isConnect = true;static final String ASC = "asc";static final String DESC = "desc";@Getterprotected List<Object> params = new ArrayList<>();StringBuilder sqlBd = new StringBuilder();boolean orderBy = false;boolean groupBy = false;AbstractSqlBuilder() {}public C where() {sqlBd.append(" where 1=1");return typedThis;}public abstract String sql();String in(Collection<?> list){StringBuilder builder = new StringBuilder();int size = list.size();for (int i = 0; i < size; i++) {builder.append("?,");}String str = builder.toString();return str.substring(0,str.length()-1);}public C and(Consumer<C> consumer) {isConnect = false;sqlBd.append(" and (");consumer.accept(typedThis);sqlBd.append(")");isConnect = true;return typedThis;}public C or() {connect = OR;return typedThis;}public C or(Consumer<C> consumer) {isConnect = false;sqlBd.append(" or (");consumer.accept(typedThis);sqlBd.append(")");isConnect = true;return typedThis;}public C appendSql(String sql) {sqlBd.append(sql);return typedThis;}public C unionAll(String sql) {sqlBd.append(" union all ");sqlBd.append(sql);return typedThis;}public C having(String havingSql) {sqlBd.append(" having ");sqlBd.append(havingSql);return typedThis;}
}
LambdaSqlBuilder 说明:
- select 方法如果是同一个表的字段,可以写到一个select方法中,如果是不同表的字段,则需要再次调用select方法传入另一个表的字段,以此类推
- selectAlias 该方法的第一个参数是要查询的字段,第二个参数是别名,多个表有字段一样的才需要起别名。默认将下划线表列转换为驼峰式的类字段名
- from 方法默认将类名的首字母小写的驼峰表达式转为下划线作为表名,如果不符合此规则,则可调用带有表名字符串的from方法。别名默认为:表名_0,也可以传入表别名
- 表之间连接和from表相同
- 表字段是根据SqlFunction获取到lambda表达式所表示的字段的驼峰表达式转下划线获取到(其它不支持)
- 排序和分组对于不同表字段也需要多次调用,和select一致
package com.zl.model.sql;import org.apache.commons.lang3.StringUtils;import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @author zhangliang* @date 2020/7/7.*/
public class LambdaSqlBuilder extends AbstractSqlBuilder<LambdaSqlBuilder> {/*** key class name,value alias*/private final Map<Class<?>, String> tableAliasMap = new HashMap<>(16);private final Map<Class<?>, String> tableMap = new HashMap<>(16);private final Map<String, String> tableClassNameAliasMap = new HashMap<>(16);private final Map<String, String> propertyAliasMap = new HashMap<>(16);private final List<SqlFunction<?,?>> selected = new ArrayList<>();private static Pattern humpPattern = Pattern.compile("[A-Z]");@SafeVarargspublic final <T,R> LambdaSqlBuilder select(SqlFunction<T, R> ...columns){selected.addAll(Arrays.asList(columns));return this;}public <T,R,K,V>LambdaSqlBuilder selectAlias(SqlFunction<T,R> column,SqlFunction<K,V> alias){selected.add(column);String property = lambdaProperty(column);String className = lambdaClassName(column);String propertyAlias = lambdaProperty(alias);propertyAliasMap.put(className+"_"+property,propertyAlias);return this;}public LambdaSqlBuilder from(Class<?> table){return from(table,null);}public LambdaSqlBuilder from(Class<?> table,String alias){from(null,alias,table);return this;}public LambdaSqlBuilder from(String table,String alias,Class<?> tableClass){analysisTable(tableClass,alias,table);sqlBd.append(" from ");sqlBd.append(tableMap.get(tableClass));sqlBd.append(" ");sqlBd.append(tableAliasMap.get(tableClass));return this;}public LambdaSqlBuilder leftJoin(Class<?> table){return leftJoin(table,null);}public LambdaSqlBuilder leftJoin(Class<?> table,String alias){leftJoin(null,alias,table);return this;}public LambdaSqlBuilder leftJoin(String table,String alias,Class<?> tableClass){join(table,tableClass,alias,"left join");return this;}public LambdaSqlBuilder rightJoin(Class<?> table){return rightJoin(table,null);}public LambdaSqlBuilder rightJoin(Class<?> table,String alias){rightJoin(null,alias,table);return this;}public LambdaSqlBuilder rightJoin(String table,String alias,Class<?> tableClass){join(table,tableClass,alias,"right join");return this;}public LambdaSqlBuilder innerJoin(Class<?> table){return rightJoin(table,null);}public LambdaSqlBuilder innerJoin(Class<?> table,String alias){innerJoin(null,alias,table);return this;}public LambdaSqlBuilder innerJoin(String table,String alias,Class<?> tableClass){join(table,tableClass,alias,"inner join");return this;}private void join(String table,Class<?> tableClass,String alias,String joinType){analysisTable(tableClass,alias,table);sqlBd.append(" ");sqlBd.append(joinType);sqlBd.append(" ");sqlBd.append(tableMap.get(tableClass));sqlBd.append(" ");sqlBd.append(tableAliasMap.get(tableClass));}public <T,R,K,V>LambdaSqlBuilder on(SqlFunction<T,R> column0,SqlFunction<K,V> column1){sqlBd.append(" on ");column(column0);sqlBd.append(" = ");column(column1);return this;}public <T, R> LambdaSqlBuilder eq(SqlFunction<T, R> column, Object value) {appendWhereCondition(SymbolEnum.EQ,column,value);return this;}public <T, R> LambdaSqlBuilder ne(SqlFunction<T, R> column, Object value) {appendWhereCondition(SymbolEnum.NE,column,value);return this;}public <T, R> LambdaSqlBuilder ge(SqlFunction<T, R> column, Object value) {appendWhereCondition(SymbolEnum.GE,column,value);return this;}public <T, R> LambdaSqlBuilder gt(SqlFunction<T, R> column, Object value) {appendWhereCondition(SymbolEnum.GT,column,value);return this;}public <T, R> LambdaSqlBuilder le(SqlFunction<T, R> column, Object value) {appendWhereCondition(SymbolEnum.LE,column,value);return this;}public <T, R> LambdaSqlBuilder lt(SqlFunction<T, R> column, Object value) {appendWhereCondition(SymbolEnum.LT,column,value);return this;}public <T, R> LambdaSqlBuilder isNull(SqlFunction<T, R> column) {connectColumn(column);sqlBd.append("is Null");return this;}public <T, R> LambdaSqlBuilder isNotNull(SqlFunction<T, R> column) {connectColumn(column);sqlBd.append("is not Null");return this;}private <T, R> void handleLike(SqlFunction<T, R> column, Object value, Consumer<LambdaSqlBuilder> consumer) {if(value != null && !"".equals(value)){connectColumn(column);sqlBd.append("like ?");consumer.accept(this);}}public <T, R> LambdaSqlBuilder like(SqlFunction<T, R> column, Object value) {handleLike(column,value,v->v.params.add("%"+value+"%"));return this;}public <T, R> LambdaSqlBuilder notLike(SqlFunction<T, R> column, Object value) {if(value != null && !"".equals(value)){connectColumn(column);sqlBd.append("not like ?");params.add("%"+value+"%");}return this;}public <T, R> LambdaSqlBuilder likeLeft(SqlFunction<T, R> column, Object value) {handleLike(column,value,v->v.params.add("%"+value));return this;}public <T, R> LambdaSqlBuilder likeRight(SqlFunction<T, R> column, Object value) {handleLike(column,value,v->v.params.add(value+"%"));return this;}public <T, R> LambdaSqlBuilder in(SqlFunction<T, R> column, Collection<?> values) {if(values != null && !values.isEmpty()){connectColumn(column);sqlBd.append("in(");sqlBd.append(in(values));sqlBd.append(")");params.addAll(values);}return this;}public <T, R> LambdaSqlBuilder notIn(SqlFunction<T, R> column, Collection<?> values) {if(values != null && !values.isEmpty()){connectColumn(column);sqlBd.append("not in(");sqlBd.append(in(values));sqlBd.append(")");params.addAll(values);}return this;}public <T, R> LambdaSqlBuilder inSql(SqlFunction<T, R> column, String inSql) {if(inSql != null && !"".equals(inSql)){connectColumn(column);sqlBd.append("in(");sqlBd.append(inSql);sqlBd.append(")");}return this;}public <T, R> LambdaSqlBuilder notInSql(SqlFunction<T, R> column, String inSql) {if(inSql != null && !"".equals(inSql)){connectColumn(column);sqlBd.append("not in(");sqlBd.append(inSql);sqlBd.append(")");}return this;}public <T, R> LambdaSqlBuilder between(SqlFunction<T, R> column, Object value1,Object value2) {if(value1 != null && !"".equals(value1) && value2 != null && !"".equals(value2)){connectColumn(column);sqlBd.append("between ? and ?");params.add(value1);params.add(value2);}return this;}public <T, R> LambdaSqlBuilder notBetween(SqlFunction<T, R> column, Object value1,Object value2) {if(value1 != null && !"".equals(value1) && value2 != null && !"".equals(value2)){connectColumn(column);sqlBd.append("not between ? and ?");params.add(value1);params.add(value2);}return this;}public <T, R> LambdaSqlBuilder orderByAsc(SqlFunction<T, R> column) {orderBy(ASC,column);return this;}@SafeVarargspublic final <T, R> LambdaSqlBuilder orderByAsc(SqlFunction<T, R> ...columns) {orderBy(ASC,columns);return this;}@SafeVarargsprivate final <T, R> void orderBy(String dir,SqlFunction<T, R> ...columns) {if(!orderBy){sqlBd.append(" order by ");}for (SqlFunction<T, R> column : columns) {if(orderBy){sqlBd.append(", ");}orderBy = true;column(column);if(DESC.equals(dir)){sqlBd.append(" desc");}}}public <T, R> LambdaSqlBuilder orderByDesc(SqlFunction<T, R> column) {orderBy(DESC,column);return this;}@SafeVarargspublic final <T, R> LambdaSqlBuilder orderByDesc(SqlFunction<T, R> ...columns) {orderBy(DESC,columns);return this;}public <T, R> LambdaSqlBuilder groupBy(SqlFunction<T, R> column) {groupBy(groupBy,column);return this;}@SafeVarargspublic final <T, R> LambdaSqlBuilder groupBy(SqlFunction<T, R>... columns) {groupBy(groupBy,columns);return this;}@SafeVarargsprivate final <T, R> void groupBy(boolean hadGroupBy,SqlFunction<T, R> ...columns) {if(!hadGroupBy){sqlBd.append(" group by ");}for (SqlFunction<T, R> column : columns) {if(hadGroupBy){sqlBd.append(", ");}hadGroupBy = true;groupBy = true;column(column);}}@Overridepublic String sql() {StringBuilder select = getSelect();return select.append(sqlBd).toString().replace(" 1=1 and", "").replace(" or ()","").replace(" and ()","");}private StringBuilder getSelect(){StringBuilder select = new StringBuilder("select ");String humpToLine;String alias;String property;String className;int size = selected.size();for (SqlFunction<?, ?> column : selected) {className = lambdaClassName(column);property = lambdaProperty(column);humpToLine = lambdaPropertyForHumpToLine(column);select.append(tableClassNameAliasMap.get(className));select.append(".");select.append(humpToLine);alias = propertyAliasMap.get(className+"_"+property);if (alias != null) {select.append(" as " );select.append(alias);} else {if (!humpToLine.equals(property)) {select.append(" as " );select.append(property);}}if(size != 1){select.append(",");}size--;}return select;}/** 驼峰转下划线*/private String humpToLine(String str) {Matcher matcher = humpPattern.matcher(str);StringBuffer sb = new StringBuffer();while (matcher.find()) {matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());}matcher.appendTail(sb);return sb.toString();}private String lambdaProperty(SqlFunction<?,?> func){String implMethodName = func.getImplMethodName();String name = implMethodName.substring(3);return name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);}private String lambdaPropertyForHumpToLine(SqlFunction<?,?> func){String implMethodName = func.getImplMethodName();String name = implMethodName.substring(3);return humpToLine(name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1));}private String lambdaClassName(SqlFunction<?,?> func){String implClass = func.getImplClass();return implClass.substring(implClass.lastIndexOf('/')+1);}private void analysisTable(Class<?> tableClass,String alias,String table){String name = tableClass.getSimpleName();if (StringUtils.isBlank(table)) {table = humpToLine(name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1));}tableMap.put(tableClass,table);if (StringUtils.isBlank(alias)) {tableAliasMap.put(tableClass,table+"_0");tableClassNameAliasMap.put(name,table+"_0");}else {tableAliasMap.put(tableClass,alias);tableClassNameAliasMap.put(name,alias);}}private <T, R> void appendWhereCondition(SymbolEnum symbol, SqlFunction<T, R> column, Object value){if(value != null && !"".equals(value)){connectColumn(column);sqlBd.append(symbol.getName());sqlBd.append(" ?");params.add(value);}}public <T, R> void connectColumn(SqlFunction<T, R> column) {if (isConnect) {sqlBd.append(connect);}else {isConnect = true;}column(column);sqlBd.append(" ");if (connect.equals(OR)) {connect = AND;}}private <T, R>void column(SqlFunction<T, R> column){sqlBd.append(tableClassNameAliasMap.get(lambdaClassName(column)));sqlBd.append(".");sqlBd.append(lambdaPropertyForHumpToLine(column));}}
SqlFunction: 从lambda获取字段名和类名
package com.zl.model.sql;import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.function.Function;@FunctionalInterface
public interface SqlFunction<T, R> extends Function<T, R>, Serializable {default SerializedLambda getSerializedLambda(){Method write;try {write = this.getClass().getDeclaredMethod("writeReplace");write.setAccessible(true);return (SerializedLambda) write.invoke(this);} catch (Exception e) {throw new IllegalArgumentException();}}default String getImplClass() {return getSerializedLambda().getImplClass();}default String getImplMethodName() {return getSerializedLambda().getImplMethodName();}
}
StringSqlBuilder说明:
- select 不支持多次调用
- orderBy 不支持多次调用,且需要传入排序方向
- groupBy 不支持多次调用
- 调用方式表名和字段名都传入字符串,调用链和LambdaSqlBuilder 一样
package com.zl.model.sql;import java.util.Collection;
import java.util.function.Consumer;/*** @author zhangliang* @date 2020/7/7.*/
public class StringSqlBuilder extends AbstractSqlBuilder<StringSqlBuilder> {public StringSqlBuilder select(String select){sqlBd.append("select ");sqlBd.append(select);return typedThis;}public StringSqlBuilder from(String table){sqlBd.append(" from ");sqlBd.append(table);return typedThis;}public StringSqlBuilder leftJoin(String table){sqlBd.append(" left join ");sqlBd.append(table);return typedThis;}public StringSqlBuilder rightJoin(String table){sqlBd.append(" right join ");sqlBd.append(table);return typedThis;}public StringSqlBuilder innerJoin(String table){sqlBd.append(" inner join ");sqlBd.append(table);return typedThis;}public StringSqlBuilder on(String column0,String column1){sqlBd.append(" on ");sqlBd.append(column0);sqlBd.append(" = ");sqlBd.append(column1);return typedThis;}public StringSqlBuilder orderBy(String orderBy) {sqlBd.append(" order by " );sqlBd.append(orderBy);return typedThis;}public StringSqlBuilder groupBy(String groupBy){sqlBd.append(" group by ");sqlBd.append(groupBy);return typedThis;}public StringSqlBuilder eq(String column, Object value) {appendWhereCondition(SymbolEnum.EQ,column,value);return this;}public StringSqlBuilder ne(String column, Object value) {appendWhereCondition(SymbolEnum.NE,column,value);return this;}public StringSqlBuilder ge(String column, Object value) {appendWhereCondition(SymbolEnum.GE,column,value);return this;}public StringSqlBuilder gt(String column, Object value) {appendWhereCondition(SymbolEnum.GT,column,value);return this;}public StringSqlBuilder le(String column, Object value) {appendWhereCondition(SymbolEnum.LE,column,value);return this;}public StringSqlBuilder lt(String column, Object value) {appendWhereCondition(SymbolEnum.LT,column,value);return this;}public StringSqlBuilder isNull(String column) {connectColumn(column);sqlBd.append("is Null");return this;}public StringSqlBuilder isNotNull(String column) {connectColumn(column);sqlBd.append("is not Null");return this;}private void handleLike(String column, Object value, Consumer<StringSqlBuilder> consumer) {if(value != null && !"".equals(value)){connectColumn(column);sqlBd.append("like ?");consumer.accept(this);}}public StringSqlBuilder like(String column, Object value) {handleLike(column,value,v->v.params.add("%"+value+"%"));return this;}public StringSqlBuilder notLike(String column, Object value) {if(value != null && !"".equals(value)){connectColumn(column);sqlBd.append("not like ?");params.add("%"+value+"%");}return this;}public StringSqlBuilder likeLeft(String column, Object value) {handleLike(column,value,v->v.params.add("%"+value));return this;}public StringSqlBuilder likeRight(String column, Object value) {handleLike(column,value,v->v.params.add(value+"%"));return this;}public StringSqlBuilder in(String column, Collection<?> values) {handleIn(column,values,"in");return this;}public StringSqlBuilder notIn(String column, Collection<?> values) {handleIn(column,values,"not in");return this;}private void handleIn(String column, Collection<?> values,String in) {if(values != null && !values.isEmpty()){connectColumn(column);sqlBd.append(in);sqlBd.append("(");sqlBd.append(in(values));sqlBd.append(")");params.addAll(values);}}public StringSqlBuilder inSql(String column, String inSql) {if(inSql != null && !"".equals(inSql)){connectColumn(column);sqlBd.append("in(");sqlBd.append(inSql);sqlBd.append(")");}return this;}public StringSqlBuilder notInSql(String column, String inSql) {if(inSql != null && !"".equals(inSql)){connectColumn(column);sqlBd.append("not in(");sqlBd.append(inSql);sqlBd.append(")");}return this;}public StringSqlBuilder between(String column, Object value1,Object value2) {handleBetween(column,value1,value2,"between");return this;}public StringSqlBuilder notBetween(String column, Object value1,Object value2) {handleBetween(column,value1,value2,"not between");return this;}private void handleBetween(String column, Object value1,Object value2,String between){if(value1 != null && !"".equals(value1) && value2 != null && !"".equals(value2)){connectColumn(column);sqlBd.append(between);sqlBd.append(" ? and ?");params.add(value1);params.add(value2);}}@Overridepublic String sql() {return sqlBd.toString().replace(" 1=1 and", "").replace(" or ()","").replace(" and ()","");}private void appendWhereCondition(SymbolEnum symbol, String column, Object value){if(value != null && !"".equals(value)){connectColumn(column);sqlBd.append(symbol.getName());sqlBd.append(" ?");params.add(value);}}private void connectColumn(String column) {if (isConnect) {sqlBd.append(connect);}else {isConnect = true;}sqlBd.append(column);sqlBd.append(" ");if (connect.equals(OR)) {connect = AND;}}
}
package com.zl.model.sql;import lombok.Getter;/*** @author zhangliang* @date 2020/7/7.*/
@Getter
public enum SymbolEnum {EQ("="),NE("!="),GE(">="),GT(">"),LE("<="),LT("<");private String name;SymbolEnum(String name){this.name = name;}
}
测试类:LambdaSqlBuilder 和StringSqlBuilder对于构建相同sql示例
package com.zl.model.sql;import com.zl.domain.Coder;
import com.zl.domain.Company;import java.util.Arrays;/*** @author zhangliang* @date 2020/7/9.*/
public class TestSqlBuilder {public static void main(String[] args) {LambdaSqlBuilder builder = new LambdaSqlBuilder();System.out.println(builder.select(Coder::getName,Coder::getAge).selectAlias(Company::getName,Company::getId).from(Coder.class,"c").leftJoin(Company.class, "co").on(Coder::getCompanyId, Company::getId).where().eq(Coder::getName, "123").and(v -> v.le(Coder::getName, "123").lt(Coder::getName, "123")).or(v -> v.ge(Coder::getName, "123").or().gt(Coder::getName, "123")).or(v -> v.ne(Coder::getName, null)).like(Coder::getName, "12").isNotNull(Coder::getName).isNull(Coder::getName).between(Coder::getId,1,2).in(Coder::getId, Arrays.asList(1L,2L)).inSql(Coder::getId,"select id from coder").orderByAsc(Company::getId, Company::getName).orderByAsc(Coder::getName).orderByDesc(Coder::getId, Coder::getAge).groupBy(Company::getId, Company::getName).groupBy(Coder::getName).sql());StringSqlBuilder builder1 = new StringSqlBuilder();System.out.println(builder1.select("c.name,c.age,co.name as id").from("coder c").leftJoin("company co").on("c.company_id","co.id").where().eq("c.name", "123").and(v -> v.le("c.name", "123").lt("c.name", "123")).or(v -> v.ge("c.name", "123").or().gt("c.name", "123")).or(v -> v.ne("c.name", null)).like("c.name", "12").isNotNull("c.name").isNull("c.name").between("c.id",1,2).in("c.id", Arrays.asList(1L,2L)).inSql("c.id","select id from coder").orderBy("co.id, co.name, c.name, c.id desc, c.age desc").groupBy("co.id, co.name, c.name").sql());}
}
结果:
select c.name,c.age,co.name as id from coder c left join company co on c.company_id = co.id where c.name = ? and (c.name <= ? and c.name < ?) or (c.name >= ? or c.name > ?) and c.name like ? and c.name is not Null and c.name is Null and c.id between ? and ? and c.id in(?,?) and c.id in(select id from coder) order by co.id, co.name, c.name, c.id desc, c.age desc group by co.id, co.name, c.nameselect c.name,c.age,co.name as id from coder c left join company co on c.company_id = co.id where c.name = ? and (c.name <= ? and c.name < ?) or (c.name >= ? or c.name > ?) and c.name like ? and c.name is not Null and c.name is Null and c.id between ? and ? and c.id in(?,?) and c.id in(select id from coder) order by co.id, co.name, c.name, c.id desc, c.age desc group by co.id, co.name, c.name
这篇关于模拟mybatis-plus使用lambda多表联查的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!