说明:此代码的封装并不适用于mybatis,只是模拟了mybatis-plus的LambdaQueryWrapper进行多表联查,最终生成的sql采用?号占位符,占位符的值封装在一个List<Object>中,可适用于jdbcTemplate。
如果使用过mybatis-plus的LambdaQueryWrapper进行过查询的话,LambdaSqlBuilder的条件查询、排序、分组使用是保持一致的。
AbstractSqlBuilder是LambdaQueryWrapper和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";
@Getter
protected 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]");
@SafeVarargs
public 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;
}
@SafeVarargs
public final <T, R> LambdaSqlBuilder orderByAsc(SqlFunction<T, R> ...columns) {
orderBy(ASC,columns);
return this;
}
@SafeVarargs
private 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;
}
@SafeVarargs
public 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;
}
@SafeVarargs
public final <T, R> LambdaSqlBuilder groupBy(SqlFunction<T, R>... columns) {
groupBy(groupBy,columns);
return this;
}
@SafeVarargs
private 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);
}
}
@Override
public 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);
}
}
@Override
public 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.name
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.name