【问题标题】:auto increment ID in H2 databaseH2数据库中的自动增量ID
【发布时间】:2012-03-10 07:46:00
【问题描述】:

有没有办法为表设置一个自动递增的 BIGINT ID。 可以这样定义

id bigint auto_increment

但这没有效果(它不会自动增加)。 我想插入除 ID 字段之外的所有字段 - ID 字段应由 DBMS 提供。 还是我需要调用一些东西来增加 ID 计数器?

【问题讨论】:

标签: database auto-increment h2


【解决方案1】:

它对我有用。 JDBC URL:jdbc:h2:~/temp/test2

drop table test;
create table test(id bigint auto_increment, name varchar(255));
insert into test(name) values('hello');
insert into test(name) values('world');
select * from test; 

结果:

ID  NAME  
1   hello
2   world

【讨论】:

  • 谢谢!我了解到,我的错误不是在创建中,而是在插入语句中。我使用插入测试值('hello');
  • 如果你能 +1 我的回答我会很好:-)
  • 它是-1,我只能把它提高到0,我做到了。也许我错过了提高它的分数。
  • 谢谢!你不需要把它提高得更高,没关系。我只是不明白为什么有人会为正确答案投票-1...可能是不理解问题的人。
  • 存在另一种使用default 的语法:insert into test values(default,'hello'); 对具有大量字段的表很有帮助。
【解决方案2】:

IDENTITY

现代方法使用IDENTITY 类型,用于自动生成递增的 64 位长整数。

H2 中使用的这种单字语法是GENERATED … AS IDENTITY 的缩写变体,在SQL:2003 标准中定义。请参阅 PDF 文档 SQL:2003 Has Been Published 中的摘要。其他数据库正在实现这一点,such as Postgres

CREATE TABLE event_ 
( 
    pkey_ IDENTITY NOT NULL PRIMARY KEY ,  -- ⬅ `identity` = auto-incrementing long integer.
    name_ VARCHAR NOT NULL ,
    start_ TIMESTAMP WITH TIME ZONE NOT NULL , 
    duration_ VARCHAR NOT NULL
) 
;

示例用法。无需为我们的pkey 列值传递值,因为它是由 H2 自动生成的。

INSERT INTO event_ ( name_ , start_ , stop_ )
VALUES ( ? , ? , ? ) 
;

还有 Java。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
OffsetDateTime start = ZonedDateTime.of( 2021 , Month.JANUARY , 23 , 19 , 0 , 0 , 0 , z ).toOffsetDateTime() ; 
Duration duration = Duration.ofHours( 2 ) ;

myPreparedStatement.setString( 1 , "Java User Group" ) ;
myPreparedStatement.setObject( 2 , start ) ;
myPreparedStatement.setString( 3 , duration.toString() ) ; 

返回生成的密钥

Statement.RETURN_GENERATED_KEYS

您可以捕获该插入命令执行期间生成的值。需要两个步骤。首先,在获取准备好的语句时传递标志 Statement.RETURN_GENERATED_KEYS

PreparedStatement pstmt = conn.prepareStatement( sql , Statement.RETURN_GENERATED_KEYS ) ;

Statement::getGeneratedKeys

第二步是在执行准备好的语句后调用Statement::getGeneratedKeys。你会得到一个ResultSet,它的行是为创建的行生成的标识符。

示例应用

这是一个完整的示例应用程序。在 Java 14 上运行并启用 Text Blocks preview feature 以获得乐趣。使用 H2 版本 1.4.200。

package work.basil.example;

import org.h2.jdbcx.JdbcDataSource;

import java.sql.*;
import java.time.*;
import java.util.Objects;

public class H2ExampleIdentity
{
    public static void main ( String[] args )
    {
        H2ExampleIdentity app = new H2ExampleIdentity();
        app.doIt();
    }

