能否在 Postgres 中使用序列?

是的,可以。不过,有个限制条件。

在 V2 开发过程中,Postgres 中的序列历经了很大变化,而且大量介绍这些序列在 Glassfish 和 JPA 中使用方式的信息已过时。希望此页面可以成为这一主题的权威性资源。

使用序列的最简单机制是对 GeneratedValue 注释使用 GenerationType.IDENTITY 策略:

@Id
    @Column(name = "id", nullable = false)
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

要使此操作生效,相关表中的列类型必须是 SERIAL。在此实例中,您透明地使用了 SEQUENCE,当定义 SERIAL 字段时 Postgres 数据库会自动创建 SEQUENCE。另一个好处是,如果您使用 Glassfish 中的工具创建部署表,则 Glassfish 会在生成的表中创建正确的 SERIAL 列。

JPA 通过 GeneratedValue 注释上的 GenerationType.SEQUENCE 来规定序列的更具体用法。

遗憾的是,Glassfish JPA 实现 Toplink 并不直接支持将此功能用于 Postgres。

Toplink 中有一个内部体系结构限制,即禁止特定数据库平台同时支持 IDENTITY 类型和 SEQUENCE 类型的生成器。

Toplink 实现器已决定使用标准 Postgres DB 平台实现的 IDENTITY 类型来支持 Postgres SERIAL 列,这样将禁用 SEQUENCE 类型的生成器。

不过,并非一切都会失去。Toplink 允许您指定用于运行应用程序的 DB 平台,而且 DB 平台的默认实现易于扩展。

使用这种机制,可以创建支持 SEQUENCE 的新 Postgres 平台,就像 Oracle 所做的那样,而不是支持 SERIAL 列和 IDENTITY。

为了执行此操作,必须创建一个新类。

// Code free for any use whatsoever.
package faq.glassfish.postgres;

import oracle.toplink.essentials.queryframework.ValueReadQuery;
import oracle.toplink.essentials.sessions.DatabaseSession;

public class PostgresPlatform 
    extends oracle.toplink.essentials.platform.database.PostgreSQLPlatform {
    
    public PostgresPlatform() {
        super();
    }
        
    public boolean shouldNativeSequenceAcquireValueAfterInsert() {
        return false;
    }
    
    public ValueReadQuery buildSelectQueryForNativeSequence(String seqName, Integer size) {
        ValueReadQuery selectQuery = new ValueReadQuery();
        selectQuery.setSQLString("select nextval(\'"  + seqName + "\')");
        return selectQuery;
    }
    
    public void platformSpecificSequencingInitialization(DatabaseSession session) {
    }
}

要构建此代码,您需要将 Glassfish 分发中的 toplink jar 添加到您的构建类路径中。

它位于 $glassfish_install_dir/lib/toplink-essentials.jar 中。

此代码将默认 Postgres DB 平台更改为切换到与 Oracle 类似的序列行为。

只需将该类添加到项目,然后向 persistence.xml 文件添加一个属性:

<property name="toplink.platform.class.name" 
          value="faq.glassfish.postgres.PostgresPlatform"/>

现在,在您的实体中使用序列:

@Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MYSEQUENCE")
    @SequenceGenerator(name="MYSEQUENCE", sequenceName="mysequence")
    @Column(name = "id", nullable = false)
    private Integer id;

额外的好处是,也可以使用序列的 allocationSize 参数。您只需更改 Postgres 序列的增量值,以匹配分配大小。

因此,如果将 allocationSize=10 添加到 SequenceGenerator 注释,则在您的数据库中只需:

ALTER SEQUENCE mysequence INCREMENT 10;

英文