使用JDBC有没有一种方法来检查SEQUENCE是否存在?

5
我需要以编程方式在数据存储中生成序列,但需要能够检测它们的存在并在已存在时不创建。有人知道提取此信息所需的JDBC元数据吗? DatabaseMetadata 的粗略扫描未显示出合适的方法;我可以获取该模式下的所有表/视图以及相关的键/索引等,但不能获取序列。有人知道一种方法,最好是与数据库无关的,但如果不行,尽可能支持多个数据库(想到Oracle有一个user_sequence表?但这只是一个数据库,我需要支持其他数据库)。
提前致谢。

你使用的是哪个数据库? - avijendr
正如所说,我需要支持多个数据库,因此我使用了H2(它具有CREATE SEQUENCE ... IF NOT EXISTS),但这是非标准的。我需要支持所有支持SEQUENCE的常见关系型数据库,例如SQLServer、PostgreSQL、Oracle、H2、NuoDB、Derby、Firebird等。 - Neil Stockton
可能是 如何从JDBC检索序列元数据? 的重复问题。 - Stephen C
@NeilStockton - 如果你真的读了那个针对Oracle的问题的答案,你会意识到它们也回答了你更一般的问题。但我也已经为你回答了它。 - Stephen C
@NeilStockton - 1) 如果你希望得到关于如何查询数据库模式的答案,你应该问这个问题...而不是问“JDBC能做到吗”。2) 最新的JDBC API已经公开发布。你不需要在SO上提问来找到它们。 - Stephen C
5个回答

