64

我正在学习 JPA 并且对@SequenceGenerator注释感到困惑。

据我了解,它会自动为实体的数字身份字段/属性分配一个值。

Q1。这个序列生成器是利用数据库不断增加的数值生成能力还是自己生成数字?

Q2。如果 JPA 使用数据库自​​动增量功能,那么它是否适用于没有自动增量功能的数据存储?

Q3。如果 JPA 自己生成数值,那么 JPA 实现如何知道接下来要生成哪个值?它是否首先咨询数据库以查看最后存储的值以生成值(last + 1)?


Q4。还请阐明注释的属性sequenceNameallocationSize属性。@SequenceGenerator

4

5 回答 5

69

sequenceName是数据库中序列的名称。这是您指定数据库中已存在的序列的方式。如果你走这条路,你必须指定allocationSize哪个需要与 DB 序列用作其“自动增量”的值相同。

用法:

@GeneratedValue(generator="my_seq")
@SequenceGenerator(name="my_seq",sequenceName="MY_SEQ", allocationSize=1)

如果你愿意,你可以让它为你创建一个序列。但要做到这一点,您必须使用 SchemaGeneration 来创建它。为此,请使用:

@GeneratedValue(strategy=GenerationType.SEQUENCE)

此外,您可以使用自动生成,它将使用表来生成 ID。在使用此功能时,您还必须在某些时候使用 SchemaGeneration,以便可以创建生成器表。为此,请使用:

@GeneratedValue(strategy=GenerationType.AUTO)
于 2010-04-07T19:13:17.140 回答
15

我用这个,它工作正常

@Id
@GeneratedValue(generator = "SEC_ODON", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "SEC_ODON", sequenceName = "SO.SEC_ODON",allocationSize=1)
@Column(name="ID_ODON", unique=true, nullable=false, precision=10, scale=0)
public Long getIdOdon() {
    return this.idOdon;
}
于 2013-08-05T23:06:59.420 回答
15

尽管这个问题已经很老了,我偶然发现它是因为我自己的 JPA 2.0 和 Oracle 序列问题。

想分享我对一些事情的研究——

数据库序列定义中GenerationType.SEQUENCE的@SequenceGenerator(allocationSize)INCREMENT BY的关系

确保@SequenceGenerator(allocationSize)设置为与数据库序列定义中的INCREMENT BY相同的值以避免出现问题(同样适用于初始值)。

例如,如果我们在数据库中使用 INCREMENT BY 值 20 定义序列,则将 SequenceGenerator 中的分配大小也设置为 20。在这种情况下,JPA 将不会调用数据库,直到它达到下一个 20 标记,同时它递增每个标记内部值加 1。这节省了每次获取下一个序列号的数据库调用。这样做的副作用是 - 每当重新部署应用程序或重新启动服务器时,它都会调用数据库来获取下一批,您会看到序列值的跳跃。此外,我们需要确保数据库定义和应用程序设置保持同步,这可能无法始终保持同步,因为它们都由不同的组管理,您很快就会失去控制。如果数据库值小于分配大小,你' 由于 Id 的重复值,您将看到 PrimaryKey 约束错误。如果数据库值高于分配大小,您将看到 Id 值的跳跃。

如果数据库序列 INCREMENT BY 设置为 1(这是 DBA 通常所做的),请将 allocationSize 也设置为 1,以便它们同步,但 JPA 每次调用数据库以获取下一个序列号。

如果您不希望每次都调用数据库,请使用GenerationType.IDENTITY策略并由数据库触发器设置 @Id 值。使用GenerationType.IDENTITY,只要我们调用em.persist,对象就会保存到 DB,并且 id 的值会分配给返回的对象,因此我们不必执行em.mergeem.flush。(这可能是特定于 JPA 提供程序的..不确定)

还有一件很重要的事——

JPA 2.0 自动运行ALTER SEQUENCE命令以同步数据库序列中的 allocationSize 和 INCREMENT BY。由于大多数情况下我们使用不同的架构名称(应用程序用户名)而不是序列存在的实际架构,并且应用程序用户名将没有 ALTER SEQUENCE 权限,您可能会在日志中看到以下警告 -

000004c1 运行时 W CWWJP9991W:openjpa.Runtime:警告:无法缓存序列“RECORD_ID_SEQ”的序列值。您的应用程序无权运行 ALTER SEQUENCE 命令。确保它具有运行 ALTER SEQUENCE 命令的适当权限。

由于 JPA 无法更改序列,因此无论@SequenceGenerator.allocationSize 的值如何,JPA 每次都会调用数据库以获取下一个序列号。这可能是我们需要注意的不良后果。

要让 JPA 不运行此命令,请在 persistence.xml 中设置此值。这确保 JPA 不会尝试运行 ALTER SEQUENCE 命令。它虽然写了一个不同的警告 -

