Java JDBC与Spring慢速元数据获取Oracle
我正在使用SpringJava JDBC与Spring慢速元数据获取Oracle,java,jdbc,oracle11g,spring-jdbc,database-metadata,Java,Jdbc,Oracle11g,Spring Jdbc,Database Metadata,我正在使用SpringJdbcUtils.extractDatabaseMetaData()方法来分析数据库。该函数调用回调并移交一个DatabaseMetaData对象。此对象提供getColumns(字符串目录、字符串模式终端、字符串表名模式、字符串列名模式) 我这样叫它getColumns(“,TABLE\u OWNER\u USERNAME,null,null),结果得到400列。这些正是我想要的结果,但请求需要超过1分钟 我是否可以优化此查询以加快速度?拉400行应该在1秒而不是一分钟
JdbcUtils.extractDatabaseMetaData()
方法来分析数据库。该函数调用回调并移交一个DatabaseMetaData
对象。此对象提供getColumns(字符串目录、字符串模式终端、字符串表名模式、字符串列名模式)
我这样叫它getColumns(“,TABLE\u OWNER\u USERNAME,null,null)
,结果得到400列。这些正是我想要的结果,但请求需要超过1分钟
我是否可以优化此查询以加快速度?拉400行应该在1秒而不是一分钟内完成
编辑:我不怀疑弹簧部分速度慢。更仔细的分析表明,获取
数据库元数据
需要几秒钟,但执行getColumns()
需要很长时间。也许查询所有选项卡列是一种更好的方法。以下是一个例子:
public final List<Column> getColumnsByOwner(final String owner) {
final String sql = "SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, "
+ " DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT"
+ " FROM ALL_TAB_COLUMNS"
+ " WHERE OWNER = ? ORDER BY COLUMN_ID";
return jdbcTemplate.query(sql,
new Object[] { owner },
new RowMapper<Column>() {
@Override
public Column mapRow(final ResultSet res, final int rowNum)
throws SQLException {
final Column reg = new Column();
reg.setColumnName(res.getString("COLUMN_NAME"));
//Read other properties
reg.setNullable(res.getString("NULLABLE").equals("Y"));
return reg;
}
});
}
public最终列表getColumnsByOwner(最终字符串所有者){
final String sql=“选择列名称、数据类型、数据长度,”
+数据精度、数据比例、可为空、数据默认值
+“从所有\u选项卡\u列”
+“其中所有者=?按列_ID排序”;
返回jdbcTemplate.query(sql,
新对象[]{owner},
新的行映射器(){
@凌驾
公共列mapRow(最终结果集res,最终int rowNum)
抛出SQLException{
最终列reg=新列();
reg.setColumnName(res.getString(“COLUMN_NAME”);
//读取其他属性
reg.setNullable(res.getString(“NULLABLE”).equals(“Y”);
返回注册表;
}
});
}
如果需要按表过滤,只需将“AND table_NAME=?”作为另一个参数添加到sql和tableName中
希望能有所帮助。通过对客户端和服务器之间的实际通信进行反向工程,我可以发现Oracle的DatabaseMetaData.getColumns()方法会发送以下SQL查询(尽管这可能会随ODBC驱动程序版本和设置而改变): 您可以理解为什么这可能会有点慢,特别是因为ALL_TAB_COLS和ALL_TYPES表的记录长度都可以达到1000。尽管如此,当Oracle努力执行第一次调用(分钟)时,后续调用几乎立即返回结果。这是一个典型的表连接性能问题,即使需要数据子集,引擎也会在计算和交付所需子集之前连接整个数据集。后续数据/结果缓存可以提高后续查询的性能 更好的解决方案可能是使用get_ddl()并按照解析返回的表定义 或者,通过执行虚拟查询,然后使用resultSetMetadata,您可以更快地查询表上的元数据,如下所示(注意:列备注元数据可能不会立即可用):
ResultSet rs=connection.CreateStatement.executeQuery(“从MyTable中选择*,其中1=0”);
ResultSetMetaData md=rs.getMetaData();
对于(int ix=1;ix)您确定这是Springs的错误吗?您可以使用普通JDBC或直接在数据库控制台中运行相同的查询吗?我认为这不是Springs的错误。问题是,它本身不是一个查询。JDBC驱动程序通过getColumns()隐藏实际的查询
method,所以我看不出到底发生了什么。@FranzKafka-您能跟踪Spring生成的SQL以确定确切的数据字典表吗正在被查询?是否在数据字典中收集了统计数据?我的经验是Oracle的系统目录非常慢。有时对我有帮助的唯一一件事是运行dbms\u stats.gather\u schema\u stats()
在SYS
模式上。另请参阅这个相关问题,其中列出了通过ojdbc在后台运行的实际查询数据库元数据
:这是一个致命的建议,尤其是使用rs.getMetaData()
的最后一个想法。以前有什么比较慢(更不用说迭代所有表元数据的25k)突然闪电般的快了。谢谢。
declare
in_owner varchar2(128);
in_name varchar2(128);
in_column varchar2(128);
xyzzy SYS_REFCURSOR;
begin
in_owner := :1; // Which resolves to the schema (user) name supplied
in_name := :2; // Which resolves to the table name supplied
in_column := :3; // Which gets set to '%';
open xyzzy for
SELECT NULL AS table_cat,
t.owner AS table_schem,
t.table_name AS table_name,
t.column_name AS column_name,
DECODE( (SELECT a.typecode
FROM ALL_TYPES A
WHERE a.type_name = t.data_type),
'OBJECT', 2002,
'COLLECTION', 2003,
DECODE(substr(t.data_type, 1, 9),
'TIMESTAMP',
DECODE(substr(t.data_type, 10, 1),
'(',
DECODE(substr(t.data_type, 19, 5),
'LOCAL', -102, 'TIME ', -101, 93),
DECODE(substr(t.data_type, 16, 5),
'LOCAL', -102, 'TIME ', -101, 93)),
'INTERVAL ',
DECODE(substr(t.data_type, 10, 3),
'DAY', -104, 'YEA', -103),
DECODE(t.data_type,
'BINARY_DOUBLE', 101,
'BINARY_FLOAT', 100,
'BFILE', -13,
'BLOB', 2004,
'CHAR', 1,
'CLOB', 2005,
'COLLECTION', 2003,
'DATE', 93,
'FLOAT', 6,
'LONG', -1,
'LONG RAW', -4,
'NCHAR', -15,
'NCLOB', 2011,
'NUMBER', 2,
'NVARCHAR', -9,
'NVARCHAR2', -9,
'OBJECT', 2002,
'OPAQUE/XMLTYPE', 2009,
'RAW', -3,
'REF', 2006,
'ROWID', -8,
'SQLXML', 2009,
'UROWI', -8,
'VARCHAR2', 12,
'VARRAY', 2003,
'XMLTYPE', 2009,
1111)))
AS data_type,
t.data_type AS type_name,
DECODE (t.data_precision, null,
DECODE(t.data_type, 'NUMBER',
DECODE(t.data_scale, null, 0 , 38),
DECODE (t.data_type, 'CHAR', t.char_length, 'VARCHAR', t.char_length, 'VARCHAR2', t.char_length, 'NVARCHAR2', t.char_length, 'NCHAR', t.char_length, 'NUMBER', 0, t.data_length) ), t.data_precision)
AS column_size,
0 AS buffer_length,
DECODE (t.data_type, 'NUMBER', DECODE(t.data_precision, null, DECODE(t.data_scale, null, -127 , t.data_scale), t.data_scale), t.data_scale) AS decimal_digits,
10 AS num_prec_radix,
DECODE (t.nullable, 'N', 0, 1) AS nullable,
NULL AS remarks,
t.data_default AS column_def,
0 AS sql_data_type,
0 AS sql_datetime_sub,
t.data_length AS char_octet_length,
t.column_id AS ordinal_position,
DECODE (t.nullable, 'N', 'NO', 'YES') AS is_nullable,
null as SCOPE_CATALOG,
null as SCOPE_SCHEMA,
null as SCOPE_TABLE,
null as SOURCE_DATA_TYPE,
'NO' as IS_AUTOINCREMENT,
t.virtual_column as IS_GENERATEDCOLUMN
FROM all_tab_cols t
WHERE t.owner LIKE in_owner ESCAPE '/'
AND t.table_name LIKE in_name ESCAPE '/'
AND t.column_name LIKE in_column ESCAPE '/'
AND t.user_generated = 'YES'
ORDER BY table_schem, table_name, ordinal_position;
end;
ResultSet rs = connection.CreateStatement.executeQuery("SELECT * from MyTable WHERE 1=0");
ResultSetMetaData md = rs.getMetaData();
for (int ix = 1; ix <= md.getColumnCount(); ix++)
{
int colSize = md.getPrecision(ix);
String nativeType = md.getColumnTypeName(ix);
int jdbcType = md.getColumnType(ix);
// and so on....
}