【问题标题】:how to create table if it doesn't exist using Derby Db如果表不存在,如何使用 Derby Db 创建表
【发布时间】:2011-08-17 11:49:53
【问题描述】:

我是apache derby 的新手,我似乎无法工作

    CREATE TABLE IF NOT EXISTS table1 ...

可以在MySql 等中实现。当我尝试在我的Java 程序中运行这个SQL 语句时,我得到一个'Syntax error: Encountered "NOT" at line 1, column 17.'

我在文档页面中查看了 Derby Db Create Statements,但找不到这样的替代方案。

【问题讨论】:

    标签: java derby database


    【解决方案1】:

    创建表,捕获SQLException并检查SQL状态码。

    错误代码的完整列表可以找到here 但是我找不到Table <value> already exists;可能是X0Y68您需要的代码是X0Y32

    只需运行一次代码并打印错误代码。不要忘记添加测试以确保代码有效;这样,您可以捕获错误代码中的更改(不应该发生......)。

    在我的项目中,我通常会添加一个带有静态方法的辅助类,这样我就可以编写:

    } catch( SQLException e ) {
        if( DerbyHelper.tableAlreadyExists( e ) ) {
            return; // That's OK
        }
        throw e;
    }
    

    另一个选项是对表运行SELECT 并检查状态代码(应该是42X05)。但这是您需要发送的第二条命令,它不提供任何额外信息。

    更糟糕的是,它可能因“表不存在”以外的其他原因而失败,因此“创建并忽略错误”更好地 IMO。

    【讨论】:

    • 谢谢亚伦。我认为这对我的问题来说是一个更简单的解决方案。
    • Table/View 'TEST1' already exists in Schema 'APP'. 的错误代码是 X0Y32
    • 这并非完全正确。只要元素已经在数据库中,就会出现错误代码X0Y32。以下脚本将把它扔到一个空数据库上:create table foo (id int primary key);create table bar (bar int constraint fk references foo(id));create table baaz (baaz int constraint fk references foo(id)); 这是因为当您到达最后一个创建表时,约束fk 已经存在于模式中。这表明当约束也存在时,会抛出错误代码X0Y32
    • @puk:正如我在回答中所说,这是我为客户编写的帮助类 - 它不是公开的。该方法只是从异常中提取 SQL 错误代码并将其与错误代码X0Y32 进行比较。您可能还想查看使用类似方法的 Spring 框架,因为代码是公开且可重用的。
    • 如果你试图做相反的事情,删除一个表(如果存在),要捕获的错误代码是42Y55
    【解决方案2】:

    Derby 不支持该 sql 语句。
    在我的程序中,我将数据库中的所有表解析为一个集合并检查表是否存在于那里。 像这样:

      private Set<String> getDBTables(Connection targetDBConn) throws SQLException
      {
        Set<String> set = new HashSet<String>();
        DatabaseMetaData dbmeta = targetDBConn.getMetaData();
        readDBTable(set, dbmeta, "TABLE", null);
        readDBTable(set, dbmeta, "VIEW", null);
        return set;
      }
    
      private void readDBTable(Set<String> set, DatabaseMetaData dbmeta, String searchCriteria, String schema)
          throws SQLException
      {
        ResultSet rs = dbmeta.getTables(null, schema, null, new String[]
        { searchCriteria });
        while (rs.next())
        {
          set.add(rs.getString("TABLE_NAME").toLowerCase());
        }
      }
    

    【讨论】:

    • +1 用于 API 的语义使用。迄今为止最好的方法恕我直言。谢谢oers。
    【解决方案3】:

    Derby db 不支持您正在执行的查询。相反,如果您知道表的名称,则可以很容易地找到表是否存在。

    public boolean isTableExist(String sTablename) throws SQLException{
        if(connection!=null)
        {
            DatabaseMetaData dbmd = connection.getMetaData();
            ResultSet rs = dbmd.getTables(null, null, sTablename.toUpperCase(),null);
            if(rs.next())
            {
                System.out.println("Table "+rs.getString("TABLE_NAME")+"already exists !!");
            }
            else
            {
                System.out.println("Write your create table function here !!!");
            }
            return true;
        }
        return false;
    }
    

    Catch 是将表名以大写形式指定,否则在元数据中将无法找到表名。

    【讨论】:

      【解决方案4】:

      检查表是否存在:

      Connection con = DriverManager.getConnection(url);
      ResultSet res = con.getMetaData().getTables(null, Schema_Name, table_name.toUpperCase(), null);//Default schema name is "APP"
      if(res.next())
      {
          //do some thing;
      }else{
          JOptionPane.showMessageDialog(null, table_name +" not exist");
      }
      

      显示所有表名:

          Connection con = DriverManager.getConnection(url);
          ResultSet res = con.getMetaData().getTables(null, Schema_Name, "%", null);//Default schema name is "APP"
          while(res.next())
          {
              JOptionPane.showMessageDialog(null, res.getString(3) + " is exist");//Show table name
          }else{
              JOptionPane.showMessageDialog(null, table_name +" not exist");
          }
      

      【讨论】:

      • "APP" 只是在连接到嵌入式 derby 驱动程序时未在连接字符串中提供任何用户名时的默认模式名称。否则为大写字母的用户名。
      【解决方案5】:

      Aaron Digulla 的带领下,使用 DerbyUtils 类检查表是否存在,这是我想出的解决方案:

      调用类

      public void createTable(String name) {
          Connection connection = null;
          PreparedStatement preparedStatement = null;
      
          try {
              connection = daoFactory.getConnection();
              String sql = String.format(SQL_CREATE_TABLE, name); 
              preparedStatement = connection.prepareStatement(sql, Statement.NO_GENERATED_KEYS);
              preparedStatement.execute();
          } catch (SQLException e) {
              if(DerbyUtils.tableAlreadyExists(e)) { //check if the exception is because of pre-existing table.
                  logger.info("Talbe " + name + " already exists.  No need to recreate");
              } else {
                  logger.error(e.getMessage() + " : " + e.getStackTrace());
              }
          } finally {
              close(connection, preparedStatement);  //DAOUtils silently closes
          }
      }
      

      DerbyUtils

      public class DerbyUtils {
      
          public DerbyUtils() {
              //empty constructor -- helper class
          }
      
          public static boolean tableAlreadyExists(SQLException e) {
              boolean exists;
              if(e.getSQLState().equals("X0Y32")) {
                  exists = true;
              } else {
                  exists = false;
              }
              return exists;
          }
      }
      

      另见

      【讨论】:

        【解决方案6】:

        我知道这已标有答案,但如果有人想要另一种检查方式,我还是想发布。在这里,我使用返回布尔值的方法检查表元数据,如果存在则为 true,如果不存在则为 false。希望它可以帮助其他人,如果他们正在寻找。

        private static Connection conn = null;
        private static Statement st = null;
        private static ResultSet rs = null;
        private static DatabaseMetaData dmd;
        
        public Boolean firstTime()
        {
            try
            {
                dmd = conn.getMetaData();
                rs = dmd.getTables(null, "APP", "LOGIN", null);
                return !rs.next();
            } catch (SQLException ex)
            {
                Logger.getLogger(Database.class.getName()).log(Level.SEVERE, null, ex);
                return false;
            }
        }
        

        【讨论】:

          【解决方案7】:

          另一种具有2个条件的解决方案:

          • 愿意在每次创建之前删除表,并且相同的内容存在于 .sql 文件中

          • 正在使用 Spring,因此愿意使用 spring-test 作为 Maven 依赖项,使用它的 @Sql 注释可以让您的生活变得更简单

          所以,首先将此作为依赖项添加到您的 pom:

                     <dependency>
                          <groupId>org.springframework</groupId>
                          <artifactId>spring-test</artifactId>
                          <version>4.2.5.RELEASE</version>
                          <scope>test</scope>
                      </dependency>
          

          其次,假设您有一个删除的 sql,在文件中创建表 a rectangle.sql

          DROP TABLE rectangles;
          
          CREATE TABLE rectangles (
              id      INTEGER NOT NULL PRIMARY KEY,
              width   INTEGER NOT NULL,
              height  INTEGER NOT NULL
          );
          

          你有一个测试类 BlahTest 应该在执行任何要运行的测试之前运行这个 sql,只需在你的类中添加以下 @Sql 注释:

          import org.springframework.test.context.ContextConfiguration; 
          import org.springframework.test.context.jdbc.Sql;
          import org.springframework.test.context.jdbc.SqlConfig;
          import org.springframework.test.context.jdbc.SqlConfig.ErrorMode;
          import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
          
          @RunWith(SpringJUnit4ClassRunner.class)
          @ContextConfiguration(classes=XyzClientConfig.class)
          @Sql(scripts="/sql/ddl/rectangle.sql", config=@SqlConfig (errorMode=ErrorMode.IGNORE_FAILED_DROPS))
          public class BlahTest { 
          ...
          }
          

          指定的config 属性值的@SqlConfig 具有在表不存在的情况下跳过drop 语句错误的魔力。我相信它是专门针对这些类型的数据库而编写的

          【讨论】:

            【解决方案8】:

            这个答案已经晚了,但它可能对某人有帮助。

            以下Java(标准JDBC)代码可用于检查表是否存在,如果存在则可以创建;

            String query = "SELECT TRUE FROM SYS.SYSTABLES WHERE TABLENAME = ? AND TABLETYPE = 'T'"; // Leave TABLETYPE out if you don't care about it
            PreparedStatement ps = connection.prepareStatement(query);
            ps.setString(1, "TABLE_NAME"); // Must be in capitals
            ResultSet rs = ps.executeQuery();
            if ( rs.next() && rs.getBoolean(1) )
            {
                // Table exists
            }
            else
            {
                // Table does NOT exist ... create it
            }
            

            【讨论】:

              【解决方案9】:

              这是一个您可以在 SQL 中编写脚本的解决方案。

              1. 创建一个类,如下所示:

                package user.fenris.spring.extensions;
                
                import java.sql.Connection;
                import java.sql.DriverManager;
                import java.sql.SQLException;
                
                import org.apache.commons.logging.Log;
                import org.apache.commons.logging.LogFactory;
                import org.springframework.jdbc.core.JdbcTemplate;
                import org.springframework.jdbc.datasource.SingleConnectionDataSource;
                
                public class SqlCreateIfNotExists {
                private static Log log = LogFactory.getLog(SqlCreateIfNotExists.class);
                
                public static void createTableIfNotExists(String tablename, String ddl) throws SQLException { 
                    Connection conn = DriverManager.getConnection("jdbc:default:connection");
                
                    if (conn != null) {
                        JdbcTemplate template = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
                        int count = template.queryForInt("select count(*) from SYS.SYSTABLES where TABLENAME = ?", tablename);
                        log.debug("Count: " + count);           
                        if (count == 0) {
                            log.debug("Executing sql statement: " + ddl);
                            template.execute(sql);
                        } else {
                            log.debug("Table exists.  Skipping sql execution...");              
                        }
                    }       
                }
                

                }

                注意:你不一定要用spring,直接用JDBC写就可以,但是你得知道怎么做才行。 (留给读者练习)。此外,您可以重写它以从 ddl 参数中解析出表名。另一件事是进行适当的错误处理。

              2. 确保类已编译并放置在将运行数据库的 VM 的类路径中。

              3. 编写您的 SQL 脚本:

                -- 2K for ddl statement should be enough.  You want more?  Seriously?
                create procedure CreateTableIfNotExists(in tablename varchar(128), in ddl varchar(2048))    
                PARAMETER STYLE JAVA
                MODIFIES SQL DATA   
                language java   
                external name 'user.fenris.spring.extensions.SqlCreateIfNotExists.createTableIfNotExists';
                
                call CreateTableIfNotExists('TABLE_NAME_MUST_BE_ALL_CAPS',
                    'create table TABLE_NAME_MUST_BE_ALL_CAPS
                     (entry_id int generated always as identity not null,
                      entry_timestamp timestamp,         
                      username varchar(128) not null,        
                      note varchar(1024) not null,           
                      primary key (entry_id))');
                
                -- you don't have to drop this, but you would have to create a similar 
                -- procedure to create the CreateTableIfNotExists procedure, 
                -- (i.e. CreateProcedureIfNotExists) but then it's turtles all the way down
                
                drop procedure CreateIfNotExists;
                
              4. ???

              5. 利润

              【讨论】:

                【解决方案10】:
                try {
                            connection.createStatement().execute("create table channels(channel varchar(20),topic varchar(20))");
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                        //  e.printStackTrace();
                        }
                

                用 try-catch 包围 create 语句并确保注释 e.printstacktace(); 如果已经存在则不显示错误,否则创建表..!!

                【讨论】:

                • 我认为@aaron 的答案是正确的方法,因为他明确检查异常是由于“表已存在”引起的。在这里,您也将忽略任何其他异常。谢谢。
                猜你喜欢
                • 2018-07-16
                • 1970-01-01
                • 1970-01-01
                • 2014-03-15
                • 2010-12-03
                • 1970-01-01
                • 2013-08-10
                • 1970-01-01
                相关资源
                最近更新 更多