当前位置: 首页 > news >正文

Spring JdbcTemplate实现自定义动态sql拼接功能

需求描述:

        sql 需要能满足支持动态拼接,包含 查询字段、查询表、关联表、查询条件、关联表的查询条件、排序、分组、去重等

实现步骤:

        1,创建表及导入测试数据

CREATE TABLE YES_DEV.T11 (ID BINARY_BIGINT NOT NULL,NAME VARCHAR(10),XX BINARY_BIGINT,CONSTRAINT _PK_SYS_25_63 PRIMARY KEY (ID)
);CREATE TABLE YES_DEV.T111 (ID BINARY_INTEGER NOT NULL,NAME NUMBER
);INSERT INTO YES_DEV.T11 (ID,NAME,XX) VALUES(11,'123',11),(9,'9',9),(8,'8',8),(7,'7',7),(6,'6',6),(5,'5',5),(4,'4',4),(3,'3',3),(2,'2',2),(1,'1',1);INSERT INTO YES_DEV.T111 (ID,NAME) VALUES(1,123);

        2,创建项目并引入 pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>testMybatis</artifactId><version>1.0-SNAPSHOT</version><!-- 父项目信息 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.8</version><relativePath/></parent><properties><maven.compiler.source>15</maven.compiler.source><maven.compiler.target>15</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency><!-- 高斯DB驱动 --><dependency><groupId>com.huawei.gauss</groupId><artifactId>com.huawei.gauss.jdbc.ZenithDriver</artifactId><version>1.2.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.2</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1-jre</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

        3,编写 application.properties文件