2
你可以使用Hibernate方言API来获取序列。请参见:http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/dialect/Dialect.html 从下面的示例中,你可以看到如何使用方言来获取序列的详细信息。
public static void main(String[] args) {
        Connection jdbcConnection = null;
        try {
            jdbcConnection = DriverManager.getConnection("", "", "");
            String sequenceName = "xyz" ; // name of sequence for check
        System.out.println("Check Sequence :" + checkSequenceName(sequenceName, jdbcConnection));
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
        if(jdbcConnection != null) {
            try {
                jdbcConnection.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

public static boolean checkSequenceName(String sequenceName, Connection conn) throws JDBCConnectionException, SQLException {
    DialectResolver dialectResolver = new StandardDialectResolver();
    Dialect dialect =  dialectResolver.resolveDialect(conn.getMetaData());

    if ( dialect.supportsSequences() ) {
        String sql = dialect.getQuerySequencesString();
        if (sql!=null) {

            Statement statement = null;
            ResultSet rs = null;
            try {
                statement = conn.createStatement();
                rs = statement.executeQuery(sql);

                while ( rs.next() ) {
                    if(sequenceName.equals(rs.getString(1))) {
                        return true;
                    }
                }
            }
            finally {
                if (rs!=null) rs.close();
                if (statement!=null) statement.close();
            }

        }
    }
    return false;
}

如果你不想使用Hibernate,那么你需要创建自定义的顺序特定实现。

自定义实现的示例代码:

interface SequenceQueryGenerator {
    String getSelectSequenceNextValString(String sequenceName);
    String getCreateSequenceString(String sequenceName, int initialValue, int incrementSize); 
    String getDropSequenceStrings(String sequenceName); 
    String getQuerySequencesString(); 
}


class OracleSequenceQueryGenerator implements SequenceQueryGenerator {

    @Override
    public String getSelectSequenceNextValString(String sequenceName) {
        return "select " + getSelectSequenceNextValString( sequenceName ) + " from dual";
    }

    @Override
    public String getCreateSequenceString(String sequenceName,
            int initialValue, int incrementSize) {
        return "create sequence " + sequenceName +  " start with " + initialValue + " increment by " + incrementSize;
    }

    @Override
    public String getDropSequenceStrings(String sequenceName) {
        return "drop sequence " + sequenceName;
    }

    @Override
    public String getQuerySequencesString() {
        return "select sequence_name from user_sequences";
    }

}


class PostgresSequenceQueryGenerator implements SequenceQueryGenerator {

    @Override
    public String getSelectSequenceNextValString(String sequenceName) {
        return "select " + getSelectSequenceNextValString( sequenceName );
    }

    @Override
    public String getCreateSequenceString(String sequenceName,
            int initialValue, int incrementSize) {
        return "create sequence " + sequenceName + " start " + initialValue + " increment " + incrementSize;
    }

    @Override
    public String getDropSequenceStrings(String sequenceName) {
        return "drop sequence " + sequenceName;
    }

    @Override
    public String getQuerySequencesString() {
        return "select relname from pg_class where relkind='S'";
    }

}


public boolean checkSequence (String sequenceName, SequenceQueryGenerator queryGenerator, Connection conn) throws SQLException {
        String sql = queryGenerator.getQuerySequencesString();
        if (sql!=null) {

            Statement statement = null;
            ResultSet rs = null;
            try {
                statement = conn.createStatement();
                rs = statement.executeQuery(sql);

                while ( rs.next() ) {
                    if(sequenceName.equals(rs.getString(1))) {
                        return true;
                    }
                }
            }
            finally {
                if (rs!=null) rs.close();
                if (statement!=null) statement.close();
            }

        }
        return false;
    }

public static void main(String[] args) {
        Connection jdbcConnection = null;
        try {
            jdbcConnection = DriverManager.getConnection("", "", "");
            String sequenceName = "xyz" ; // name of sequence for check
            System.out.println(checkSequence(sequenceName, new OracleSequenceQueryGenerator(), jdbcConnection));
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(jdbcConnection != null) {
                try {
                    jdbcConnection.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
}

在Hibernate >= 4.x中,需要将Connection.getMetaData包装在DatabaseMetaDataDialectResolutionInfoAdapter中。 - Kalle Richter
我建议如果任何Dialect.get...String方法返回null,就抛出异常,因为null不是一个文档化的返回值。 - Kalle Richter

2
有没有办法使用JDBC检查SEQUENCE的存在性?
答案很简单,不行。
支持SEQUENCE元数据不是JDBC规范的一部分。 如果您想查找此信息,您需要使代码了解它正在处理的数据库类型,并针对用于表示数据库模式等的供应商特定表执行相关查询。
您可能会找到一个第三方Java库来完成这个任务...但我不知道有这样一个库存在。
实际上,理论上你可以通过尝试创建具有相同名称的序列来测试序列是否存在。但是,这就涉及到各种其他问题,比如处理CREATE的不同语法,删除作为测试创建的SEQUENCE,诊断供应商特定的错误代码以确定为什么CREATE失败。您最好查询特定于供应商的模式表。

1
我并不知道有任何直接的方法。因为每个数据库都有自己生成/处理序列的方式。在Oracle中是< strong>sequence,在mysql中是< strong>auto_increment(不是序列,但接近或实现了相同的结果),在SQL Server中是< strong>Identity Columns等。
我会这样做 - 您需要创建一个接口:
interface ISequenceChecker{ // or some name which suits you
    SequenceObject getSequence();
}

不同数据库/存储的实现(例如,针对Oracle的实现如下):

public class OracleSequenceChecker implements ISequenceChecker{
   OracleSequenceObject getSequence(){
    // some jdbc or similar call
    // to get SELECT SEQUENCE_NAME.NEXTVAL FROM DUAL
   }
}

2
感谢您的回复。是的,每个数据存储区都有一个变量可能是我要走的路。顺便说一下,在MySQL中,“自动递增”不等同于(SQL)SEQUENCE,SEQUENCE是一个独立的命名值生成器,而自动递增则是特定列的标识,没有名称。有些RDBMS支持IDENTITY(自动递增)和SEQUENCE,因此需要区分它们。 - Neil Stockton
谢谢Neil。我知道这不是等同的,但它是最接近的,否则你将不得不使用interlock或类似的东西(但仍然不是sequence)。我想不出除了我上面提到的解决方案之外的其他解决方案。祝你编程愉快! - avijendr

1
你不需要这样做。每个关系数据库管理系统都有各自存储元数据信息的方法。其中一些可能类似于其他系统,但是你几乎不可能在这些表中找到完全相同的信息。
最好的方法是建立某种形式的数据字典来识别RDBMS,然后从字典中的具体配置获取信息。
想法是创建一个表来存储数据库及其是否支持序列功能,然后再创建另一个表来存储必要的配置信息(如序列表、序列列等),以便加载该序列信息。
然后实现一种获取此信息的方法。我会选择 @avijendr 的回答(他在我写这篇文章时发布了回答)。

0

我使用Postgres 9、Java 8和JDBC 4.2进行了测试。

为了检索序列列表,我做了以下操作:

  1. 首先,使用java.sql.DatabaseMetaData#getTableTypes列出实体类型

结果如下:

 * FOREIGN TABLE
 * INDEX
 * MATERIALIZED VIEW
 * SEQUENCE
 * SYSTEM INDEX
 * SYSTEM TABLE
 * SYSTEM TOAST INDEX
 * SYSTEM TOAST TABLE
 * SYSTEM VIEW
 * TABLE
 * TEMPORARY INDEX
 * TEMPORARY SEQUENCE
 * TEMPORARY TABLE
 * TEMPORARY VIEW
 * TYPE
 * VIEW

使用Postgres JDBC驱动程序,有一个名为“SEQUENCE”的表类型。

  1. 然后我像这样使用java.sql.DatabaseMetaData#getTables

    String catalog = "";
    String tableNamePattern = "%";
    String schemaPattern = "my_schema";
    ResultSet tablesRS = cnx.getMetaData().getTables(catalog, schemaPattern, tableNamePattern, new String[] {"SEQUENCE"});

tablesRS.getString("TABLE_NAME")会给出找到的每个序列的名称。

我创建了一个实用类ExtractMetadataUtil来测试这个功能(在GitHub上)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接