From fa3f4695f1917ab60f08ff0f1cdd9db4b69f4d1c Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Tue, 9 May 2023 01:05:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=AE=80=E5=8D=95=E7=9A=84?= =?UTF-8?q?=20JdbcUtil=EF=BC=8C=E5=B9=B6=E5=80=9F=E5=8A=A9=20MyBatis=20?= =?UTF-8?q?=E7=9A=84=20SQL=20=E6=9E=84=E5=BB=BA=E5=99=A8=E7=AE=80=E5=8C=96?= =?UTF-8?q?=20SQL=20=E7=9A=84=E6=8B=BC=E6=8E=A5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 13 + .../org/apache/ibatis/jdbc/AbstractSQL.java | 740 ++++++++++++++++++ .../commons/exception/DbException.java | 19 + .../zhouxy/plusone/commons/jdbc/JdbcUtil.java | 145 ++++ .../plusone/commons/jdbc/ResultMap.java | 12 + .../xyz/zhouxy/plusone/commons/jdbc/SQL.java | 25 + .../zhouxy/plusone/commons/util/DbRecord.java | 61 ++ .../plusone/commons/util/JdbcUtilTests.java | 66 ++ 8 files changed, 1081 insertions(+) create mode 100644 src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/exception/DbException.java create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/jdbc/JdbcUtil.java create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/jdbc/ResultMap.java create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/jdbc/SQL.java create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/util/DbRecord.java create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/util/JdbcUtilTests.java diff --git a/pom.xml b/pom.xml index dc4a1ae..3bbbf23 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,19 @@ 5.9.2 test + + + com.zaxxer + HikariCP + 4.0.3 + test + + + org.postgresql + postgresql + 42.3.8 + test + diff --git a/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java b/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java new file mode 100644 index 0000000..6e031c4 --- /dev/null +++ b/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java @@ -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 { + + 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 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 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 sets = new ArrayList<>(); + List select = new ArrayList<>(); + List tables = new ArrayList<>(); + List join = new ArrayList<>(); + List innerJoin = new ArrayList<>(); + List outerJoin = new ArrayList<>(); + List leftOuterJoin = new ArrayList<>(); + List rightOuterJoin = new ArrayList<>(); + List where = new ArrayList<>(); + List having = new ArrayList<>(); + List groupBy = new ArrayList<>(); + List orderBy = new ArrayList<>(); + List lastList = new ArrayList<>(); + List columns = new ArrayList<>(); + List> 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 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; + } + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/DbException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/DbException.java new file mode 100644 index 0000000..67b3ecb --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/DbException.java @@ -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); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/jdbc/JdbcUtil.java b/src/main/java/xyz/zhouxy/plusone/commons/jdbc/JdbcUtil.java new file mode 100644 index 0000000..0eced64 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/jdbc/JdbcUtil.java @@ -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 List query(String sql, Object[] params, ResultMap 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 result = new ArrayList<>(); + while (rs.next()) { + T e = resultMap.map(rs); + result.add(e); + } + return result; + } + } + } + + public static final ResultMap> mapResultMap = rs -> { + Map 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> query(String sql, Object... params) throws SQLException { + return query(sql, params, mapResultMap); + } + + public static final ResultMap 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 queryToRecordList(String sql, Object... params) throws SQLException { + return query(sql, params, recordResultMap); + } + + public Optional queryToString(String sql, Object... params) throws SQLException { + List 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 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 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 queryToBigDecimal(String sql, Object... params) throws SQLException { + List 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 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; + } + } + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/jdbc/ResultMap.java b/src/main/java/xyz/zhouxy/plusone/commons/jdbc/ResultMap.java new file mode 100644 index 0000000..126a56b --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/jdbc/ResultMap.java @@ -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 map(ResultSet rs) throws SQLException; +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/jdbc/SQL.java b/src/main/java/xyz/zhouxy/plusone/commons/jdbc/SQL.java new file mode 100644 index 0000000..da172f2 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/jdbc/SQL.java @@ -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 { + + @Override + public SQL getSelf() { + return this; + } + + public SQL WHERE_IF(boolean condition, String sqlConditions) { + if (condition) { + return WHERE(sqlConditions); + } + return getSelf(); + } + +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/DbRecord.java b/src/main/java/xyz/zhouxy/plusone/commons/util/DbRecord.java new file mode 100644 index 0000000..9507546 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/DbRecord.java @@ -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 { + + public DbRecord() { + super(new HashMap<>(), k -> Assert.isNotBlank(k, "Key can not be null."), null); + } + + public DbRecord(Map map) { + super(map, k -> Assert.isNotBlank(k, "Key can not be null."), null); + } + + public Optional getValueAsString(String key) { + return this.getAndConvert(key); + } + + public List getValueAsList(String key) { + return this.>getAndConvert(key) + .>map(l -> (l instanceof List) ? (List) l : new ArrayList<>(l)) + .orElse(Collections.emptyList()); + } + + public Set getValueAsSet(String key) { + return this.>getAndConvert(key) + .>map(l -> (l instanceof Set) ? (Set) l : new HashSet<>(l)) + .orElse(Collections.emptySet()); + } + + public OptionalInt getValueAsInt(String key) { + return OptionalUtil.toOptionalInt(this.getAndConvert(key)); + } + + public OptionalLong getValueAsLong(String key) { + return OptionalUtil.toOptionalLong(this.getAndConvert(key)); + } + + public OptionalDouble getValueAsDouble(String key) { + return OptionalUtil.toOptionalDouble(this.getAndConvert(key)); + } + + @Override + protected DbRecord getSelf() { + return this; + } +} diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/JdbcUtilTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/JdbcUtilTests.java new file mode 100644 index 0000000..f8862e0 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/JdbcUtilTests.java @@ -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> ms = JdbcUtil.connect(conn).query( + "SELECT * FROM public.base_table WHERE id IN (?, ?, ?)", 501533, 501554, 544599); + assertNotNull(ms); + List 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")); + } + } +}