spring.datasource.url=jdbc:zenith:@xxxx:xxx
spring.datasource.username=xxx
spring.datasource.password=xxxx
spring.datasource.driver-class-name=com.huawei.gauss.jdbc.inner.GaussDriver
mybatis.mapper-locations=classpath:mapper/*.xmlorg.apache.springframework.jdbc.core.JdbcTemplate = debug

        4,核心类之 Column

package com.example.dao.sql;import com.example.utils.SqlUtils;
import lombok.Getter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;@Getter
public class Column {private final String name;private final List<String> tableAliases = new ArrayList<>();private Column(String name, List<String> tableAliases) {this.name = name;if (CollectionUtils.isNotEmpty(tableAliases)) {this.tableAliases.addAll(tableAliases);}}public static Column of(String name, String... tableAliases) {if (ArrayUtils.isNotEmpty(tableAliases)) {tableAliases = Arrays.stream(tableAliases).filter(Objects::nonNull).toArray(String[]::new);}if (ArrayUtils.isEmpty(tableAliases)) {String tableAlias = StringUtils.substringBefore(name, ".").trim();if (SqlUtils.isValidAlias(tableAlias)) {tableAliases = new String[] {tableAlias};}}return new Column(name, ArrayUtils.isEmpty(tableAliases) ? null : Arrays.asList(tableAliases));}public String getTableAlias() {return tableAliases.isEmpty() ? null : tableAliases.get(0);}}

        5,核心类之 Table

package com.example.dao.sql;import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.example.utils.SqlUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class Table {private String joinType;private boolean weak;private boolean optimizable = true;private Map<String, List<?>> parameterTempData;private final String name;private final String alias;private final String physicalName;private final List<Condition> joinConditions = new ArrayList<>();private final List<PredicateCondition<?>> joinPredicateConditions = new ArrayList<>();private final List<Condition> filterConditions = new ArrayList<>();private final List<PredicateCondition<?>> filterPredicateConditions = new ArrayList<>();private final List<PropertyCondition> filterPropertyConditions = new ArrayList<>();private Table(String name, String alias, String physicalName) {this.name = name;this.alias = alias;this.physicalName = physicalName;}public static Table of(String name, String physicalName) {String separator = " ";String normalName = name.trim();String alias = StringUtils.substringAfterLast(normalName, separator).trim();SqlUtils.assertValidAlias(alias);if (StringUtils.isEmpty(physicalName)) {physicalName = StringUtils.substringBefore(normalName, separator).trim();}return new Table(name, alias, physicalName);}public static Table of(String name) {return of(name, (String) null);}public static Table of(String name, Map<String, List<?>> parameterTempData) {return of(name, null, parameterTempData);}public static Table of(String name, String physicalName, Map<String, List<?>> parameterTempData) {Table table = Table.of(name, physicalName);table.setParameterTempData(parameterTempData);return table;}public String getJoinType() {return joinType;}public void setJoinType(String joinType) {this.joinType = joinType;}public String getName() {return name;}public String getAlias() {return alias;}public String getPhysicalName() {return physicalName;}public List<Condition> getJoinConditions() {return joinConditions;}public List<PredicateCondition<?>> getJoinPredicateConditions() {return joinPredicateConditions;}public List<Condition> getFilterConditions() {return filterConditions;}public List<PredicateCondition<?>> getFilterPredicateConditions() {return filterPredicateConditions;}public List<PropertyCondition> getFilterPropertyConditions() {return filterPropertyConditions;}public Table on(String condition, String... associationTableAliases) {joinConditions.add(Condition.of(condition, associationTableAliases == null ? null : Arrays.asList(associationTableAliases)));return this;}public <T> Table on(PredicateCondition<T> predicateCondition) {joinPredicateConditions.add(predicateCondition);return this;}public <T> Table on(Predicate<T> predicate, String condition) {return filter(PredicateCondition.of(predicate, condition));}public <T> Table on(Predicate<T> predicate, Function<T, String> conditionSupplier) {return filter(PredicateCondition.of(predicate, conditionSupplier));}@SafeVarargspublic final <T> Table on(Predicate<T> predicate, PredicateCondition<T>... predicateConditions) {if (ArrayUtils.isEmpty(predicateConditions)) {return this;}for (PredicateCondition<T> predicateCondition : predicateConditions) {predicateCondition.setPredicate(predicate.and(predicateCondition.getPredicate()));joinPredicateConditions.add(predicateCondition);}return this;}public Table filter(String... conditions) {if (ArrayUtils.isNotEmpty(conditions)) {filterConditions.addAll(Stream.of(conditions).map(Condition::of).collect(Collectors.toList()));}return this;}public <T> Table filter(PredicateCondition<T> predicateCondition) {filterPredicateConditions.add(predicateCondition);return this;}public <T> Table filter(SFunction<T, Object> fieldGetter, String columnName) {return filter(fieldGetter, columnName, true);}public <T> Table filter(SFunction<T, Object> fieldGetter, String columnName, boolean optimizable) {filterPropertyConditions.add(PropertyCondition.of(fieldGetter, columnName, optimizable));return this;}public <T> Table filter(Predicate<T> predicate, String condition) {return filter(PredicateCondition.of(predicate, condition));}public <T> Table filter(Predicate<T> predicate, Function<T, String> conditionSupplier) {return filter(PredicateCondition.of(predicate, conditionSupplier));}@SafeVarargspublic final <T> Table filter(Predicate<T> predicate, PredicateCondition<T>... predicateConditions) {if (ArrayUtils.isEmpty(predicateConditions)) {return this;}for (PredicateCondition<T> predicateCondition : predicateConditions) {predicateCondition.setPredicate(predicate.and(predicateCondition.getPredicate()));filter(predicateCondition);}return this;}public Table weak() {return weak(true);}public Table weak(boolean weak) {this.weak = weak;return this;}public boolean isWeak() {return weak;}public Table optimize(boolean optimizable) {this.optimizable = optimizable;return this;}public boolean isOptimizable() {return optimizable;}public Map<String, List<?>> getParameterTempData() {return parameterTempData;}public void setParameterTempData(Map<String, List<?>> parameterTempData) {this.parameterTempData = parameterTempData;}}

        6,核心类之 Condition

package com.example.dao.sql;import java.util.Collections;
import java.util.List;class Condition {private final String sql;private final List<String> associationTableAliases;public static Condition of(String sql) {return new Condition(sql, null);}public static Condition of(String sql, List<String> associationTableAliases) {return new Condition(sql, associationTableAliases);}private Condition(String sql, List<String> associationTableAliases) {this.sql = sql;this.associationTableAliases = associationTableAliases == null ? Collections.emptyList() : associationTableAliases;}public String getSql() {return sql;}public List<String> getAssociationTableAliases() {return associationTableAliases;}}

        7,核心类之 PredicateCondition

package com.example.dao.sql;import java.util.function.Function;
import java.util.function.Predicate;public class PredicateCondition<T> {private Predicate<T> predicate;private Condition condition;private Function<T, Condition> conditionSupplier;public static <T> PredicateCondition<T> of(Predicate<T> predicate, String condition) {return new PredicateCondition<>(predicate, Condition.of(condition));}public static <T> PredicateCondition<T> of(Predicate<T> predicate, Function<T, String> conditionSupplier) {return new PredicateCondition<>(predicate, parameter -> Condition.of(conditionSupplier.apply(parameter)));}private PredicateCondition(Predicate<T> predicate, Condition condition) {this.predicate = predicate;this.condition = condition;}private PredicateCondition(Predicate<T> predicate, Function<T, Condition> conditionSupplier) {this.predicate = predicate;this.conditionSupplier = conditionSupplier;}public Predicate<T> getPredicate() {return predicate;}public void setPredicate(Predicate<T> predicate) {this.predicate = predicate;}public Condition getCondition() {return condition;}public void setCondition(Condition condition) {this.condition = condition;}public Function<T, Condition> getConditionSupplier() {return conditionSupplier;}public void setConditionSupplier(Function<T, Condition> conditionSupplier) {this.conditionSupplier = conditionSupplier;}}

        8,核心类之 PropertyCondition

package com.example.dao.sql;import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.Data;@Data
public class PropertyCondition {private SFunction<?, Object> fieldGetter;private String columnName;private boolean optimizable;private PropertyCondition(SFunction<?, Object> fieldGetter, String columnName, boolean optimizable) {this.fieldGetter = fieldGetter;this.columnName = columnName;this.optimizable = optimizable;}public static PropertyCondition of(SFunction<?, Object> fieldGetter, String columnName, boolean optimizable) {return new PropertyCondition(fieldGetter, columnName, optimizable);}}

        9,核心类之 SqlBuilder

package com.example.dao.sql;import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.example.utils.ObjectUtils;
import com.example.utils.SqlUtils;
import com.google.common.base.CaseFormat;
import lombok.Getter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.reflection.property.PropertyNamer;import java.util.*;
import java.util.stream.Collectors;//TODO 优化join排序,无依赖的inner join排在前面,有依赖的排在依赖表后面
public class SqlBuilder {private static final int PARAM_VALUE_NUM = 1000;private static final String NA = "NA";private static final String FROM = "FROM";private static final String INNER = "INNER";private static final String LEFT = "LEFT";private static final String RIGHT = "RIGHT";private static final String FULL = "FULL";@Getterprivate final List<Table> fromTables = new ArrayList<>();@Getterprivate final List<Table> queryTables = new ArrayList<>();@Getterprivate final List<Column> selectColumns = new ArrayList<>();private final List<Condition> filterConditions = new ArrayList<>();private final List<String> orderByConditions = new ArrayList<>();private final List<String> appendSqlSnippets = new ArrayList<>();private final Map<Table, TableQueryCondition> tableQueryConditions = new HashMap<>();//private final Map<Table, List<Table>> associationTables = new HashMap<>();/*   private final List<PredicateCondition<?>> predicateConditions = new ArrayList<>();*/private boolean enablePlaceholder = true;private boolean enableOrderBy = true;/*** 增加字段去重*/private boolean enableDistinct;public boolean isEnablePlaceholder() {return enablePlaceholder;}public SqlBuilder setEnablePlaceholder(boolean enablePlaceholder) {this.enablePlaceholder = enablePlaceholder;return this;}public SqlBuilder enableOrderBy() {this.enableOrderBy = true;return this;}public SqlBuilder disableOrderBy() {this.enableOrderBy = false;return this;}public boolean isEnableDistinct() {return enableDistinct;}public void setEnableDistinct(boolean enableDistinct) {this.enableDistinct = enableDistinct;}private static class TableQueryCondition {private final Table table;private String joinCondition;private String filterCondition;private boolean referenced;public TableQueryCondition(Table table) {this.table = table;}public Table getTable() {return table;}public String getJoinCondition() {return joinCondition;}public void setJoinCondition(String joinCondition) {this.joinCondition = joinCondition;}public String getFilterCondition() {return filterCondition;}public void setFilterCondition(String filterCondition) {this.filterCondition = filterCondition;}public boolean isReferenced() {return referenced;}public void setReferenced(boolean referenced) {this.referenced = referenced;}}private Map<String, List<?>> supplementPropertyFilters(Table table, Object queryCriteria) {Map<String, List<?>> parameterTempData = new HashMap<>();if(queryCriteria == null){return parameterTempData;}for (PropertyCondition propertyCondition : table.getFilterPropertyConditions()) {SFunction<?, Object> fieldGetter = propertyCondition.getFieldGetter();String fieldName = PropertyNamer.methodToProperty(LambdaUtils.resolve(fieldGetter).getImplMethodName());Object fieldValue = fieldGetter.apply(ObjectUtils.cast(queryCriteria));if (!(fieldValue instanceof List) || ((List<?>) fieldValue).isEmpty()) {continue;}String columnName = propertyCondition.getColumnName();if (!propertyCondition.isOptimizable()) {table.optimize(false);}List<?> collectionValue = ((List<?>) fieldValue);if (collectionValue.size() <= PARAM_VALUE_NUM) {StringBuilder condition = new StringBuilder(columnName).append(SqlUtils.buildColumnInCondition(fieldName, collectionValue, enablePlaceholder));if (collectionValue.contains(NA) || collectionValue.contains(null)) {condition.append(" OR ").append(columnName).append(" is null");table.optimize(false);}table.filter(condition.toString());} else {// 当参数值过多采用临时表关联时,将表设置为强关联// todo referencedtable.weak(false);String tempTableAlias = "_" + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName);String tempDataType = tempTableAlias.toUpperCase();StringBuilder joinCondition = new StringBuilder();joinCondition.append(tempTableAlias).append(".value").append(" = ");if (collectionValue.contains(NA)) {joinCondition.append("nvl(").append(columnName).append(", '").append(NA).append("')");} else {joinCondition.append(columnName);}Object sampleValue = collectionValue.get(0);String queryField = sampleValue instanceof Number ? "to_number(string_value)" : "string_value";Table tempTable = Table.of("(SELECT " + queryField + " value FROM t_comm_data_temp WHERE data_type = '" + tempDataType + "') " + tempTableAlias).on(joinCondition.toString(), table.getAlias());tempTable.setJoinType(INNER);associationTable(tempTable, table);parameterTempData.put(tempDataType, collectionValue);}}return parameterTempData;}public ExecutionSql builder(Object queryCriteria) {ExecutionSql executionSql = new ExecutionSql();analyzeTables(queryCriteria, executionSql);StringBuilder sql = new StringBuilder();appendSelectColumns(sql);appendFromTables(sql, executionSql);appendJoinTables(sql, executionSql);appendWhereConditions(sql);appendOrderByConditions(sql);appendSqlSnippets(sql);executionSql.setSql(sql.toString());return executionSql;}private void analyzeTables(Object queryCriteria, ExecutionSql executionSql) {supplementTablePropertyFilters(queryCriteria, executionSql);analyzeTableDependences(queryCriteria);}private void supplementTablePropertyFilters(Object queryCriteria, ExecutionSql executionSql) {Map<String, List<?>> parameterTempData = new HashMap<>();// Query Table列表需进行拷贝,便于逻辑中动态增加临时表for (Table table : new ArrayList<>(queryTables)) {parameterTempData.putAll(supplementPropertyFilters(table, queryCriteria));}executionSql.setParameterTempData(parameterTempData);}private void analyzeTableDependences(Object queryCriteria) {Set<String> referencedTableAliases = filterConditions.stream().flatMap(record -> record.getAssociationTableAliases().stream()).collect(Collectors.toSet());referencedTableAliases.addAll(selectColumns.stream().flatMap(column -> column.getTableAliases().stream()).collect(Collectors.toSet()));Map<String, Table> tableAliasMap = new HashMap<>();List<Table> referencedTables = new ArrayList<>();for (Table table : queryTables) {TableQueryCondition tableAnalysisResult = new TableQueryCondition(table);tableAnalysisResult.setJoinCondition(buildConditionSql(table.getJoinConditions(), table.getJoinPredicateConditions(), queryCriteria));tableAnalysisResult.setFilterCondition(buildConditionSql(table.getFilterConditions(), table.getFilterPredicateConditions(), queryCriteria));// 是否自带过滤条件或被其他其他地方使用if (StringUtils.isNotEmpty(tableAnalysisResult.getFilterCondition()) || referencedTableAliases.contains(table.getAlias())) {tableAnalysisResult.setReferenced(true);referencedTables.add(table);}// 是否开启优化, 有过滤条件的情况下将Left/Right Join优化成Inner Join,提升查询速度if (table.isOptimizable() && Arrays.asList(LEFT, RIGHT).contains(table.getJoinType()) && StringUtils.isNotEmpty(tableAnalysisResult.getFilterCondition())) {table.setJoinType(INNER);}tableQueryConditions.put(table, tableAnalysisResult);tableAliasMap.put(StringUtils.defaultString(table.getAlias(), table.getName()), table);}//TODO tale and table 引用依赖/*for (Table table : referencedTables) {markReferenceTable(table, queryCriteria, tableAliasMap);}*/}/*private void markReferenceTable(Table referencedTable, Object queryCriteria, Map<String, Table> tableAliasMap) {List<Condition> effectiveConditions = findEffectiveConditions(referencedTable.getJoinConditions(), referencedTable.getJoinPredicateConditions(),queryCriteria);effectiveConditions.addAll(findEffectiveConditions(referencedTable.getFilterConditions(), referencedTable.getFilterPredicateConditions(), queryCriteria));List<String> associationTableAliases = effectiveConditions.stream().flatMap(condition -> condition.getAssociationTableAliases().stream()).collect(Collectors.toList());for (String associationTableAlias : associationTableAliases) {Table associationTable = tableAliasMap.get(associationTableAlias);Assert.notNull(associationTable, "Can't find association table for alias [" + associationTableAlias + "]");TableQueryCondition tableQueryCondition = tableQueryConditions.get(associationTable);if (!tableQueryCondition.isReferenced()) {//todo table.setReferenced(true);tableQueryCondition.setReferenced(true);markReferenceTable(associationTable, queryCriteria, tableAliasMap);}}}*/private List<Condition> findEffectiveConditions(List<Condition> conditions, List<PredicateCondition<?>> predicateConditions, Object queryCriteria) {List<Condition> effectiveConditions = new ArrayList<>();conditions.stream().filter(record -> StringUtils.isNotEmpty(record.getSql())).forEach(effectiveConditions::add);if (predicateConditions != null) {for (PredicateCondition<?> predicateCondition : predicateConditions) {if (predicateCondition.getPredicate().test(ObjectUtils.cast(queryCriteria))) {Condition condition = predicateCondition.getCondition();if (condition != null && StringUtils.isNotEmpty(condition.getSql())) {effectiveConditions.add(condition);}if (predicateCondition.getConditionSupplier() != null) {condition = predicateCondition.getConditionSupplier().apply(ObjectUtils.cast(queryCriteria));if (condition != null && StringUtils.isNotEmpty(condition.getSql())) {effectiveConditions.add(condition);}}}}}return effectiveConditions;}private String buildConditionSql(List<Condition> conditions, List<PredicateCondition<?>> predicateConditions, Object queryCriteria) {StringBuilder conditionSql = new StringBuilder();List<Condition> effectiveConditions = findEffectiveConditions(conditions, predicateConditions, queryCriteria);for (Condition condition : effectiveConditions) {appendChildCondition(conditionSql, condition.getSql(), true);}return conditionSql.toString();}private void appendSelectColumns(StringBuilder sql) {sql.append("SELECT ");if(enableDistinct){sql.append("DISTINCT ");}for (int i = 0; i < selectColumns.size(); i++) {Column currentColumn = selectColumns.get(i);sql.append(currentColumn.getName());if (i < selectColumns.size() - 1) {sql.append(", ");}}}private void appendFromTables(StringBuilder sql, ExecutionSql executionSql) {List<Table> fromTables = queryTables.stream().filter(table -> FROM.equals(table.getJoinType())).collect(Collectors.toList());sql.append(" FROM ");for (int i = 0; i < fromTables.size(); i++) {Table table = fromTables.get(i);sql.append(table.getName());if (i < fromTables.size() - 1) {sql.append(", ");}if (table.getParameterTempData() != null) {executionSql.getParameterTempData().putAll(table.getParameterTempData());}}}private void appendJoinTables(StringBuilder sql, ExecutionSql executionSql) {List<Table> joinTables = queryTables.stream().filter(table -> !FROM.equals(table.getJoinType())).collect(Collectors.toList());for (Table table : joinTables) {if (!judgeJoinTable(table)) {continue;}sql.append(" ").append(table.getJoinType()).append(" JOIN ").append(table.getName());sql.append(" ON ").append(tableQueryConditions.get(table).getJoinCondition());if (table.getParameterTempData() != null) {executionSql.getParameterTempData().putAll(table.getParameterTempData());}}}private boolean judgeJoinTable(Table table) {// 非弱表(即强表)则必须Joinif (!table.isWeak()) {return true;}// 是否有被引用return tableQueryConditions.get(table).isReferenced();}private void appendWhereConditions(StringBuilder sql) {StringBuilder whereConditionSql = new StringBuilder();for (Table table : queryTables) {String conditionSql = tableQueryConditions.get(table).getFilterCondition();appendChildCondition(whereConditionSql, conditionSql);}String conditionSql = buildConditionSql(filterConditions, null, null);appendChildCondition(whereConditionSql, conditionSql);if (whereConditionSql.length() > 0) {sql.append(" WHERE ").append(whereConditionSql);}}private void appendChildCondition(StringBuilder parentSql, String condition) {appendChildCondition(parentSql, condition, false);}private void appendChildCondition(StringBuilder parentSql, String condition, boolean bracketWrap) {if (StringUtils.isNotEmpty(condition)) {if (parentSql.length() > 0) {parentSql.append(" AND ");}if (bracketWrap) {condition = "(" + condition + ")";}parentSql.append(condition);}}private void appendOrderByConditions(StringBuilder sql) {if (enableOrderBy && CollectionUtils.isNotEmpty(orderByConditions)) {sql.append(" ORDER BY ").append(String.join(", ", orderByConditions));}}private void appendSqlSnippets(StringBuilder sql) {if (CollectionUtils.isNotEmpty(appendSqlSnippets)) {sql.append(" ").append(String.join(" ", appendSqlSnippets));}}public SqlBuilder select(String... columnNames) {if (ArrayUtils.isNotEmpty(columnNames)) {for (String columnName : columnNames) {select(columnName, null);}}return this;}public SqlBuilder select(String columnName, String tableAlias) {selectColumns.add(Column.of(columnName, tableAlias));return this;}public SqlBuilder select(Column... columns) {if (ArrayUtils.isEmpty(columns)) {return this;}return select(Arrays.asList(columns));}public SqlBuilder select(List<Column> columns) {selectColumns.addAll(columns);return this;}public SqlBuilder fromTable(Table table) {table.setJoinType(FROM);fromTables.add(table);associationTable(table, null);return this;}public SqlBuilder innerJoin(Table table) {table.setJoinType(INNER);associationTable(table, null);return this;}public SqlBuilder leftJoin(Table table) {table.weak().setJoinType(LEFT);associationTable(table, null);return this;}public SqlBuilder rightJoin(Table table) {table.weak().setJoinType(RIGHT);associationTable(table, null);return this;}public SqlBuilder fullJoin(Table table) {table.setJoinType(FULL);associationTable(table, null);return this;}private void associationTable(Table table, Table previousTable) {int index = previousTable == null ? queryTables.size() : queryTables.indexOf(previousTable) + 1;queryTables.add(index, table);}public SqlBuilder filter(String condition, String... associationTableAliases) {filterConditions.add(Condition.of(condition, ArrayUtils.isEmpty(associationTableAliases) ? Collections.emptyList() : Arrays.asList(associationTableAliases)));return this;}public SqlBuilder orderBy(String condition) {orderByConditions.add(condition);return this;}public SqlBuilder append(String sqlSnippet) {appendSqlSnippets.add(sqlSnippet);return this;}}

