forked from plusone/plusone-commons
创建简单的 JdbcUtil,并借助 MyBatis 的 SQL 构建器简化 SQL 的拼接。
parent
85ababc482
commit
fa3f4695f1
13
pom.xml
13
pom.xml
|
@ -49,6 +49,19 @@
|
||||||
<version>5.9.2</version>
|
<version>5.9.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>4.0.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<version>42.3.8</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -0,0 +1,740 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.ibatis.jdbc;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Clinton Begin
|
||||||
|
* @author Jeff Butler
|
||||||
|
* @author Adam Gent
|
||||||
|
* @author Kazuki Shimizu
|
||||||
|
*/
|
||||||
|
public abstract class AbstractSQL<T> {
|
||||||
|
|
||||||
|
private static final String AND = ") \nAND (";
|
||||||
|
private static final String OR = ") \nOR (";
|
||||||
|
|
||||||
|
private final SQLStatement sql = new SQLStatement();
|
||||||
|
|
||||||
|
public abstract T getSelf();
|
||||||
|
|
||||||
|
public T UPDATE(String table) {
|
||||||
|
sql().statementType = SQLStatement.StatementType.UPDATE;
|
||||||
|
sql().tables.add(table);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T SET(String sets) {
|
||||||
|
sql().sets.add(sets);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the.
|
||||||
|
*
|
||||||
|
* @param sets
|
||||||
|
* the sets
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T SET(String... sets) {
|
||||||
|
sql().sets.addAll(Arrays.asList(sets));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T INSERT_INTO(String tableName) {
|
||||||
|
sql().statementType = SQLStatement.StatementType.INSERT;
|
||||||
|
sql().tables.add(tableName);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T VALUES(String columns, String values) {
|
||||||
|
INTO_COLUMNS(columns);
|
||||||
|
INTO_VALUES(values);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Into columns.
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
* the columns
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T INTO_COLUMNS(String... columns) {
|
||||||
|
sql().columns.addAll(Arrays.asList(columns));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Into values.
|
||||||
|
*
|
||||||
|
* @param values
|
||||||
|
* the values
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T INTO_VALUES(String... values) {
|
||||||
|
List<String> list = sql().valuesList.get(sql().valuesList.size() - 1);
|
||||||
|
Collections.addAll(list, values);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T SELECT(String columns) {
|
||||||
|
sql().statementType = SQLStatement.StatementType.SELECT;
|
||||||
|
sql().select.add(columns);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select.
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
* the columns
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T SELECT(String... columns) {
|
||||||
|
sql().statementType = SQLStatement.StatementType.SELECT;
|
||||||
|
sql().select.addAll(Arrays.asList(columns));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T SELECT_DISTINCT(String columns) {
|
||||||
|
sql().distinct = true;
|
||||||
|
SELECT(columns);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select distinct.
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
* the columns
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T SELECT_DISTINCT(String... columns) {
|
||||||
|
sql().distinct = true;
|
||||||
|
SELECT(columns);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T DELETE_FROM(String table) {
|
||||||
|
sql().statementType = SQLStatement.StatementType.DELETE;
|
||||||
|
sql().tables.add(table);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T FROM(String table) {
|
||||||
|
sql().tables.add(table);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From.
|
||||||
|
*
|
||||||
|
* @param tables
|
||||||
|
* the tables
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T FROM(String... tables) {
|
||||||
|
sql().tables.addAll(Arrays.asList(tables));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T JOIN(String join) {
|
||||||
|
sql().join.add(join);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join.
|
||||||
|
*
|
||||||
|
* @param joins
|
||||||
|
* the joins
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T JOIN(String... joins) {
|
||||||
|
sql().join.addAll(Arrays.asList(joins));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T INNER_JOIN(String join) {
|
||||||
|
sql().innerJoin.add(join);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner join.
|
||||||
|
*
|
||||||
|
* @param joins
|
||||||
|
* the joins
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T INNER_JOIN(String... joins) {
|
||||||
|
sql().innerJoin.addAll(Arrays.asList(joins));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T LEFT_OUTER_JOIN(String join) {
|
||||||
|
sql().leftOuterJoin.add(join);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left outer join.
|
||||||
|
*
|
||||||
|
* @param joins
|
||||||
|
* the joins
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T LEFT_OUTER_JOIN(String... joins) {
|
||||||
|
sql().leftOuterJoin.addAll(Arrays.asList(joins));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T RIGHT_OUTER_JOIN(String join) {
|
||||||
|
sql().rightOuterJoin.add(join);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right outer join.
|
||||||
|
*
|
||||||
|
* @param joins
|
||||||
|
* the joins
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T RIGHT_OUTER_JOIN(String... joins) {
|
||||||
|
sql().rightOuterJoin.addAll(Arrays.asList(joins));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T OUTER_JOIN(String join) {
|
||||||
|
sql().outerJoin.add(join);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outer join.
|
||||||
|
*
|
||||||
|
* @param joins
|
||||||
|
* the joins
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T OUTER_JOIN(String... joins) {
|
||||||
|
sql().outerJoin.addAll(Arrays.asList(joins));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T WHERE(String conditions) {
|
||||||
|
sql().where.add(conditions);
|
||||||
|
sql().lastList = sql().where;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where.
|
||||||
|
*
|
||||||
|
* @param conditions
|
||||||
|
* the conditions
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T WHERE(String... conditions) {
|
||||||
|
sql().where.addAll(Arrays.asList(conditions));
|
||||||
|
sql().lastList = sql().where;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T OR() {
|
||||||
|
sql().lastList.add(OR);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T AND() {
|
||||||
|
sql().lastList.add(AND);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GROUP_BY(String columns) {
|
||||||
|
sql().groupBy.add(columns);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group by.
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
* the columns
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T GROUP_BY(String... columns) {
|
||||||
|
sql().groupBy.addAll(Arrays.asList(columns));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T HAVING(String conditions) {
|
||||||
|
sql().having.add(conditions);
|
||||||
|
sql().lastList = sql().having;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Having.
|
||||||
|
*
|
||||||
|
* @param conditions
|
||||||
|
* the conditions
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T HAVING(String... conditions) {
|
||||||
|
sql().having.addAll(Arrays.asList(conditions));
|
||||||
|
sql().lastList = sql().having;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T ORDER_BY(String columns) {
|
||||||
|
sql().orderBy.add(columns);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order by.
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
* the columns
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.4.2
|
||||||
|
*/
|
||||||
|
public T ORDER_BY(String... columns) {
|
||||||
|
sql().orderBy.addAll(Arrays.asList(columns));
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the limit variable string(e.g. {@code "#{limit}"}).
|
||||||
|
*
|
||||||
|
* @param variable
|
||||||
|
* a limit variable string
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #OFFSET(String)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T LIMIT(String variable) {
|
||||||
|
sql().limit = variable;
|
||||||
|
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the limit value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* an offset value
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #OFFSET(long)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T LIMIT(int value) {
|
||||||
|
return LIMIT(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset variable string(e.g. {@code "#{offset}"}).
|
||||||
|
*
|
||||||
|
* @param variable
|
||||||
|
* a offset variable string
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #LIMIT(String)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T OFFSET(String variable) {
|
||||||
|
sql().offset = variable;
|
||||||
|
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* an offset value
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #LIMIT(int)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T OFFSET(long value) {
|
||||||
|
return OFFSET(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the fetch first rows variable string(e.g. {@code "#{fetchFirstRows}"}).
|
||||||
|
*
|
||||||
|
* @param variable
|
||||||
|
* a fetch first rows variable string
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #OFFSET_ROWS(String)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T FETCH_FIRST_ROWS_ONLY(String variable) {
|
||||||
|
sql().limit = variable;
|
||||||
|
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the fetch first rows value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* a fetch first rows value
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #OFFSET_ROWS(long)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T FETCH_FIRST_ROWS_ONLY(int value) {
|
||||||
|
return FETCH_FIRST_ROWS_ONLY(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset rows variable string(e.g. {@code "#{offset}"}).
|
||||||
|
*
|
||||||
|
* @param variable
|
||||||
|
* a offset rows variable string
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #FETCH_FIRST_ROWS_ONLY(String)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T OFFSET_ROWS(String variable) {
|
||||||
|
sql().offset = variable;
|
||||||
|
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset rows value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* an offset rows value
|
||||||
|
*
|
||||||
|
* @return a self instance
|
||||||
|
*
|
||||||
|
* @see #FETCH_FIRST_ROWS_ONLY(int)
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T OFFSET_ROWS(long value) {
|
||||||
|
return OFFSET_ROWS(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to add a new inserted row while do multi-row insert.
|
||||||
|
*
|
||||||
|
* @return the t
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public T ADD_ROW() {
|
||||||
|
sql().valuesList.add(new ArrayList<>());
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SQLStatement sql() {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <A extends Appendable> A usingAppender(A a) {
|
||||||
|
sql().sql(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sql().sql(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SafeAppendable {
|
||||||
|
private final Appendable appendable;
|
||||||
|
private boolean empty = true;
|
||||||
|
|
||||||
|
public SafeAppendable(Appendable a) {
|
||||||
|
this.appendable = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeAppendable append(CharSequence s) {
|
||||||
|
try {
|
||||||
|
if (empty && s.length() > 0) {
|
||||||
|
empty = false;
|
||||||
|
}
|
||||||
|
appendable.append(s);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SQLStatement {
|
||||||
|
|
||||||
|
public enum StatementType {
|
||||||
|
|
||||||
|
DELETE,
|
||||||
|
|
||||||
|
INSERT,
|
||||||
|
|
||||||
|
SELECT,
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum LimitingRowsStrategy {
|
||||||
|
NOP {
|
||||||
|
@Override
|
||||||
|
protected void appendClause(SafeAppendable builder, String offset, String limit) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ISO {
|
||||||
|
@Override
|
||||||
|
protected void appendClause(SafeAppendable builder, String offset, String limit) {
|
||||||
|
if (offset != null) {
|
||||||
|
builder.append(" OFFSET ").append(offset).append(" ROWS");
|
||||||
|
}
|
||||||
|
if (limit != null) {
|
||||||
|
builder.append(" FETCH FIRST ").append(limit).append(" ROWS ONLY");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OFFSET_LIMIT {
|
||||||
|
@Override
|
||||||
|
protected void appendClause(SafeAppendable builder, String offset, String limit) {
|
||||||
|
if (limit != null) {
|
||||||
|
builder.append(" LIMIT ").append(limit);
|
||||||
|
}
|
||||||
|
if (offset != null) {
|
||||||
|
builder.append(" OFFSET ").append(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected abstract void appendClause(SafeAppendable builder, String offset, String limit);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StatementType statementType;
|
||||||
|
List<String> sets = new ArrayList<>();
|
||||||
|
List<String> select = new ArrayList<>();
|
||||||
|
List<String> tables = new ArrayList<>();
|
||||||
|
List<String> join = new ArrayList<>();
|
||||||
|
List<String> innerJoin = new ArrayList<>();
|
||||||
|
List<String> outerJoin = new ArrayList<>();
|
||||||
|
List<String> leftOuterJoin = new ArrayList<>();
|
||||||
|
List<String> rightOuterJoin = new ArrayList<>();
|
||||||
|
List<String> where = new ArrayList<>();
|
||||||
|
List<String> having = new ArrayList<>();
|
||||||
|
List<String> groupBy = new ArrayList<>();
|
||||||
|
List<String> orderBy = new ArrayList<>();
|
||||||
|
List<String> lastList = new ArrayList<>();
|
||||||
|
List<String> columns = new ArrayList<>();
|
||||||
|
List<List<String>> valuesList = new ArrayList<>();
|
||||||
|
boolean distinct;
|
||||||
|
String offset;
|
||||||
|
String limit;
|
||||||
|
LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP;
|
||||||
|
|
||||||
|
public SQLStatement() {
|
||||||
|
// Prevent Synthetic Access
|
||||||
|
valuesList.add(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
|
||||||
|
String conjunction) {
|
||||||
|
if (!parts.isEmpty()) {
|
||||||
|
if (!builder.isEmpty()) {
|
||||||
|
builder.append("\n");
|
||||||
|
}
|
||||||
|
builder.append(keyword);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(open);
|
||||||
|
String last = "________";
|
||||||
|
for (int i = 0, n = parts.size(); i < n; i++) {
|
||||||
|
String part = parts.get(i);
|
||||||
|
if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
|
||||||
|
builder.append(conjunction);
|
||||||
|
}
|
||||||
|
builder.append(part);
|
||||||
|
last = part;
|
||||||
|
}
|
||||||
|
builder.append(close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectSQL(SafeAppendable builder) {
|
||||||
|
if (distinct) {
|
||||||
|
sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
|
||||||
|
} else {
|
||||||
|
sqlClause(builder, "SELECT", select, "", "", ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlClause(builder, "FROM", tables, "", "", ", ");
|
||||||
|
joins(builder);
|
||||||
|
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
|
||||||
|
sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
|
||||||
|
sqlClause(builder, "HAVING", having, "(", ")", " AND ");
|
||||||
|
sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
|
||||||
|
limitingRowsStrategy.appendClause(builder, offset, limit);
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void joins(SafeAppendable builder) {
|
||||||
|
sqlClause(builder, "JOIN", join, "", "", "\nJOIN ");
|
||||||
|
sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
|
||||||
|
sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
|
||||||
|
sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
|
||||||
|
sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String insertSQL(SafeAppendable builder) {
|
||||||
|
sqlClause(builder, "INSERT INTO", tables, "", "", "");
|
||||||
|
sqlClause(builder, "", columns, "(", ")", ", ");
|
||||||
|
for (int i = 0; i < valuesList.size(); i++) {
|
||||||
|
sqlClause(builder, i > 0 ? "," : "VALUES", valuesList.get(i), "(", ")", ", ");
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String deleteSQL(SafeAppendable builder) {
|
||||||
|
sqlClause(builder, "DELETE FROM", tables, "", "", "");
|
||||||
|
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
|
||||||
|
limitingRowsStrategy.appendClause(builder, null, limit);
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String updateSQL(SafeAppendable builder) {
|
||||||
|
sqlClause(builder, "UPDATE", tables, "", "", "");
|
||||||
|
joins(builder);
|
||||||
|
sqlClause(builder, "SET", sets, "", "", ", ");
|
||||||
|
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
|
||||||
|
limitingRowsStrategy.appendClause(builder, null, limit);
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String sql(Appendable a) {
|
||||||
|
SafeAppendable builder = new SafeAppendable(a);
|
||||||
|
if (statementType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String answer;
|
||||||
|
|
||||||
|
switch (statementType) {
|
||||||
|
case DELETE:
|
||||||
|
answer = deleteSQL(builder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INSERT:
|
||||||
|
answer = insertSQL(builder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SELECT:
|
||||||
|
answer = selectSQL(builder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UPDATE:
|
||||||
|
answer = updateSQL(builder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
answer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package xyz.zhouxy.plusone.commons.exception;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
@Beta
|
||||||
|
public class DbException extends RuntimeException {
|
||||||
|
|
||||||
|
public DbException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
package xyz.zhouxy.plusone.commons.jdbc;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.ResultSetMetaData;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
import xyz.zhouxy.plusone.commons.util.DbRecord;
|
||||||
|
import xyz.zhouxy.plusone.commons.util.MoreCollections;
|
||||||
|
|
||||||
|
@Beta
|
||||||
|
public class JdbcUtil {
|
||||||
|
|
||||||
|
public static JdbcExecutor connect(Connection conn) {
|
||||||
|
return new JdbcExecutor(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JdbcUtil() {
|
||||||
|
throw new IllegalStateException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JdbcExecutor {
|
||||||
|
|
||||||
|
private final Connection conn;
|
||||||
|
|
||||||
|
public JdbcExecutor(Connection conn) {
|
||||||
|
this.conn = conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<T> query(String sql, Object[] params, ResultMap<T> resultMap) throws SQLException {
|
||||||
|
try (PreparedStatement stmt = this.conn.prepareStatement(sql)) {
|
||||||
|
if (params != null && params.length > 0) {
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
stmt.setObject(i + 1, params[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
while (rs.next()) {
|
||||||
|
T e = resultMap.map(rs);
|
||||||
|
result.add(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ResultMap<Map<String, Object>> mapResultMap = rs -> {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
ResultSetMetaData metaData = rs.getMetaData();
|
||||||
|
int columnCount = metaData.getColumnCount();
|
||||||
|
for (int i = 1; i <= columnCount; i++) {
|
||||||
|
String colName = metaData.getColumnName(i);
|
||||||
|
result.put(colName, rs.getObject(colName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<Map<String, Object>> query(String sql, Object... params) throws SQLException {
|
||||||
|
return query(sql, params, mapResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ResultMap<DbRecord> recordResultMap = rs -> {
|
||||||
|
DbRecord result = new DbRecord();
|
||||||
|
ResultSetMetaData metaData = rs.getMetaData();
|
||||||
|
int columnCount = metaData.getColumnCount();
|
||||||
|
for (int i = 1; i <= columnCount; i++) {
|
||||||
|
String colName = metaData.getColumnName(i);
|
||||||
|
result.put(colName, rs.getObject(colName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<DbRecord> queryToRecordList(String sql, Object... params) throws SQLException {
|
||||||
|
return query(sql, params, recordResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> queryToString(String sql, Object... params) throws SQLException {
|
||||||
|
List<String> result = query(sql, params, (ResultSet rs) -> rs.getString(1));
|
||||||
|
return MoreCollections.isNotEmpty(result) ? Optional.ofNullable(result.get(0)) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalInt queryToInt(String sql, Object... params) throws SQLException {
|
||||||
|
List<Integer> result = query(sql, params, (ResultSet rs) -> rs.getBigDecimal(1).intValue());
|
||||||
|
Integer i = MoreCollections.isNotEmpty(result) ? result.get(0) : null;
|
||||||
|
return i != null ? OptionalInt.of(i) : OptionalInt.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalLong queryToLong(String sql, Object... params) throws SQLException {
|
||||||
|
List<Long> result = query(sql, params, (ResultSet rs) -> rs.getBigDecimal(1).longValue());
|
||||||
|
Long i = MoreCollections.isNotEmpty(result) ? result.get(0) : null;
|
||||||
|
return i != null ? OptionalLong.of(i) : OptionalLong.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<BigDecimal> queryToBigDecimal(String sql, Object... params) throws SQLException {
|
||||||
|
List<BigDecimal> result = query(sql, params, (ResultSet rs) -> rs.getBigDecimal(1));
|
||||||
|
return MoreCollections.isNotEmpty(result) ? Optional.ofNullable(result.get(0)) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int update(String sql, Object... params) throws SQLException {
|
||||||
|
try (PreparedStatement stmt = this.conn.prepareStatement(sql)) {
|
||||||
|
if (params != null && params.length > 0) {
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
stmt.setObject(i + 1, params[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stmt.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] batchUpdate(String sql, Collection<Object[]> params, int batchSize) throws SQLException {
|
||||||
|
int[] result = {};
|
||||||
|
try (PreparedStatement stmt = this.conn.prepareStatement(sql)) {
|
||||||
|
int i = 0;
|
||||||
|
for (Object[] ps : params) {
|
||||||
|
i++;
|
||||||
|
for (int j = 0; j < ps.length; j++) {
|
||||||
|
stmt.setObject(j + 1, ps[j]);
|
||||||
|
}
|
||||||
|
stmt.addBatch();
|
||||||
|
if (i % batchSize == 0 || i >= params.size()) {
|
||||||
|
int[] n = stmt.executeBatch();
|
||||||
|
result = ArrayUtils.addAll(result, n);
|
||||||
|
stmt.clearBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package xyz.zhouxy.plusone.commons.jdbc;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
@Beta
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ResultMap<T> {
|
||||||
|
T map(ResultSet rs) throws SQLException;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package xyz.zhouxy.plusone.commons.jdbc;
|
||||||
|
|
||||||
|
import org.apache.ibatis.jdbc.AbstractSQL;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ZhouXY
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class SQL extends AbstractSQL<SQL> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQL getSelf() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQL WHERE_IF(boolean condition, String sqlConditions) {
|
||||||
|
if (condition) {
|
||||||
|
return WHERE(sqlConditions);
|
||||||
|
}
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package xyz.zhouxy.plusone.commons.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.OptionalDouble;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
@Beta
|
||||||
|
public class DbRecord extends AbstractMapWrapper<String, Object, DbRecord> {
|
||||||
|
|
||||||
|
public DbRecord() {
|
||||||
|
super(new HashMap<>(), k -> Assert.isNotBlank(k, "Key can not be null."), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbRecord(Map<String, Object> map) {
|
||||||
|
super(map, k -> Assert.isNotBlank(k, "Key can not be null."), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getValueAsString(String key) {
|
||||||
|
return this.<String>getAndConvert(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<T> getValueAsList(String key) {
|
||||||
|
return this.<Collection<T>>getAndConvert(key)
|
||||||
|
.<List<T>>map(l -> (l instanceof List) ? (List<T>) l : new ArrayList<>(l))
|
||||||
|
.orElse(Collections.<T>emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Set<T> getValueAsSet(String key) {
|
||||||
|
return this.<Collection<T>>getAndConvert(key)
|
||||||
|
.<Set<T>>map(l -> (l instanceof Set) ? (Set<T>) l : new HashSet<>(l))
|
||||||
|
.orElse(Collections.<T>emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalInt getValueAsInt(String key) {
|
||||||
|
return OptionalUtil.toOptionalInt(this.<Integer>getAndConvert(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalLong getValueAsLong(String key) {
|
||||||
|
return OptionalUtil.toOptionalLong(this.<Long>getAndConvert(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalDouble getValueAsDouble(String key) {
|
||||||
|
return OptionalUtil.toOptionalDouble(this.<Double>getAndConvert(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DbRecord getSelf() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package xyz.zhouxy.plusone.commons.util;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
|
||||||
|
import xyz.zhouxy.plusone.commons.jdbc.JdbcUtil;
|
||||||
|
|
||||||
|
class JdbcUtilTests {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(JdbcUtilTests.class);
|
||||||
|
|
||||||
|
static final String url = "jdbc:postgresql://localhost:5432/plusone";
|
||||||
|
static final String username = "postgres";
|
||||||
|
static final String password = "zhouxy108";
|
||||||
|
|
||||||
|
DataSource dataSource;
|
||||||
|
|
||||||
|
String[] cStruct = {
|
||||||
|
"id",
|
||||||
|
"created_by",
|
||||||
|
"create_time",
|
||||||
|
"updated_by",
|
||||||
|
"update_time",
|
||||||
|
"status"
|
||||||
|
};
|
||||||
|
|
||||||
|
JdbcUtilTests() {
|
||||||
|
HikariConfig config = new HikariConfig();
|
||||||
|
config.setJdbcUrl(url);
|
||||||
|
config.setUsername(username);
|
||||||
|
config.setPassword(password);
|
||||||
|
this.dataSource = new HikariDataSource(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQuery() throws SQLException {
|
||||||
|
Connection conn = this.dataSource.getConnection();
|
||||||
|
List<Map<String, Object>> ms = JdbcUtil.connect(conn).query(
|
||||||
|
"SELECT * FROM public.base_table WHERE id IN (?, ?, ?)", 501533, 501554, 544599);
|
||||||
|
assertNotNull(ms);
|
||||||
|
List<DbRecord> es = ms.stream()
|
||||||
|
.map(input -> new DbRecord().putAll(input))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertEquals(3, es.size());
|
||||||
|
for (DbRecord baseEntity : es) {
|
||||||
|
log.info("id: {}", baseEntity.getValueAsLong("id"));
|
||||||
|
assertNull(baseEntity.getValueAsString("updated_by"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue