添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account The keyGenerator doesn't work in transaction-2pc-xa-raw-jdbc-example module when a oracle database used #5421 The keyGenerator doesn't work in transaction-2pc-xa-raw-jdbc-example module when a oracle database used #5421 TaoZhiMLND opened this issue May 4, 2020 · 12 comments · Fixed by #5436

I have reproduced the behavior as @lvye351 says in #5383 . The keyGenerator doesn't work in the transaction-2pc-xa-raw-jdbc sample module when a oracle database 12c and ojbdc8.jar used.

The log is as follows,

It seems that when table t_order created, its tableMetaData doesn't put into schemaMetaData.
I will try to find out the reason and fix it.

when configuration is as follows(actual table name use uppercase words), the issue above will absent,

shardingRule:
  tables:
    t_order:
      actualDataNodes: ds_${0..1}.T_ORDER_${0..1}
      tableStrategy: 
        standard:
          shardingColumn: order_id
          shardingAlgorithm:
            type: INLINE
            props:
              algorithm.expression: T_ORDER_${order_id % 2}
      keyGenerator:
        type: SNOWFLAKE
        column: order_id
        props:
          worker.id: 123

But when TableMetaDataLoader#isTableExist return true, there is another error occur from ColumnMetaDataLoader#load.
Java code is

        try (ResultSet resultSet = connection.createStatement().executeQuery(generateEmptyResultSQL(table, databaseType))) {
            for (String each : columnNames) {
                isCaseSensitives.add(resultSet.getMetaData().isCaseSensitive(resultSet.findColumn(each)));

Exception cause is

Error : 933, Position : 38, Sql = SELECT * FROM "T_ORDER_0" WHERE 1 != 1;, OriginalSql = SELECT * FROM "T_ORDER_0" WHERE 1 != 1;, Error Msg = ORA-00933: SQL 命令未正确结束

And this sql can execute successfully in PL/SQL.

when configuration is as follows(actual table name use uppercase words), the issue above will absent,

shardingRule:
  tables:
    t_order:
      actualDataNodes: ds_${0..1}.T_ORDER_${0..1}
      tableStrategy: 
        standard:
          shardingColumn: order_id
          shardingAlgorithm:
            type: INLINE
            props:
              algorithm.expression: T_ORDER_${order_id % 2}
      keyGenerator:
        type: SNOWFLAKE
        column: order_id
        props:
          worker.id: 123

But when TableMetaDataLoader#isTableExist return true, there is another error occur from ColumnMetaDataLoader#load.
Java code is

        try (ResultSet resultSet = connection.createStatement().executeQuery(generateEmptyResultSQL(table, databaseType))) {
            for (String each : columnNames) {
                isCaseSensitives.add(resultSet.getMetaData().isCaseSensitive(resultSet.findColumn(each)));

Exception cause is

Error : 933, Position : 38, Sql = SELECT * FROM "T_ORDER_0" WHERE 1 != 1;, OriginalSql = SELECT * FROM "T_ORDER_0" WHERE 1 != 1;, Error Msg = ORA-00933: SQL 命令未正确结束

And this sql can execute successfully in PL/SQL.

before I have found that ,SELECT * FROM "T_ORDER_0" WHERE 1 != 1; the ; in the end is not allowed int oracle grammar,I commented it in code temporarily , key still not generated, so this is only one bug in ColumnMetaDataLoader.generateEmptyResultSQL

Now the test turn into green.
changes are as follows,
1、modify ColumnMetaDataLoade#generateEmptyResultSQL

return "SELECT * FROM " + delimiterLeft + table + delimiterRight + " WHERE 1 != 1" 

(remove semicolons behind WHERE 1 != 1 which not allowed in oracle)
2、return value of connection.getMetaData().getIndexInfo includes a null name record most times but not always . So IndexMetaDataLoader#load needs a check for whether indexName is null.

    public static Collection<IndexMetaData> load(final Connection connection, final String table) throws SQLException {
        Collection<IndexMetaData> result = new HashSet<>();
        try (ResultSet resultSet = connection.getMetaData().getIndexInfo(connection.getCatalog(), connection.getSchema(), table, false, false)) {
            while (resultSet.next()) {
                String indexName = resultSet.getString(INDEX_NAME);
                if (Strings.isNullOrEmpty(indexName)) {
                    continue;
                result.add(new IndexMetaData(indexName));
        return result;

3、connection.getMetaData().getColumns in ColumnMetaDataLoader#load need specify a schema, otherwise it will return duplicated columns, so that I use connection.getSchema() replace null.

        try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), table, "%")) {
            while (resultSet.next()) {
                String columnName = resultSet.getString(COLUMN_NAME);
                columnTypes.add(resultSet.getInt(DATA_TYPE));
                columnTypeNames.add(resultSet.getString(TYPE_NAME));
                isPrimaryKeys.add(primaryKeys.contains(columnName));
                columnNames.add(columnName);

it is works to me. Can we discuss further how to modify it?

  • We need to make sure about oracle SQL and semicolon.
  • If return true for ResultSet.next() with connection.getMetaData().getIndexInfo(), what's happen for null value of resultSet.getString(INDEX_NAME)?
  • The 3rd modification is OK.
  • 1、I found some infomation may explain the use of semicolon in Oracle JDBC. This means that a semi-colon is not needed, and as it is not part of the actual statement syntax for a lot of database it is also a syntax error to include it.

    Usually the semi-colon is not part of the actual syntax of a statement (as most database internal APIs execute a single statement at a time). Instead the semi-colon is an 'end-of-statement' marker or statement separator that is - usually - defined in CLI or scripting tools for the database. This allows that tool to know when a statement ends, so it can send that single statement to the database for execution.
    On the other hand, the JDBC api is intended to execute a single(!) statement at a time, therefor you don't need such a separator (the statement is the whole string). This means that a semi-colon is not needed, and as it is not part of the actual statement syntax for a lot of database it is also a syntax error to include it. Some JDBC drivers will strip the last ; from a statement to 'fix' that, some drivers don't.
    Some drivers allow - contrary to the JDBC specification - multiple statements to be executed as a single string, this usually has to be enabled with a connection property, for example for MySQL it is the option allowMultiQueries (see the MySQL properties for details).

    Original answer link https://stackoverflow.com/questions/18941539/is-the-semicolon-necessary-in-sql

    Note, however, that there is no semicolon at the end of the SQL92 CallableStatement

    Original articles from Expert Oracle JDBC Programming

    2、ResultSet of OracleDatabaseMetaData#getIndexInfo contains a record describing table's statistics which INDEX_NAME is null . But DatabaseMetaData#getIndexInfo in mysql jdbc implementation doesn't return the statistics record.

    public java.sql.ResultSet getIndexInfo(java.lang.String catalog,
                                           java.lang.String schema,
                                           java.lang.String table,
                                           boolean unique,
                                           boolean approximate)
                                    throws java.sql.SQLException

    Get a description of a table's indices and statistics. They are ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
    Each index column description has the following columns:

  • TABLE_CAT String => table catalog (may be null)
  • TABLE_SCHEM String => table schema (may be null)
  • TABLE_NAME String => table name
  • NON_UNIQUE boolean => Can index values be non-unique? false when TYPE is tableIndexStatistic
  • INDEX_QUALIFIER String => index catalog (may be null); null when TYPE is tableIndexStatistic
  • INDEX_NAME String => index name; null when TYPE is tableIndexStatistic
  • TYPE short => index type:
    tableIndexStatistic - this identifies table statistics that are returned in conjuction with a table's index descriptions
    tableIndexClustered - this is a clustered index
    tableIndexHashed - this is a hashed index
    tableIndexOther - this is some other style of index
  • ORDINAL_POSITION short => column sequence number within index; zero when TYPE is tableIndexStatistic
  • CARDINALITY long => When TYPE is tableIndexStatistic, then this is the number of rows in the table; otherwise, it is the number of unique values in the index.
  • PAGES long => When TYPE is tableIndexStatisic then this is the number of pages used for the table, otherwise it is the number of pages used for the current index.
  • Original document from https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/jdbc/OracleDatabaseMetaData.html#getIndexInfo(java.lang.String,%20java.lang.String,%20java.lang.String,%20boolean,%20boolean)
    https://docs.oracle.com/javase/8/docs/api/java/sql/DatabaseMetaData.html#getIndexInfo-java.lang.String-java.lang.String-java.lang.String-boolean-boolean-