Task: Add sql core features with based features and support query and custom parameters for general used with module

This commit is contained in:
Sambo Chea 2022-07-05 21:21:27 +07:00
commit 429c8ba243
Signed by: sombochea
GPG Key ID: 3C7CF22A05D95490
15 changed files with 594 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

8
README.md Normal file
View File

@ -0,0 +1,8 @@
# CUBETIQ DSM Core
- DataSource
- Core Context for DSM
# Contributors
- Sambo Chea <sombochea@cubetiqs.com>

10
build.gradle Normal file
View File

@ -0,0 +1,10 @@
plugins {
id 'java'
}
group 'com.cubetiqs'
version = '1.0.0'
sourceCompatibility = JavaVersion.VERSION_1_8
dependencies {}

View File

@ -0,0 +1,6 @@
package com.cubetiqs.mapper;
@FunctionalInterface
public interface MapperProvider<E, R> {
R transformTo(E element, Class<R> clazz);
}

View File

@ -0,0 +1,9 @@
package com.cubetiqs.mapper;
import java.sql.ResultSet;
import java.sql.SQLException;
@FunctionalInterface
public interface RowMapperProvider<T> {
T map(ResultSet rs, int rowNum) throws SQLException;
}

View File

@ -0,0 +1,44 @@
package com.cubetiqs.sql;
import com.cubetiqs.mapper.MapperProvider;
import java.util.List;
import java.util.Map;
public final class DataFactory {
public static IExecuteResult<List<Object>> queryForList(JdbcDataQuery manager, String sql) {
return manager.queryForList(sql);
}
public static IExecuteResult<List<Object>> queryForList(JdbcDataQuery manager, String sql, Object... args) {
return manager.queryForList(sql, args);
}
public static IExecuteResult<Object> queryForObject(JdbcDataQuery manager, String sql) {
return manager.queryForObject(sql);
}
public static IExecuteResult<Object> queryForObject(JdbcDataQuery manager, String sql, Object... args) {
return manager.queryForObject(sql, args);
}
public static <R> IExecuteResult<R> query(JdbcDataQuery manager, MapperProvider<Map<String, Object>, R> mapper, String sql, Class<R> clazz) {
return manager.query(mapper, sql, clazz);
}
public static <R> IExecuteResult<R> query(JdbcDataQuery manager, MapperProvider<Map<String, Object>, R> mapper, String sql, Class<R> clazz, Object... args) {
return manager.query(mapper, sql, clazz, args);
}
public static IExecuteResult<List<Object>> queryForList(JdbcDataQuery manager, ISqlMapParameter parameter) {
return manager.queryForList(parameter);
}
public static IExecuteResult<Object> queryForObject(JdbcDataQuery manager, ISqlMapParameter parameter) {
return manager.queryForObject(parameter);
}
public static <R> IExecuteResult<R> query(JdbcDataQuery manager, MapperProvider<Map<String, Object>, R> mapper, ISqlMapParameter parameter, Class<R> clazz) {
return manager.query(mapper, parameter, clazz);
}
}

View File

@ -0,0 +1,68 @@
package com.cubetiqs.sql;
import java.util.Date;
public class ExecuteResult<R> implements IExecuteResult<R> {
private R data;
private Throwable error;
public ExecuteResult() {
}
public ExecuteResult(R data, Throwable error) {
this.data = data;
this.error = error;
}
private final Date start = new Date();
public void setData(R data) {
this.data = data;
}
public void setError(Throwable error) {
this.error = error;
}
@Override
public R getData() {
return data;
}
@Override
public Throwable getError() {
return error;
}
@Override
public long getDuration() {
return new Date().getTime() - start.getTime();
}
public static ExecuteResult<Object> createEmpty() {
return builder().build();
}
public static class ExecuteResultBuilder<R> {
private R data;
private Throwable error;
public ExecuteResultBuilder<R> data(R data) {
this.data = data;
return this;
}
public ExecuteResultBuilder<R> error(Throwable error) {
this.error = error;
return this;
}
public ExecuteResult<R> build() {
return new ExecuteResult<>(data, error);
}
}
public static <R> ExecuteResultBuilder<R> builder() {
return new ExecuteResultBuilder<>();
}
}