00000094 运行时 W CWWJP9991W:openjpa.Runtime:警告:属性“openjpa.jdbc.DBDictionary=disableAlterSeqenceIncrementBy”设置为 true。这意味着不会为序列“RECORD_ID_SEQ”执行“ALTER SEQUENCE...INCREMENT BY”SQL 语句。OpenJPA 执行此命令以确保数据库中定义的序列的 INCREMENT BY 值与实体序列中定义的 allocationSize 匹配。禁用此 SQL 语句后,用户有责任确保实体的序列定义与数据库中定义的序列匹配。

如警告中所述,重要的是我们需要确保数据库序列定义中的@SequenceGenerator.allocationSize 和INCREMENT BY 同步,包括@SequenceGenerator(allocationSize) 的默认值50。否则会导致错误。

于 2017-03-16T15:07:05.043 回答
11

现在,回到你的问题:

Q1。这个序列生成器是利用数据库不断增加的数值生成能力还是自己生成数字?

通过使用注释GenerationType.SEQUENCE上的策略@GeneratedValue,JPA 提供者将尝试使用支持此功能的底层数据库(例如,Oracle、SQL Server、PostgreSQL、MariaDB)的数据库序列对象。

如果您使用的是不支持数据库序列对象的 MySQL,那么 Hibernate 将回退到使用 the GenerationType.TABLEinstead,这是不可取的,因为 TABLE 生成性能很差。

所以,不要在GenerationType.SEQUENCEMySQL 中使用该策略。

Q2。如果 JPA 使用数据库自​​动增量功能,那么它是否适用于没有自动增量功能的数据存储?

我假设您说的是GenerationType.IDENTITY当您说database auto-increment feature.

要使用AUTO_INCREMENTorIDENTITY列,您需要在注释上使用GenerationType.IDENTITY策略。@GeneratedValue

Q3。如果 JPA 自己生成数值,那么 JPA 实现如何知道接下来要生成哪个值?它是否首先咨询数据库以查看最后存储的值以生成值(last + 1)?

JPA 提供程序自己生成值的唯一时间是当您使用基于序列的优化器时,例如:

这些优化器可以减少数据库序列调用的数量,因此它们将使用单个数据库序列调用可以生成的标识符值的数量相乘。

为了避免 Hibernate 标识符优化器和其他 3rd-party 客户端之间的冲突,您应该使用pooledorpooled-lo代替hi/lo. 即使您使用的是设计为使用 hi/lo 的遗留应用程序,您也可以迁移到pooledpooled-lo优化器

Q4。还请阐明注释的属性sequenceNameallocationSize 属性。@SequenceGenerator

sequenceName属性定义了用于生成标识符值的数据库序列对象。IT 是您使用CREATE SEQUENCEDDL 语句创建的对象。

因此,如果您提供此映射:

@Id
@GeneratedValue(
    strategy = GenerationType.SEQUENCE,
    generator = "seq_post"
)
@SequenceGenerator(
    name = "seq_post"
)
private Long id;

Hibernate 将使用seq_post数据库对象来生成标识符值:

SELECT nextval('hibernate_sequence')

定义标识符值乘数,如果您提供的allocationSize值大于 1,那么 Hibernate 将使用pooled优化器来减少数据库序列调用的次数。

因此,如果您提供此映射:

@Id
@GeneratedValue(
    strategy = GenerationType.SEQUENCE,
    generator = "seq_post"
)
@SequenceGenerator(
    name = "seq_post",
    allocationSize = 5
)
private Long id;

然后,当您持久化 5 个实体时:

for (int i = 1; i <= 5; i++) {
    entityManager.persist(
        new Post().setTitle(
            String.format(
                "High-Performance Java Persistence, Part %d",
                i
            )
        )
    );
}

只会执行 2 个数据库序列调用,而不是 5 个:

SELECT nextval('hibernate_sequence')
SELECT nextval('hibernate_sequence')
 
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 1', 1)
 
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 2', 2)
 
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 3', 3)
 
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 4', 4)
 
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 5', 5)
于 2020-07-05T08:40:41.380 回答
6

我有带有 autogen 值的 MySQL 模式。我使用strategy=GenerationType.IDENTITY标签并且似乎在 MySQL 中工作正常我想它也应该适用于大多数数据库引擎。

CREATE TABLE user (
    id bigint NOT NULL auto_increment,
    name varchar(64) NOT NULL default '',
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

User.java

// mark this JavaBean to be JPA scoped class
@Entity
@Table(name="user")
public class User {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;    // primary key (autogen surrogate)

    @Column(name="name")
    private String name;

    public long getId() { return id; }
    public void setId(long id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name=name; }
}
于 2013-03-26T19:44:39.457 回答