JPA @SequenceGenerator 注解如何工作

发布于 2024-08-28 12:55:45 字数 470 浏览 7 评论 0原文

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

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

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

Q2. 如果 JPA 使用数据库自​​动增量功能,那么它是否可以与没有自动增量功能的数据存储一起使用?

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


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

I am learning JPA and have confusion in the @SequenceGenerator annotation.

To my understanding, it automatically assigns a value to the numeric identity fields/properties of an entity.

Q1. Does this sequence generator make use of the database's increasing numeric value generating capability or generates the number on its own?

Q2. If JPA uses a database auto-increment feature, then will it work with datastores that don't have an auto-increment feature?

Q3. If JPA generates numeric value on his own, then how does the JPA implementation know which value to generate next? Does it consult with the database first to see what value was stored last in order to generate the value (last + 1)?


Q4. Please also shed some light on sequenceName and allocationSize properties of @SequenceGenerator annotation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

回忆躺在深渊里 2024-09-04 12:55:46

sequenceName 是数据库中序列的名称。这是指定数据库中已存在的序列的方式。如果您采用此路线,则必须指定 allocationSize,该值需要与数据库序列用作其“自动增量”的值相同。

用法:

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

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

@GeneratedValue(strategy=GenerationType.SEQUENCE)

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

@GeneratedValue(strategy=GenerationType.AUTO)

sequenceName is the name of the sequence in the DB. This is how you specify a sequence that already exists in the DB. If you go this route, you have to specify the allocationSize which needs to be the same value that the DB sequence uses as its "auto increment".

Usage:

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

If you want, you can let it create a sequence for you. But to do this, you must use SchemaGeneration to have it created. To do this, use:

@GeneratedValue(strategy=GenerationType.SEQUENCE)

Also, you can use the auto-generation, which will use a table to generate the IDs. You must also use SchemaGeneration at some point when using this feature, so the generator table can be created. To do this, use:

@GeneratedValue(strategy=GenerationType.AUTO)
甜味拾荒者 2024-09-04 12:55:46

现在,回到你的问题:

Q1。该序列生成器是否利用数据库的递增
数值生成能力或生成其上的数字
自己的?

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

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

因此,不要对 MySQL 使用 GenerationType.SEQUENCE 策略。

第二季度。如果JPA使用数据库自​​增功能,那么它可以工作吗
数据存储没有自动增量功能?

当您说数据库自动增量功能时,我假设您正在谈论GenerationType.IDENTITY

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

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

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

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

为了避免 Hibernate 标识符优化器和其他第 3 方客户端之间的冲突,您应该使用 pooledpooled-lo 而不是 hi/lo。即使您使用的是设计为使用 hi/lo 的旧应用程序,您也可以迁移到pooledpooled-lo 优化器

第四季度。另请说明 sequenceNameallocationSize
@SequenceGenerator 注释的属性。

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

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

@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 将使用池化优化器来减少数据库序列调用的数量。

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

@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)

Now, back to your questions:

Q1. Does this sequence generator make use of the database's increasing
numeric value generating capability or generates the number on its
own?

By using the GenerationType.SEQUENCE strategy on the @GeneratedValue annotation, the JPA provider will try to use a database sequence object of the underlying database that supports this feature (e.g., Oracle, SQL Server, PostgreSQL, MariaDB).

If you are using MySQL, which doesn't support database sequence objects, then Hibernate is going to fall back to using the GenerationType.TABLE instead, which is undesirable since the TABLE generation performs badly.

So, don't use the GenerationType.SEQUENCE strategy with MySQL.

Q2. If JPA uses a database auto-increment feature, then will it work
with datastores that don't have auto-increment feature?

I assume you are talking about the GenerationType.IDENTITY when you say database auto-increment feature.

To use an AUTO_INCREMENT or IDENTITY column, you need to use the GenerationType.IDENTITYstrategy on the @GeneratedValue annotation.

Q3. If JPA generates numeric value on its own, then how does the JPA
implementation know which value to generate next? Does it consult with
the database first to see what value was stored last in order to
generate the value (last + 1)?

The only time when the JPA provider generates values on its own is when you are using the sequence-based optimizers, like:

These optimizers are meat to reduce the number of database sequence calls, so they multiply the number of identifier values that can be generated using a single database sequence call.

To avoid conflicts between Hibernate identifier optimizers and other 3rd-party clients, you should use pooled or pooled-lo instead of hi/lo. Even if you are using a legacy application that was designed to use hi/lo, you can migrate to the pooled or pooled-lo optimizers.

Q4. Please also shed some light on sequenceName and allocationSize
properties of @SequenceGenerator annotation.

The sequenceName attribute defines the database sequence object to be used to generate the identifier values. IT's the object you created using the CREATE SEQUENCE DDL statement.

So, if you provide this mapping:

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

Hibernate is going to use the seq_post database object to generate the identifier values:

SELECT nextval('hibernate_sequence')

The allocationSize defines the identifier value multiplier, and if you provide a value that's greater than 1, then Hibernate is going to use the pooled optimizer, to reduce the number of database sequence calls.

So, if you provide this mapping:

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

Then, when you persist 5 entities:

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

Only 2 database sequence calls will be executed, instead of 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)
守望孤独 2024-09-04 12:55:46

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

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

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