View File

@ -0,0 +1,7 @@
package com.cubetiqs.sql;
import java.sql.Connection;
public interface IDataManager {
Connection getConnection();
}

View File

@ -0,0 +1,13 @@
package com.cubetiqs.sql;
public interface IExecuteResult<R> {
R getData();
default Throwable getError() {
return null;
}
default long getDuration() {
return 0L;
}
}

View File

@ -0,0 +1,19 @@
package com.cubetiqs.sql;
import java.util.Map;
public interface ISqlMapParameter {
String getSql();
default String getFormatSql() {
return getSql();
}
default Map<String, Object> getParams() {
return null;
}
default Object[] getSqlArgs() {
return null;
}
}

View File

@ -0,0 +1,5 @@
package com.cubetiqs.sql;
public interface JdbcAccessor {
IDataManager getManager();
}

View File

@ -0,0 +1,61 @@
package com.cubetiqs.sql;
import com.cubetiqs.mapper.MapperProvider;
import com.cubetiqs.mapper.RowMapperProvider;
import java.util.List;
import java.util.Map;
public interface JdbcDataQuery extends JdbcAccessor {
IExecuteResult<List<Object>> queryForList(String sql);
IExecuteResult<List<Object>> queryForList(String sql, Object... args);
IExecuteResult<Object> queryForObject(String sql);
IExecuteResult<Object> queryForObject(String sql, Object... args);
<R> IExecuteResult<R> query(MapperProvider<Map<String, Object>, R> mapper, String sql, Class<R> clazz);
<R> IExecuteResult<R> query(MapperProvider<Map<String, Object>, R> mapper, String sql, Class<R> clazz, Object... args);
<R> IExecuteResult<R> query(String sql, RowMapperProvider<R> mapper);
<R> IExecuteResult<R> query(String sql, RowMapperProvider<R> mapper, Object... args);
default IExecuteResult<List<Object>> queryForList(ISqlMapParameter parameter) {
if (parameter.getParams().size() == 0) {
return queryForList(parameter.getSql());
}
Object[] args = parameter.getSqlArgs();
return queryForList(parameter.getFormatSql(), args);
}
default IExecuteResult<Object> queryForObject(ISqlMapParameter parameter) {
if (parameter.getParams().size() == 0) {
return queryForObject(parameter.getSql());
}
Object[] args = parameter.getSqlArgs();
return queryForObject(parameter.getFormatSql(), args);
}
default <R> IExecuteResult<R> query(MapperProvider<Map<String, Object>, R> mapper, ISqlMapParameter parameter, Class<R> clazz) {
if (parameter.getParams().size() == 0) {
return query(mapper, parameter.getSql(), clazz);
}
Object[] args = parameter.getSqlArgs();
return query(mapper, parameter.getFormatSql(), clazz, args);
}
default <R> IExecuteResult<R> query(ISqlMapParameter parameter, RowMapperProvider<R> mapper) {
if (parameter.getParams().size() == 0) {
return query(parameter.getSql(), mapper);
}
Object[] args = parameter.getSqlArgs();
return query(parameter.getFormatSql(), mapper, args);
}
}

View File