        10,核心类之 ExecutionSql

package com.example.dao.sql;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;public class ExecutionSql {private String id;private String sql;private Map<String, List<?>> parameterTempData = new HashMap<>();public ExecutionSql() {id = UUID.randomUUID().toString().replace("-", "");id = id.substring(id.length() - 10);}public String getId() {return id;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}public Map<String, List<?>> getParameterTempData() {return parameterTempData;}public void setParameterTempData(Map<String, List<?>> parameterTempData) {this.parameterTempData = parameterTempData;}}

        11,核心类之 BasicConditionQueryCriteria(入参)

package com.example.dao.sql;import lombok.Data;import java.util.List;
import java.util.Set;@Data
public class BasicConditionQueryCriteria implements Cloneable {private List<String> name;private List<Long> xx;private List<Integer> newName;
}

        12,编写测试类

import com.example.DemoApplication;
import com.example.dao.sql.*;
import com.example.entity.DataTemp;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@Slf4j
public class SqlBuilderTest {@Autowiredprivate JdbcTemplate jdbcTemplate;@Testpublic void test1(){BasicConditionQueryCriteria queryCriteria = new BasicConditionQueryCriteria();queryCriteria.setName(Arrays.asList("1","2"));//拼接sqlList<Column> columns = Arrays.asList(Column.of("t11.id","t11"),Column.of("t11.name","t11"),Column.of("t11.xx","t11"),Column.of("T1111.name as newName","T1111"));SqlBuilder sqlBuilder = new SqlBuilder();sqlBuilder.select(columns);sqlBuilder.setEnablePlaceholder(false);sqlBuilder.fromTable(Table.of("t11 t11").filter(BasicConditionQueryCriteria::getName,"t11.name")).leftJoin(Table.of("T1111 T1111").on("t11.name = T1111.name"));ExecutionSql executionSql = sqlBuilder.builder(queryCriteria);executionSql.setSql(executionSql.getSql() + " order by t11.id");log.info("sql:{}",executionSql.getSql());List<Map<String, Object>> maps = jdbcTemplate.queryForList(executionSql.getSql());maps.stream().forEach(v->{v.entrySet().stream().forEach(w->{log.info("key:{},value:{}",w.getKey(),w.getValue());});});}}

