重构SqlDataSet,使其支持结果值类型转换,并对SQL类型有更好的兼容性,解决不支持PostgresSQL的[SELECT 'aaa'

as NAME]语句的BUG
This commit is contained in:
datagear 2020-08-31 22:05:11 +08:00
parent e0fbcafd86
commit 695ec6c2ab
3 changed files with 360 additions and 537 deletions

View File

@ -9,20 +9,28 @@ package org.datagear.analysis.support;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetProperty.DataType;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvableDataSet;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.util.JDBCCompatiblity;
import org.datagear.util.JdbcSupport;
import org.datagear.util.JdbcUtil;
import org.datagear.util.QueryResultSet;
import org.datagear.util.Sql;
import org.datagear.util.SqlType;
import org.datagear.util.resource.ConnectionFactory;
/**
@ -36,7 +44,7 @@ import org.datagear.util.resource.ConnectionFactory;
*/
public class SqlDataSet extends AbstractFmkTemplateDataSet implements ResolvableDataSet
{
protected static final SqlDataSetSupport SQL_DATA_SET_SUPPORT = new SqlDataSetSupport();
protected static final JdbcSupport JDBC_SUPPORT = new JdbcSupport();
private ConnectionFactory connectionFactory;
@ -91,17 +99,18 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
if (properties == null || properties.isEmpty())
throw new DataSetException("[getProperties()] must not be empty");
ResolvedDataSetResult result = getResolvedDataSetResult(paramValues, properties);
ResolvedDataSetResult result = resolveResult(paramValues, properties);
return result.getResult();
}
@Override
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
{
return getResolvedDataSetResult(paramValues, null);
return resolveResult(paramValues, null);
}
/**
* 解析结果
*
* @param paramValues
* @param properties
@ -109,8 +118,8 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
* @return
* @throws DataSetException
*/
protected TemplateResolvedDataSetResult getResolvedDataSetResult(Map<String, ?> paramValues,
List<DataSetProperty> properties) throws DataSetException
protected TemplateResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException
{
String sql = resolveTemplate(getSql(), paramValues);
@ -120,32 +129,42 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
{
cn = getConnectionFactory().get();
}
catch (Exception e)
catch (Throwable t)
{
JdbcUtil.closeConnection(cn);
throw new SqlDataSetConnectionException(e);
throw new SqlDataSetConnectionException(t);
}
Sql sqlObj = Sql.valueOf(sql);
JdbcSupport jdbcSupport = getJdbcSupport();
QueryResultSet qrs = null;
try
{
qrs = getSqlDataSetSupport().executeQuery(cn, sqlObj, ResultSet.TYPE_FORWARD_ONLY);
qrs = jdbcSupport.executeQuery(cn, sqlObj, ResultSet.TYPE_FORWARD_ONLY);
}
catch (Throwable t)
{
throw new SqlDataSetSqlExecutionException(sql, t);
}
try
{
ResultSet rs = qrs.getResultSet();
if (properties == null || properties.isEmpty())
properties = getSqlDataSetSupport().resolveDataSetProperties(cn, rs, null);
ResolvedDataSetResult result = resolveResult(cn, rs, properties);
List<Map<String, ?>> data = getSqlDataSetSupport().resolveResultData(cn, rs, properties);
DataSetResult result = new DataSetResult(data);
return new TemplateResolvedDataSetResult(result, properties, sql);
return new TemplateResolvedDataSetResult(result.getResult(), result.getProperties(), sql);
}
catch (SQLException e)
catch (DataSetException e)
{
throw new SqlDataSetSqlExecutionException(sql, e);
throw e;
}
catch (Throwable t)
{
throw new DataSetException(t);
}
finally
{
@ -167,46 +186,163 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
}
/**
* 解析结果
*
* @param cn
* @param sql
* @param rs
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws DataSetException
* @throws Throwable
*/
protected ResolvedDataSetResult getResolvedDataSetResult(Connection cn, String sql,
List<DataSetProperty> properties) throws DataSetException
protected ResolvedDataSetResult resolveResult(Connection cn, ResultSet rs, List<DataSetProperty> properties)
throws Throwable
{
Sql sqlObj = Sql.valueOf(sql);
boolean resolveProperties = (properties == null || properties.isEmpty());
QueryResultSet qrs = null;
List<Map<String, ?>> datas = new ArrayList<>();
try
JdbcSupport jdbcSupport = getJdbcSupport();
DataSetPropertyValueConverter converter = createDataSetPropertyValueConverter();
ResultSetMetaData rsMeta = rs.getMetaData();
String[] colNames = jdbcSupport.getColumnNames(rsMeta);
SqlType[] sqlTypes = jdbcSupport.getColumnSqlTypes(rsMeta);
if (resolveProperties)
{
qrs = getSqlDataSetSupport().executeQuery(cn, sqlObj, ResultSet.TYPE_FORWARD_ONLY);
ResultSet rs = qrs.getResultSet();
if (properties == null || properties.isEmpty())
properties = getSqlDataSetSupport().resolveDataSetProperties(cn, rs, null);
List<Map<String, ?>> data = getSqlDataSetSupport().resolveResultData(cn, rs, properties);
DataSetResult result = new DataSetResult(data);
return new ResolvedDataSetResult(result, properties);
properties = new ArrayList<>(colNames.length);
for (int i = 0; i < colNames.length; i++)
properties.add(new DataSetProperty(colNames[i], toPropertyDataType(sqlTypes[i], colNames[i])));
}
catch (SQLException e)
int maxColumnSize = Math.min(colNames.length, properties.size());
int rowIdx = 0;
while (rs.next())
{
throw new SqlDataSetSqlExecutionException(sql, e);
}
finally
{
QueryResultSet.close(qrs);
Map<String, Object> row = new HashMap<>();
for (int i = 0; i < maxColumnSize; i++)
{
DataSetProperty property = properties.get(i);
Object value = jdbcSupport.getColumnValue(cn, rs, colNames[i], sqlTypes[i].getType());
if (resolveProperties && rowIdx == 0)
{
@JDBCCompatiblity("某些驱动程序可能存在一种情况列类型是toPropertyDataType()无法识别的,但是实际值是允许的,"
+ "比如PostgreSQL-42.2.5驱动对于[SELECT 'aaa' as NAME]语句结果的SQL类型是Types.OTHER但实际值是允许的字符串")
boolean resolveTypeByValue = (DataType.UNKNOWN.equals(property.getType()));
if (resolveTypeByValue)
property.setType(resolveDataType(value));
}
value = convertToPropertyDataType(converter, value, property);
row.put(property.getName(), value);
}
datas.add(row);
rowIdx++;
}
DataSetResult result = new DataSetResult(datas);
return new ResolvedDataSetResult(result, properties);
}
protected SqlDataSetSupport getSqlDataSetSupport()
/**
* 由SQL类型转换为{@linkplain DataSetProperty#getType()}
*
* @param sqlType
* @param columnName
* 允许为{@code null}列名称
* @return
* @throws SQLException
* @throws SqlDataSetUnsupportedSqlTypeException
*/
public String toPropertyDataType(SqlType sqlType, String columnName)
throws SQLException, SqlDataSetUnsupportedSqlTypeException
{
return SQL_DATA_SET_SUPPORT;
String dataType = null;
int type = sqlType.getType();
switch (type)
{
// 确定不支持的类型
case Types.BINARY:
case Types.BLOB:
case Types.LONGVARBINARY:
case Types.VARBINARY:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType, columnName);
case Types.CHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
dataType = DataType.STRING;
break;
}
case Types.BOOLEAN:
{
dataType = DataType.BOOLEAN;
break;
}
case Types.BIGINT:
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
dataType = DataType.INTEGER;
break;
}
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.NUMERIC:
case Types.REAL:
{
dataType = DataType.DECIMAL;
break;
}
case Types.DATE:
{
dataType = DataType.DATE;
break;
}
case Types.TIME:
{
dataType = DataType.TIME;
break;
}
case Types.TIMESTAMP:
{
dataType = DataType.TIMESTAMP;
break;
}
default:
dataType = DataType.UNKNOWN;
}
return dataType;
}
protected JdbcSupport getJdbcSupport()
{
return JDBC_SUPPORT;
}
}

