Java - JPA - 生成器 - @SequenceGenerator

时间:2010-04-07 19:00:39

标签: java jpa

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

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

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

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

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

<小时/> 问题4。请同时了解sequenceName注释的allocationSize@SequenceGenerator属性。

5 个答案:

答案 0 :(得分:54)

sequenceName是数据库中序列的名称。这是您指定DB中已存在的序列的方法。如果你走这条路线,你必须指定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)

答案 1 :(得分:11)

我使用它并且它正常工作

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

答案 2 :(得分:7)

即使这个问题很老,我也偶然发现了我自己在JPA 2.0和Oracle序列中遇到的问题。

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

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

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

例如,如果我们使用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 ,对象就会保存到数据库中,并且将id值赋给返回的对象,因此我们不会必须做 em.merge em.flush 。 (这可能是JPA提供商特定的......不确定)

另一件重要的事情 -

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

  

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

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

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

  

00000094运行时W CWWJP9991W:openjpa.Runtime:警告:   property&#34; openjpa.jdbc.DBDictionary = disableAlterSeqenceIncrementBy&#34;是   设为true。这意味着&#39; ALTER SEQUENCE ... INCREMENT BY&#39; SQL   语句不会执行序列&#34; RECORD_ID_SEQ&#34;。 OpenJPA的   执行此命令以确保序列的INCREMENT BY值   在数据库中定义的,匹配在其中定义的allocationSize   实体的序列。禁用此SQL语句后,它就是   用户有责任确保实体的顺序   定义与数据库中定义的序列匹配。

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

答案 3 :(得分:3)

我有自动化值的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; }
}

答案 4 :(得分:3)

由于这些问题在使用JPA ad Hibernate时非常常见,因此该答案基于我在博客上写的this article

现在,回到您的问题:

Q1。此序列生成器是否利用数据库的增量 数值生成能力或在其上生成数字 拥有吗?

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

如果您使用的MySQL不支持数据库序列对象,那么Hibernate将转而使用GenerationType.TABLE,这是不理想的,因为tABELE一代performs badly

因此,请勿在MySQL中使用GenerationType.SEQUENCE策略。

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

我假设您在说GenerationType.IDENTITY时是在谈论database auto-increment feature

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

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

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

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

为避免Hibernate标识符优化器与其他第三方客户端之间发生冲突,应使用pooledpooled-lo而不是hi/lo。即使您使用的是设计为使用高/低的旧版应用程序,也可以迁移到pooledpooled-lo优化器,如this article中所述。

Q4。还请阐明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将使用pooled优化器来减少数据库序列调用的次数。 / p>

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

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

查看this article了解更多详细信息。