        13,项目结构类图

相关文章:

Spring JdbcTemplate实现自定义动态sql拼接功能

需求描述&#xff1a; sql 需要能满足支持动态拼接&#xff0c;包含 查询字段、查询表、关联表、查询条件、关联表的查询条件、排序、分组、去重等 实现步骤&#xff1a; 1&#xff0c;创建表及导入测试数据 CREATE TABLE YES_DEV.T11 (ID BINARY_BIGINT NOT NULL,NAME VARCH…...

第十一篇:操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲

操作系统新纪元&#xff1a;智能融合、量子跃迁与虚拟现实的交响曲 1 引言 在数字化的浪潮中&#xff0c;操作系统如同一位智慧的舵手&#xff0c;引领着信息技术的航船穿越波涛汹涌的海洋。随着人工智能、物联网、量子计算等前沿技术的蓬勃发展&#xff0c;操作系统正站在一个…...

【大数据】学习笔记

文章目录 [toc]NAT配置IP配置SecureCRT配置PropertiesTerminal Java安装环境变量配置 Hadoop安装修改配置文件hadoop-env.shyarn-env.shslavescore-site.xmlhdfs-site.xmlmapred-site.xmlyarn-site.xml 环境变量配置 IP与主机名映射关系配置hostname配置映射关系配置 关闭防火墙…...

PHP 框架安全:ThinkPHP 序列 漏洞测试.

什么是 ThinkPHP 框架. ThinkPHP 是一个流行的国内 PHP 框架&#xff0c;它提供了一套完整的安全措施来帮助开发者构建安全可靠的 web 应用程序。ThinkPHP 本身不断更新和改进&#xff0c;以应对新的安全威胁和漏洞。 目录&#xff1a; 什么是 ThinkPHP 框架. ThinkPHP 框架…...

厂家自定义 Android Ant编译流程源码分析

0、Ant安装 Windows下安装Ant&#xff1a; ant 官网可下载 http://ant.apache.org ant 环境配置&#xff1a; 解压ant的包到本地目录。 在环境变量中设置ANT_HOME&#xff0c;值为你的安装目录。 把ANT_HOME/bin加到你系统环境的path。 Ubuntu下安装Ant&#xff1a; sudo apt…...

基于springboot+vue+Mysql的体质测试数据分析及可视化设计

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…...

uniapp的app端推送功能,不使用unipush

1&#xff1a;推送功能使用htmlPlus实现&#xff1a;地址HTML5 API Reference (html5plus.org) 效果图&#xff1a; 代码实现&#xff1a; <template><view class"content"><view class"text-area"><button click"createMsg&q…...

数据结构(四)————二叉树和堆(中)

制作不易&#xff0c;三连支持一下呗&#xff01;&#xff01;&#xff01; 文章目录 前言一、堆的概念及结构二、堆的实现三.堆的应用 总结 前言 CSDN 这篇博客介绍了二叉树中的基本概念和存储结构&#xff0c;接下来我们将运用这些结构来实现二叉树 一、堆的概念及结构 1…...

随便写点东西

1 react的高阶组件 1.1 操纵组件的props、对组件的props进行增删&#xff1b; 1.2 复用组件逻辑 服用的组件逻辑&#xff0c;互不影响&#xff1b;比如高阶组件中复用了input框&#xff0c;输入内容是互不影响的&#xff1b; 1.3 可以通过配置装饰器来实现高阶组件&#xff08…...

Mac 报错 Zsh: command not found :brew

Mac 安装其他命令时报错 Zsh: command not found :brew终于找到一个能行的&#xff0c;还能够配置国内下载源&#xff0c;记录一下 执行 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"选择一个开始继续执行即可...

分析师常用商业分析模型

一、背景 在用户调研中&#xff0c;我们发现分析师对商业分析模型的使用还是比较频繁。本文主要对用户调研结果中的分析师常用商业分析模型以及一些业界经典的商业分析模型进行分析&#xff0c;并梳理出执行落地流程&#xff0c;以此来指导分析师工具设计分析功能的引导性。 …...

KMeans,KNN,Mean-shift算法的学习

1.KMeans算法是什么&#xff1f; 在没有标准标签的情况下&#xff0c;以空间的k个节点为中心进行聚类&#xff0c;对最靠近他们的对象进行归类。 2.KMeans公式&#xff1a; 2. 1.关键分为三个部分&#xff1a; 1.一开始会定义n个中心点&#xff0c;然后计算各数据点与中心点…...

web前端笔记8

8. Less的使用 Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言。Less 是一门CSS预处理语言,它扩充了CSS语言,增加了诸如变量、混合(mixin)、函数等功能,让CSS更易维护、方便制作主题、扩充。Less可以运行在Node.js或浏览器端。LESS由Alexis Sellier于…...

【漏洞复现】Apahce HTTPd 2.4.49(CVE-2021-41773)路径穿越漏洞

简介&#xff1a; Apache HTTP Server是一个开源、跨平台的Web服务器&#xff0c;它在全球范围内被广泛使用。2021年10月5日&#xff0c;Apache发布更新公告&#xff0c;修复了Apache HTTP Server2.4.49中的一个路径遍历和文件泄露漏洞&#xff08;CVE-2021-41773&#xff09;。…...

API低代码平台介绍2-最基本的数据查询功能

最基本的数据查询功能 本篇文章我们将介绍如何使用ADI平台定义一个基本的数据查询接口。由于是介绍平台具体功能的第一篇文章&#xff0c;里面会涉及比较多的概念介绍&#xff0c;了解了这些概念有助于您阅读后续的文章。 ADI平台的首页面如下&#xff1a; 1.菜单介绍 1.1 O…...

面试经典150题——盛最多水的容器

面试经典150题 day28 题目来源我的题解方法一 双指针 题目来源 力扣每日一题&#xff1b;题序&#xff1a;11 我的题解 方法一 双指针 使用两个指针left和right&#xff0c;初始分别指向最左侧和最右侧&#xff0c;然后每次移动矮的一侧。存水量Math.min(height[left],heigh…...

Box86源码解读记录

1. 背景说明 Github地址&#xff1a;https://github.com/ptitSeb/box86 官方推荐的视频教程&#xff1a;Box86/Box64视频教程网盘 2. 程序执行主体图 Box86版本: Box86 with Dynarec v0.3.4 主函数会执行一大堆的初始化工作&#xff0c;包括但不限于&#xff1a;BOX上下文 …...

Azure AKS日志查询KQL表达式

背景需求 Azure&#xff08;Global&#xff09; AKS集群中&#xff0c;需要查询部署服务的历史日志&#xff0c;例如&#xff1a;我部署了服务A&#xff0c;但服务A的上一个版本Pod已经被杀掉由于版本的更新迭代&#xff0c;而我在命令行中只能看到当前版本的pod日志&#xff…...

Set接口

Set接口的介绍 Set接口基本介绍 无序&#xff08;添加和取出的顺序不一致&#xff09;&#xff0c;没有索引不允许重复元素&#xff0c;所以最多包含一个nullJDK API中Set接口的实现类&#xff1a;主要有HashSet&#xff1b;TreeSet Set接口的常用方法 和List 接口一样&am…...

vue2结合element-ui实现TreeSelect 树选择功能

需求背景 在日常开发中&#xff0c;我们会遇见很多不同的业务需求。如果让你用element-ui实现一个 tree-select 组件&#xff0c;你会怎么做&#xff1f; 这个组件在 element-plus 中是有这个组件存在的&#xff0c;但是在 element-ui 中是没有的。 可能你会直接使用 elemen…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...