View File

@ -1,433 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetProperty.DataType;
import org.datagear.util.JdbcSupport;
import org.datagear.util.SqlType;
/**
* SQL数据集支持类
*
* @author datagear@163.com
*
*/
public class SqlDataSetSupport extends JdbcSupport
{
public SqlDataSetSupport()
{
super();
}
/**
* 解析结果数据
*
* @param cn
* @param rs
* @param properties
* @return
* @throws SQLException
*/
public List<Map<String, ?>> resolveResultData(Connection cn, ResultSet rs, List<DataSetProperty> properties)
throws SQLException
{
List<Map<String, ?>> datas = new ArrayList<>();
ResultSetMetaData rsMeta = rs.getMetaData();
int[] rsColumns = resolveResultsetColumns(properties, rsMeta);
while (rs.next())
{
Map<String, Object> row = new HashMap<>();
for (int i = 0; i < rsColumns.length; i++)
{
DataSetProperty property = properties.get(i);
int rsColumn = rsColumns[i];
Object value = resolvePropertyDataValue(cn, rs, rsColumn, getColumnSqlType(rsMeta, rsColumn),
property.getType());
row.put(property.getName(), value);
}
datas.add(row);
}
return datas;
}
/**
* 解析结果集中对应{@linkplain DataSetProperty}的索引数组
*
* @param properties
* @param rsMeta
* @return
* @throws SQLException
* @throws DataSetException
*/
public int[] resolveResultsetColumns(List<DataSetProperty> properties, ResultSetMetaData rsMeta)
throws SQLException, DataSetException
{
int[] columns = new int[properties.size()];
int rsColumnCount = rsMeta.getColumnCount();
for (int i = 0; i < columns.length; i++)
{
String pname = properties.get(i).getName();
int myIndex = -1;
for (int j = 1; j <= rsColumnCount; j++)
{
if (pname.equalsIgnoreCase(getColumnName(rsMeta, j)))
{
myIndex = j;
break;
}
}
if (myIndex <= 0)
throw new DataSetException(
"Column named '" + pname + "' not found in the " + ResultSet.class.getSimpleName());
columns[i] = myIndex;
}
return columns;
}
/**
* 解析数据值
*
* @param cn
* @param rs
* @param column
* @param sqlType
* @param dataType
* @return
* @throws SQLException
*/
public Object resolvePropertyDataValue(Connection cn, ResultSet rs, int column, SqlType sqlType, String dataType)
throws SQLException
{
Object value = null;
int type = sqlType.getType();
if (DataType.isString(dataType))
{
switch (type)
{
case Types.CHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
value = rs.getString(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isBoolean(dataType))
{
switch (type)
{
case Types.BIT:
case Types.BIGINT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
value = (rs.getInt(column) > 0);
break;
}
case Types.BOOLEAN:
{
value = rs.getBoolean(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isInteger(dataType))
{
switch (type)
{
case Types.BIGINT:
{
value = rs.getLong(column);
break;
}
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
value = rs.getInt(column);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
{
value = rs.getBigDecimal(column).toBigInteger();
break;
}
case Types.DOUBLE:
{
value = new Double(rs.getDouble(column)).longValue();
break;
}
case Types.FLOAT:
case Types.REAL:
{
value = new Float(rs.getFloat(column)).longValue();
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isDecimal(dataType))
{
switch (type)
{
case Types.BIGINT:
{
value = rs.getLong(column);
break;
}
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
value = rs.getInt(column);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
{
value = rs.getBigDecimal(column);
break;
}
case Types.DOUBLE:
{
value = rs.getDouble(column);
break;
}
case Types.FLOAT:
case Types.REAL:
{
value = rs.getFloat(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isDate(dataType))
{
switch (type)
{
case Types.DATE:
{
value = rs.getDate(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isTime(dataType))
{
switch (type)
{
case Types.TIME:
{
value = rs.getTime(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isTimestamp(dataType))
{
switch (type)
{
case Types.TIMESTAMP:
{
value = rs.getTimestamp(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else
throw new UnsupportedOperationException();
if (rs.wasNull())
value = null;
return value;
}
/**
* 解析{@linkplain DataSetProperty}列表
*
* @param cn
* @param rs
* @param labels
* {@linkplain DataSetProperty#getLabel()}数组允许为{@code null}或任意长度的数组
* @return
* @throws SQLException
*/
public List<DataSetProperty> resolveDataSetProperties(Connection cn, ResultSet rs, String[] labels)
throws SQLException
{
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
List<DataSetProperty> properties = new ArrayList<>(columnCount);
for (int i = 1; i <= columnCount; i++)
{
String columnName = getColumnName(metaData, i);
SqlType sqlType = getColumnSqlType(metaData, i);
String dataType = toPropertyDataType(sqlType, columnName);
DataSetProperty property = createDataSetProperty();
property.setName(columnName);
property.setType(dataType);
if (labels != null && labels.length > i - 1)
property.setLabel(labels[i - 1]);
properties.add(property);
}
return properties;
}
/**
* 由SQL类型转换为{@linkplain DataSetProperty#getType()}
*
* @param sqlType
* @param columnName
* 允许为{@code null}列名称
* @return
* @throws SQLException
*/
public String toPropertyDataType(SqlType sqlType, String columnName) throws SQLException
{
String dataType = null;
int type = sqlType.getType();
switch (type)
{
case Types.CHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
dataType = DataType.STRING;
break;
}
case Types.BOOLEAN:
{
dataType = DataType.BOOLEAN;
break;
}
case Types.BIGINT:
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
dataType = DataType.INTEGER;
break;
}
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.NUMERIC:
case Types.REAL:
{
dataType = DataType.DECIMAL;
break;
}
case Types.DATE:
{
dataType = DataType.DATE;
break;
}
case Types.TIME:
{
dataType = DataType.TIME;
break;
}
case Types.TIMESTAMP:
{
dataType = DataType.TIMESTAMP;
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType, columnName);
}
return dataType;
}
protected DataSetProperty createDataSetProperty()
{
return new DataSetProperty();
}
}

View File

@ -950,27 +950,16 @@ public class JdbcSupport
* @return
* @throws SQLException
*/
@JDBCCompatiblity("某些驱动程序可能不支持ResultSet.getObject方法所以这里没有使用")
public Object getColumnValue(Connection cn, ResultSet rs, String columnName, int sqlType) throws SQLException
{
Object value = null;
switch (sqlType)
{
case Types.TINYINT:
case Types.ARRAY:
{
value = rs.getByte(columnName);
break;
}
case Types.SMALLINT:
{
value = rs.getShort(columnName);
break;
}
case Types.INTEGER:
{
value = rs.getInt(columnName);
value = rs.getArray(columnName);
break;
}
@ -980,23 +969,9 @@ public class JdbcSupport
break;
}
case Types.REAL:
case Types.BINARY:
{
value = rs.getFloat(columnName);
break;
}
case Types.FLOAT:
case Types.DOUBLE:
{
value = rs.getDouble(columnName);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
{
value = rs.getBigDecimal(columnName);
value = rs.getBytes(columnName);
break;
}
@ -1006,6 +981,12 @@ public class JdbcSupport
break;
}
case Types.BLOB:
{
value = rs.getBlob(columnName);
break;
}
case Types.BOOLEAN:
{
value = rs.getBoolean(columnName);
@ -1013,22 +994,68 @@ public class JdbcSupport
}
case Types.CHAR:
case Types.VARCHAR:
{
value = rs.getString(columnName);
break;
}
case Types.LONGVARCHAR:
case Types.CLOB:
{
value = rs.getCharacterStream(columnName);
value = rs.getClob(columnName);
break;
}
case Types.BINARY:
case Types.VARBINARY:
case Types.DATALINK:
{
value = rs.getBytes(columnName);
value = rs.getObject(columnName);
break;
}
case Types.DATE:
{
value = rs.getDate(columnName);
break;
}
case Types.DECIMAL:
{
value = rs.getBigDecimal(columnName);
break;
}
case Types.DISTINCT:
{
value = rs.getObject(columnName);
break;
}
case Types.DOUBLE:
{
value = rs.getDouble(columnName);
break;
}
case Types.FLOAT:
{
value = rs.getFloat(columnName);
break;
}
case Types.INTEGER:
{
value = rs.getInt(columnName);
break;
}
case Types.JAVA_OBJECT:
{
value = rs.getObject(columnName);
break;
}
case Types.LONGNVARCHAR:
{
value = rs.getNCharacterStream(columnName);
break;
}
@ -1038,9 +1065,81 @@ public class JdbcSupport
break;
}
case Types.DATE:
case Types.LONGVARCHAR:
{
value = rs.getDate(columnName);
value = rs.getCharacterStream(columnName);
break;
}
case Types.NCHAR:
{
value = rs.getNString(columnName);
break;
}
case Types.NCLOB:
{
value = rs.getNClob(columnName);
break;
}
case Types.NUMERIC:
{
value = rs.getBigDecimal(columnName);
break;
}
case Types.NVARCHAR:
{
value = rs.getNString(columnName);
break;
}
case Types.OTHER:
{
value = rs.getObject(columnName);
break;
}
case Types.REAL:
{
value = rs.getFloat(columnName);
break;
}
case Types.REF:
{
value = rs.getRef(columnName);
break;
}
case Types.REF_CURSOR:
{
value = rs.getObject(columnName);
break;
}
case Types.ROWID:
{
value = rs.getRowId(columnName);
break;
}
case Types.SMALLINT:
{
value = rs.getShort(columnName);
break;
}
case Types.SQLXML:
{
value = rs.getSQLXML(columnName);
break;
}
case Types.STRUCT:
{
value = rs.getObject(columnName);
break;
}
@ -1058,46 +1157,29 @@ public class JdbcSupport
break;
}
case Types.CLOB:
case Types.TINYINT:
{
value = rs.getClob(columnName);
value = rs.getByte(columnName);
break;
}
case Types.BLOB:
case Types.VARBINARY:
{
value = rs.getBlob(columnName);
value = rs.getBytes(columnName);
break;
}
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
value = rs.getNString(columnName);
break;
}
case Types.LONGNVARCHAR:
{
value = rs.getNCharacterStream(columnName);
break;
}
case Types.NCLOB:
{
value = rs.getNClob(columnName);
break;
}
case Types.SQLXML:
{
value = rs.getSQLXML(columnName);
value = rs.getString(columnName);
break;
}
default:
{
value = getColumnValueExt(cn, rs, columnName, sqlType);
break;
}
}
if (rs.wasNull())
@ -1171,6 +1253,25 @@ public class JdbcSupport
return columnName;
}
/**
* 获取列名数组
*
* @param metaData
* @return
* @throws SQLException
*/
public String[] getColumnNames(ResultSetMetaData metaData) throws SQLException
{
int size = metaData.getColumnCount();
String[] names = new String[size];
for (int i = 0; i < names.length; i++)
names[i] = getColumnName(metaData, i + 1);
return names;
}
/**
* 获取列类型
*
@ -1188,6 +1289,25 @@ public class JdbcSupport
return new SqlType(type, typeName);
}
/**
* 获取列类型数组
*
* @param metaData
* @return
* @throws SQLException
*/
public SqlType[] getColumnSqlTypes(ResultSetMetaData metaData) throws SQLException
{
int size = metaData.getColumnCount();
SqlType[] sqlTypes = new SqlType[size];
for (int i = 0; i < sqlTypes.length; i++)
sqlTypes[i] = getColumnSqlType(metaData, i + 1);
return sqlTypes;
}
/**
* SQL插入操作的自动生成结果
*