确保将@SequenceGenerator(allocationSize)设置为与数据库序列定义中的INCRMENT 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,对象就会保存到数据库中,并且 id 的值会分配给返回的对象,因此我们不必执行em.mergeem.flush。 (这可能是 JPA 提供程序特定的......不确定)

另一件重要的事情 -

JPA 2.0 自动运行ALTER SEQUENCE 命令来同步数据库序列中的allocationSize 和INCRMENT 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。这意味着“ALTER SEQUENCE...INCRMENT BY”SQL
不会对序列“RECORD_ID_SEQ”执行语句。 OpenJPA
执行此命令以确保序列的 INCREMENT BY 值
数据库中定义的分配大小与中定义的分配大小匹配
实体的序列。禁用此 SQL 语句后,它是
用户有责任确保实体的顺序
定义与数据库中定义的序列匹配。

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

Even though this question is very old and I stumbled upon it for my own issues with JPA 2.0 and Oracle sequences.

Want to share my research on some of the things -

Relationship between @SequenceGenerator(allocationSize) of GenerationType.SEQUENCE and INCREMENT BY in database sequence definition

Make sure @SequenceGenerator(allocationSize) is set to same value as INCREMENT BY in Database sequence definition to avoid problems (the same applies to the initial value).

For example, if we define the sequence in database with a INCREMENT BY value of 20, set the allocationsize in SequenceGenerator also to 20. In this case the JPA will not make a call to database until it reaches the next 20 mark while it increments each value by 1 internally. This saves database calls to get the next sequence number each time.
The side effect of this is - Whenever the application is redeployed or the server is restarted in between, it'll call database to get the next batch and you'll see jumps in the sequence values. Also we need to make sure the database definition and the application setting to be in-sync which may not be possible all the time as both of them are managed by different groups and you can quickly lose control of. If database value is less than the allocationsize, you'll see PrimaryKey constraint errors due to duplicate values of Id. If the database value is higher than the allocationsize, you'll see jumps in the values of Id.

If the database sequence INCREMENT BY is set to 1 (which is what DBAs generally do), set the allocationSize as also 1 so that they are in-sync but the JPA calls database to get next sequence number each time.

If you don't want the call to database each time, use GenerationType.IDENTITY strategy and have the @Id value set by database trigger. With GenerationType.IDENTITY as soon as we call em.persist the object is saved to DB and a value to id is assigned to the returned object so we don't have to do a em.merge or em.flush. (This may be JPA provider specific..Not sure)

Another important thing -

JPA 2.0 automatically runs ALTER SEQUENCE command to sync the allocationSize and INCREMENT BY in database sequence. As mostly we use a different Schema name(Application user name) rather than the actual Schema where the sequence exists and the application user name will not have ALTER SEQUENCE privileges, you might see the below warning in the logs -

000004c1 Runtime W CWWJP9991W: openjpa.Runtime: Warn: Unable
to cache sequence values for sequence "RECORD_ID_SEQ". Your
application does not have permission to run an ALTER SEQUENCE command.
Ensure that it has the appropriate permission to run an ALTER SEQUENCE
command.

As the JPA could not alter the sequence, JPA calls database everytime to get next sequence number irrespective of the value of @SequenceGenerator.allocationSize. This might be a unwanted consequence which we need to be aware of.

To let JPA not to run this command, set this value - in persistence.xml. This ensures that JPA will not try to run ALTER SEQUENCE command. It writes a different warning though -

00000094 Runtime W CWWJP9991W: openjpa.Runtime: Warn: The
property "openjpa.jdbc.DBDictionary=disableAlterSeqenceIncrementBy" is
set to true. This means that the 'ALTER SEQUENCE...INCREMENT BY' SQL
statement will not be executed for sequence "RECORD_ID_SEQ". OpenJPA
executes this command to ensure that the sequence's INCREMENT BY value
defined in the database matches the allocationSize which is defined in
the entity's sequence. With this SQL statement disabled, it is the
responsibility of the user to ensure that the entity's sequence
definition matches the sequence defined in the database.

As noted in the warning, important here is we need to make sure @SequenceGenerator.allocationSize and INCREMENT BY in database sequence definition are in sync including the default value of @SequenceGenerator(allocationSize) which is 50. Otherwise it'll cause errors.

美羊羊 2024-09-04 12:55:46

我用这个并且效果很好

@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;
}

I use this and it works right

@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;
}
扶醉桌前 2024-09-04 12:55:46

我有带有 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; }
}

I have MySQL schema with autogen values. I use strategy=GenerationType.IDENTITY tag and seems to work fine in MySQL I guess it should work most db engines as well.

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; }
}
狼亦尘 2024-09-04 12:55:46

创建表后,我必须在 postgresql 上创建一个序列。
请记住,让 spring 处理数据库序列并不是一个好主意。
删除 @GenerateValue(strategy=GenerationType.SEQUENCE),

添加到您的模型上

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

并在 SQL 上运行

CREATE SEQUENCE my_seq
INCREMENT BY 1;

I had to create a sequence on postgresql after creating a table.
Remember, its not a good idea keep spring taking care of DB sequence.
Remove @GeneratedValue(strategy=GenerationType.SEQUENCE),

add on your model

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

and run on SQL

CREATE SEQUENCE my_seq
INCREMENT BY 1;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文