@ -0,0 +1,115 @@
package com.cubetiqs.sql;
import com.cubetiqs.mapper.MapperProvider;
import com.cubetiqs.mapper.RowMapperProvider;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
public class JdbcDataQueryImpl implements JdbcDataQuery {
private final IDataManager manager;
public JdbcDataQueryImpl(IDataManager manager) {
this.manager = manager;
}
@Override
public IDataManager getManager() {
return manager;
}
@Override
public IExecuteResult<List<Object>> queryForList(String sql) {
ExecuteResult.ExecuteResultBuilder<List<Object>> builder = ExecuteResult.builder();
try (Statement statement = manager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
builder.data(ResultSetUtil.getDataAsList(statement.executeQuery(sql)));
} catch (SQLException ex) {
builder.error(ex);
}
return builder.build();
}
@Override
public IExecuteResult<List<Object>> queryForList(String sql, Object... args) {
ExecuteResult.ExecuteResultBuilder<List<Object>> builder = ExecuteResult.builder();
try (PreparedStatement statement = manager.getConnection().prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
ResultSetUtil.applyParameterized(statement, sql, args);
builder.data(ResultSetUtil.getDataAsList(statement.executeQuery()));
} catch (SQLException ex) {
builder.error(ex);
}
return builder.build();
}
@Override
public IExecuteResult<Object> queryForObject(String sql) {
ExecuteResult.ExecuteResultBuilder<Object> builder = ExecuteResult.builder();
try (Statement statement = manager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
builder.data(ResultSetUtil.getDataAsObject(statement.executeQuery(sql)));
} catch (SQLException ex) {
builder.error(ex);
}
return builder.build();
}
@Override
public IExecuteResult<Object> queryForObject(String sql, Object... args) {
ExecuteResult.ExecuteResultBuilder<Object> builder = ExecuteResult.builder();
try (PreparedStatement statement = manager.getConnection().prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
ResultSetUtil.applyParameterized(statement, sql, args);
builder.data(ResultSetUtil.getDataAsObject(statement.executeQuery()));
} catch (SQLException ex) {
builder.error(ex);
}
return builder.build();
}
@Override
public <R> IExecuteResult<R> query(MapperProvider<Map<String, Object>, R> mapper, String sql, Class<R> clazz) {
ExecuteResult.ExecuteResultBuilder<R> builder = ExecuteResult.builder();
try (Statement statement = manager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
builder.data(ResultSetUtil.getDataAsType(mapper, statement.executeQuery(sql), clazz));
} catch (SQLException ex) {
builder.error(ex);
}
return builder.build();
}
@Override
public <R> IExecuteResult<R> query(MapperProvider<Map<String, Object>, R> mapper, String sql, Class<R> clazz, Object... args) {
ExecuteResult.ExecuteResultBuilder<R> builder = ExecuteResult.builder();
try (PreparedStatement statement = manager.getConnection().prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
ResultSetUtil.applyParameterized(statement, sql, args);
builder.data(ResultSetUtil.getDataAsType(mapper, statement.executeQuery(), clazz));
} catch (SQLException ex) {
builder.error(ex);
}
return builder.build();
}
@Override
public <R> IExecuteResult<R> query(String sql, RowMapperProvider<R> mapper) {
return null;
}
@Override
public <R> IExecuteResult<R> query(String sql, RowMapperProvider<R> mapper, Object... args) {
return null;
}
}

View File