    private void doIt ( )
    {
        JdbcDataSource dataSource = Objects.requireNonNull( new JdbcDataSource() );  // Implementation of `DataSource` bundled with H2.
        dataSource.setURL( "jdbc:h2:mem:h2_identity_example_db;DB_CLOSE_DELAY=-1" ); // Set `DB_CLOSE_DELAY` to `-1` to keep in-memory database in existence after connection closes.
        dataSource.setUser( "scott" );
        dataSource.setPassword( "tiger" );

        String sql = null;

        try (
                Connection conn = dataSource.getConnection() ;
        )
        {
            sql = """
                  CREATE TABLE event_
                     ( 
                        id_ IDENTITY NOT NULL PRIMARY KEY,  -- ⬅ `identity` = auto-incrementing integer number.
                        title_ VARCHAR NOT NULL ,
                        start_ TIMESTAMP WITHOUT TIME ZONE NOT NULL ,
                        duration_ VARCHAR NOT NULL
                      )
                  ;
                  """;
            System.out.println( "sql:  \n" + sql );
            try ( Statement stmt = conn.createStatement() ; )
            {
                stmt.execute( sql );
            }

            // Insert row.
            sql = """
                  INSERT INTO event_ ( title_ , start_ , duration_ )
                  VALUES ( ? , ? , ? )
                  ;
                  """;
            try (
                    PreparedStatement pstmt = conn.prepareStatement( sql , Statement.RETURN_GENERATED_KEYS ) ;
            )
            {
                ZoneId z = ZoneId.of( "America/Montreal" );
                ZonedDateTime start = ZonedDateTime.of( 2021 , 1 , 23 , 19 , 0 , 0 , 0 , z );
                Duration duration = Duration.ofHours( 2 );

                pstmt.setString( 1 , "Java User Group" );
                pstmt.setObject( 2 , start.toOffsetDateTime() );
                pstmt.setString( 3 , duration.toString() );

                pstmt.executeUpdate();
                try (
                        ResultSet rs = pstmt.getGeneratedKeys() ;
                )
                {
                    while ( rs.next() )
                    {
                        int id = rs.getInt( 1 );
                        System.out.println( "generated key: " + id );
                    }
                }
            }

            // Query all.
            sql = "SELECT * FROM event_ ;";
            try (
                    Statement stmt = conn.createStatement() ;
                    ResultSet rs = stmt.executeQuery( sql ) ;
            )
            {
                while ( rs.next() )
                {
                    //Retrieve by column name
                    int id = rs.getInt( "id_" );
                    String title = rs.getString( "title_" );
                    OffsetDateTime odt = rs.getObject( "start_" , OffsetDateTime.class );  // Ditto, pass class for type-safety.
                    Instant instant = odt.toInstant();  // If you want to see the moment in UTC.
                    Duration duration = Duration.parse( rs.getString( "duration_" ) );

                    //Display values
                    ZoneId z = ZoneId.of( "America/Montreal" );
                    System.out.println( "id_" + id + " | start_: " + odt + " | duration: " + duration + " ➙ running from: " + odt.atZoneSameInstant( z ) + " to: " + odt.plus( duration ).atZoneSameInstant( z ) );
                }
            }
        }
        catch ( SQLException e )
        {
            e.printStackTrace();
        }
    }
}

接下来,运行时查看结果。

Instant, OffsetDateTime, & ZonedDateTime

在执行此操作时,我的 JVM 当前的默认时区是 America/Los_Angeles。在存储时刻的时间点(2021 年 1 月 23 日晚上 7 点,魁北克),America/Los_Angeles 区域与 UTC 的偏移量落后了 8 小时。所以H2 JDBC驱动返回的OffsetDateTime对象被设置为-08:00的偏移量。这确实让人分心,所以在实际工作中,我会立即将 OffsetDateTime 转换为 UTC 的 Instant 或我想到的特定时区的 ZonedDateTime。清楚地了解InstantOffsetDateTimeZonedDateTime 对象都将代表相同的同时时刻,时间轴上的相同点。每个人都通过不同的挂钟时间观看同一时刻。想象一下加利福尼亚、魁北克和冰岛(其时区为 UTC,偏移量为零)的 3 个人在电话会议结束时都在同一巧合的时刻抬头看着各自墙上的时钟。

生成的密钥:1

id_1 |开始_:2021-01-23T16:00-08:00 |持续时间:PT2H ➙ 运行时间:2021-01-23T19:00-05:00[America/Montreal] 至:2021-01-23T21:00-05:00[America/Montreal]


顺便说一句,在应用程序预订未来约会的实际工作中,我们会在 Java 和数据库中使用不同的数据类型。

我们会在 Java 中使用 LocalDateTimeZoneId。在数据库中,我们将使用类似于 SQL 标准类型 TIMESTAMP WITHOUT TIME ZONE 的数据类型,第二列是预期时区的名称。从数据库中检索值以构建调度日历时,我们会将时区应用于存储的日期时间以获取ZonedDateTime 对象。这将允许我们在一天中的某个时间进行预约,而不管该司法管辖区的政治家对与 UTC 的偏移量所做的更改。

【讨论】:

  • 如果您显示插入以及创建表格,此答案会更有用
  • @JesseBoyd 感谢您的建议。我添加了一个完整的示例应用程序,展示了表的创建、插入一行、检索生成的密钥并将表转储到控制台。
【解决方案3】:

很简单:

id int auto_increment primary key

H2会自动创建Sequence对象

【讨论】:

    【解决方案4】:

    你也可以使用default:

    create table if not exists my(id int auto_increment primary key,s text);
    insert into my values(default,'foo');
    

    【讨论】:

      【解决方案5】:
      id bigint(size) zerofill not null auto_increment,
      

      【讨论】:

      • zerofill 不受 H2 数据库支持。
      猜你喜欢
      • 2014-11-18
      • 2020-06-17
      • 2014-08-03
      • 2018-01-22
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      • 1970-01-01
      • 2021-07-22
      相关资源
      最近更新 更多