Java - JPA - 生成器 - @SequenceGenerator

Yat*_*oel 45 java jpa

我正在学习JPA并且在@SequenceGenerator注释中有困惑.

据我了解,它会自动为实体的数字标识字段/属性赋值.

Q1.此序列生成器是否利用数据库增加的数值生成功能或自己生成数字?

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

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


Q4.还请详细说明注释sequenceNameallocationSize属性@SequenceGenerator.

Kev*_*ell 60

sequenceName是DB中序列的名称.这是您指定DB中已存在的序列的方法.如果你走这条路线,你必须指定allocationSize哪个需要与DB序列用作"自动增量"的值相同.

用法:

@GeneratedValue(generator="my_seq")
@SequenceGenerator(name="my_seq",sequenceName="MY_SEQ", allocationSize=1)
Run Code Online (Sandbox Code Playgroud)

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

@GeneratedValue(strategy=GenerationType.SEQUENCE)
Run Code Online (Sandbox Code Playgroud)

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

@GeneratedValue(strategy=GenerationType.AUTO)
Run Code Online (Sandbox Code Playgroud)

  • JPA提供程序(例如Hibernate)将使用序列值作为基础,并且*withtitiply*它与allocationSize一起获取它将插入的实际ID.因此,如果下一个seq值为11且allocationSize为20,则生成的下一个ID将为220.通常,您只希望ID完全遵循序列值,因此请设置allocationSize =序列的INCREMENT BY.另见http://stackoverflow.com/questions/5346147/hibernate-oracle-sequence-produces-large-gap (5认同)

Edy*_*rre 13

我用它,它工作正常

@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;
}
Run Code Online (Sandbox Code Playgroud)


Vla*_*cea 10

现在,回到你的问题:

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

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

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

因此,不要在GenerationType.SEQUENCEMySQL 中使用该策略。

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

我假设你在谈论GenerationType.IDENTITY当你说的时候database auto-increment feature

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

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

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

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

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

第 4 季度。还请一些线索sequenceNameallocationSize 性能的@SequenceGenerator注解。

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

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

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

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

SELECT nextval('hibernate_sequence')
Run Code Online (Sandbox Code Playgroud)

allocationSize定义的标识值倍增,如果你提供一个值大于1那是,则Hibernate将使用的pooled优化,减少数据库调用序列数。

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

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

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

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

只会执行 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)
Run Code Online (Sandbox Code Playgroud)


小智 8

虽然这个问题很老,但我偶然发现了JPA 2.0和Oracle序列的问题.

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

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

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

例如,如果我们使用INCREMENT BY值20定义数据库中的序列,则将SequenceGenerator中的allocationsize也设置为20.在这种情况下,JPA将不会调用数据库,直到它到达下一个20标记,同时增加每个内部的值为1.这样可以保存数据库调用,以便每次都获取下一个序列号.这样做的副作用是 - 每当重新部署应用程序或在其间重新启动服务器时,它将调用数据库以获取下一批,并且您将看到序列值中的跳转.此外,我们需要确保数据库定义和应用程序设置是同步的,这可能无法一直进行,因为它们都由不同的组管理,您很快就会失去控制权.如果数据库值小于allocationsize,则由于重复的Id值,您将看到PrimaryKey约束错误.如果数据库值高于allocationsize,您将看到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无法改变序列,JPA每次调用数据库以获得下一个序列号,而不管@ SequenceGenerator.allocationSize的值如何.这可能是我们需要注意的不必要的后果.

要让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.否则会导致错误.


Who*_*ome 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;
Run Code Online (Sandbox Code Playgroud)

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; }
}
Run Code Online (Sandbox Code Playgroud)