@ -0,0 +1,150 @@
package com.cubetiqs.sql;
import com.cubetiqs.mapper.MapperProvider;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
import static java.util.regex.Pattern.compile;
public final class ResultSetUtil {
public static void applyParameterized(PreparedStatement statement, String sql, Object... args) throws SQLException {
if (args != null && sql.indexOf('?') != -1) {
for (int index = 0; index < args.length; index++) {
statement.setObject(index + 1, args[index]);
}
}
}
public static int getColumnCount(ResultSet rs) throws SQLException {
return rs.getMetaData().getColumnCount();
}
public static List<String> getColumns(ResultSet rs) throws SQLException {
List<String> columns = new ArrayList<>();
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
IntStream.range(0, columnCount).forEach(i -> {
try {
columns.add(metaData.getColumnName(i + 1));
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
return columns;
}
public static List<Object> getDataAsList(ResultSet rs) throws SQLException {
List<Object> rows = new ArrayList<>();
List<String> columns = ResultSetUtil.getColumns(rs);
while (rs.next()) {
Map<String, Object> row = new HashMap<>();
for (String column : columns) {
row.put(column, rs.getObject(column));
}
rows.add(row);
}
return rows;
}
public static Object getDataAsObject(ResultSet rs) throws SQLException {
if (!rs.next()) {
return null;
}
List<String> columns = getColumns(rs);
if (columns.size() == 0) {
return null;
}
if (columns.size() == 1) {
return rs.getObject(columns.get(0));
} else {
Map<String, Object> row = new HashMap<>();
for (String column : columns) {
row.put(column, rs.getObject(column));
}
return row;
}
}
@SuppressWarnings("unchecked")
public static <R> R getDataAsType(MapperProvider<Map<String, Object>, R> mapper, ResultSet rs, Class<R> clazz) throws SQLException {
if (!rs.next()) {
return null;
}
List<String> columns = getColumns(rs);
if (columns.size() == 0) {
return null;
}
if (columns.size() == 1) {
String column = columns.get(0);
if (clazz.isInstance(String.class)) {
return (R) rs.getString(column);
} else if (clazz.isInstance(Integer.class)) {
return (R) Integer.valueOf(rs.getInt(column));
} else if (clazz.isInstance(Boolean.class)) {
return (R) Boolean.valueOf(rs.getBoolean(column));
} else if (clazz.isInstance(Date.class)) {
return (R) rs.getDate(column);
} else if (clazz.isInstance(Double.class)) {
return (R) Double.valueOf(rs.getDouble(column));
} else {
return (R) rs.getObject(column);
}
} else {
Map<String, Object> row = new HashMap<>();
for (String column : columns) {
row.put(column, rs.getObject(column));
}
return mapper.transformTo(row, clazz);
}
}
private static CharSequence escapeRegex(CharSequence text) {
if (text == null) return null;
return Pattern.quote(text + "");
}
public static List<String> findValues(String template, CharSequence sequence1, CharSequence sequence2) {
List<String> data = new ArrayList<>();
Pattern pattern;
if (sequence1 != null) {
if (sequence2 != null) {
pattern = compile(escapeRegex(sequence1) + "(.*?)" + escapeRegex(sequence2));
} else {
pattern = compile(escapeRegex(sequence1) + "([A-Za-z\\d]*)");
}
} else {
return null;
}
Matcher matcher = pattern.matcher(template);
while (matcher.find()) {
data.add(matcher.group(1));
}
return data;
}
public static String replacerPrefix(String template, Map<String, Object> replacement, CharSequence prefix) {
AtomicReference<String> message = new AtomicReference<>(template);
replacement.forEach((key, value) -> message.set(message.get().replace(prefix + key, value.toString())));
return message.get();
}
}

View File

@ -0,0 +1,78 @@
package com.cubetiqs.sql;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SqlMapParameter implements ISqlMapParameter {
private final String sql;
private final Map<String, Object> params = new HashMap<>();
// Replace all ":variableName" with "?"
private String formatSql;
public SqlMapParameter(String sql) {
this.sql = sql;
}
public SqlMapParameter addParam(String key, Object value) {
params.put(key, value);
return this;
}
public SqlMapParameter addParams(Map<String, Object> values) {
params.putAll(values);
return this;
}
@Override
public Map<String, Object> getParams() {
return params;
}
@Override
public String getSql() {
return sql;
}
@Override
public String getFormatSql() {
return formatSql;
}
@Override
public Object[] getSqlArgs() {
if (params.size() == 0) {
return null;
}
if (sql == null || sql.equals("")) {
return null;
}
List<String> keys = ResultSetUtil.findValues(sql, ":", null);
if (keys.size() > 1 && params.size() == 0) {
System.out.println("Keys = " + keys);
System.out.println("Params = " + params);
throw new IllegalArgumentException("Parameter not matched with keys size!");
}
Object[] args = new Object[keys.size()];
for (int i = 0; i < args.length; i++) {
args[i] = params.get(keys.get(i));
}
Map<String, Object> replacement = new HashMap<>();
for (String key : keys) {
replacement.put(key, "?");
}
formatSql = ResultSetUtil.replacerPrefix(sql, replacement, ":");
return args;
}
public static SqlMapParameter create(String sql) {
return new SqlMapParameter(sql);
}
}