【问题标题】:Gaps In the sequence values generated by JPA @GeneratedValue with PostgreSQLJPA @GeneratedValue 与 PostgreSQL 生成的序列值中的差距
【发布时间】:2014-05-12 07:14:48
【问题描述】:

对于我的桌面应用程序,我使用 JavaFX、Spring、JPA + Hibernate 和 PostgreSQL。目前我遇到了几个问题。

问题一:违反PRIMARY KEY约束SQL错误

当我按照以下方式(GenerationType 为 AUTO)创建实体类时,它可以正常工作。但是当我创建一个新的数据库并使用 sql 脚本添加一些测试数据(如下图所示)并尝试使用我的应用程序插入一些数据时,我遇到了“违反主键约束”的 SQL 错误。这意味着 Hibernate 似乎试图生成已经可用的 PK 值(与我的测试数据一起分配,例如 1、2、3 等)。但是经过 5 次尝试(超过了测试数据最大 pk 值)就可以了,开始插入 PK 键值为 6 的数据。

实体类 - GenerationType 为 AUTO

@Entity
@Table(name = "devicetype")
public class Devicetype implements Serializable {

    @Id
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

}

带有初始测试数据的表格

EntityManager 工厂

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
    <property name="packagesToScan" value="com.core.domain" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.PostgresPlusDialect</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="hibernate.format_sql">false</prop>
            <prop key="hibernate.use_sql_comments">false</prop>
            <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
        </props>
    </property>
</bean>  

问题二:序列值的差距

为了解决错误,我根据以下方式更改了实体类(GenerationType 为 SEQUENCE)并完成了相同的步骤(使用 sql 脚本插入初始测试数据并尝试通过应用程序插入数据)。然后数据被插入,没有任何异常。现在我的表包含通过脚本和应用程序插入的记录(如下图所示)。但是通过应用程序新添加的数据(我在浅蓝色上突出显示)具有非常高的 PK 值(从 184 开始,而不是从 6 开始)。这意味着“GenerationType as SEQUENCE”似乎休眠不按顺序填充 ID 值(保持一些间隙)。当我通过应用程序进一步添加一些数据时,它似乎会插入具有另一个更高 ID 值的数据(不是从 214 开始)。这意味着似乎 ID 没有按顺序递增。

实体类 - GenerationType 为 SEQUENCE

@Entity
@Table(name = "device_type")
public class DeviceType implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name="my_seq", sequenceName="MY_SEQ", allocationSize=1, initialValue=1)
    @Basic(optional = false)
    @Column(name = "ID")
    private Integer id;
 }

有间隙的表格数据(用于序列 ID)

【问题讨论】:

标签: java sql hibernate postgresql jpa


【解决方案1】:

这就是序列(以及 PostgreSQL SERIAL 类型)的行为方式。

如果您手动插入值,则需要update the sequence accordingly。或者,更常见的是,不要手动插入值,而是让序列来完成。

除非您想在每次插入时锁定表并扼杀任何并发希望,否则间隙是不可避免的。我建议不要在意。

请记住 - 数字没有任何意义,它们只是一个方便的标识符。

花几分钟时间了解这一切是如何运作的:CREATE SEQUENCEALTER SEQUENCE

【讨论】:

  • 此外,Hibernate 默认以 50 个块为单位分配序列值,作为插入性能的优化。您可以修改 @SequenceGenerator 声明以覆盖它。
  • @CraigRinger 感谢您的评论。是的,我已经使用 allocationSize=1
  • @Richard 感谢您的反馈。当然,我会参考这些资源。我注意到即使我们不手动输入任何数据,它也会开始插入带有一些黑线鳕值的数据起始 ID 值(不是从 1 开始,而是从 10 开始)
  • @Channa:如果序列是使用CACHE 选项创建的,Postgres 也可能会缓存值。这些值以每个连接为基础进行缓存。因此,如果序列上有CACHE,连接到数据库,插入一行然后断开连接,这些缓存值也会丢失。
  • @Channa 如果您对序列值有特定要求,您应该为每个表创建一个自定义@SequenceGenerator,并在您的@GeneratedValue 中使用它。如果您在测试期间重建数据库,如果您关心起始值,您可能希望使用 SELECT setval(...) 重置序列起始值。
猜你喜欢
  • 1970-01-01
  • 2011-09-15
  • 2018-11-10
  • 2020-10-25
  • 2017-09-21
  • 1970-01-01
  • 2021-12-14
  • 2012-08-13
  • 2020-10-14
相关资源
最近更新 更多