【问题标题】:JPA GenerationType.AUTO not considering column with auto incrementJPA GenerationType.AUTO 不考虑具有自动增量的列
【发布时间】:2014-09-22 17:01:00
【问题描述】:

我有一个表,其中包含一个简单的 int id 列,SQL Server 中的 Identity 自动递增。

实体的 ID 用 @Id@GeneratedValue 注释

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;

在 SQL Server 中,该列被正确设置为 Identity,SeedIncrement 等于 1。

当我尝试持久化该实体的实例时,Hibernate 尝试查询 hibernate_sequence 表以获取 ID 值。由于我没有在我的架构中创建该表,我收到了一个错误:

could not read a hi value: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'MySchema.hibernate_sequence'

如果我将生成类型更改为 IDENTITY,一切都会按预期工作

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;

我无法以这种方式更改它,因为我的应用程序将同时在 MS SQL 和 ORACLE 上运行,而后者不支持自动递增的列。

据我所知,如果底层数据库支持 AUTO 类型应该使用自动递增行为,所以我不知道为什么不起作用。

更新:

我花了一些时间,但我能够准确地理解发生了什么。

我正在使用具有以下行为的旧数据库:

  • MSSQL:id 生成使用表 IDENTITY
  • ORACLE:ID 生成使用触发器。触发器查询并更新存储所有“下一个 id”的自定义表。此表称为 SEQ。

以下是使用一些 id 生成策略的结果:

  • AUTO:在 MSSQL 中不起作用,如上所述
  • IDENTITY:在 MSSQL 中工作,但不受 Oracle 支持
  • “native”:在 MSSQL 中有效,但在 ORACLE 中失败。它失败是因为 Hibernate 激活了它的默认序列策略,它使用 hibernate_sequences.nextval。由于这是一个遗留应用程序,来自 SEQ 表(如上所述)和 hibernate_sequences 的值不同步(该特定表的 SEQ 值为 6120,hibernate_sequences 为 1,这是预期的因为它直到现在才使用)。

所以我需要弄清楚的是一种配置该实体的方法:

  • 使用 MSSQL 身份功能 或
  • 使用 Oracle 时,不要自动为 ID 变量设置任何值,而是将所有内容留给预先存在的触发器

当我需要插入依赖于主实体(通过外键)的实体时,这可能会导致我在 Oracle 上出现严重问题,因为 Hibernate 不知道哪个 ID 值是由“外部”触发器生成的。

【问题讨论】:

    标签: java sql-server database hibernate jpa


    【解决方案1】:

    Orcale 12c 支持 IDENTITY,SQL SERVER 2012 支持 SEQUENCES。我相信SEQUENCE is always a better choice than an IDENTITY。 IDENTITY 禁用批处理,而 SEQUENCES 允许您提供优化器,例如 pooled-lo optimization strategy

    这是为配置的 GenerationType 值选择实际标识符生成器的方式:

    switch ( generatorEnum ) {
        case IDENTITY:
            return "identity";
        case AUTO:
            return useNewGeneratorMappings
                    ? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
                    : "native";
        case TABLE:
            return useNewGeneratorMappings
                    ? org.hibernate.id.enhanced.TableGenerator.class.getName()
                    : MultipleHiLoPerTableGenerator.class.getName();
        case SEQUENCE:
            return useNewGeneratorMappings
                    ? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
                    : "seqhilo";
    }
    
    • 如果您使用new identifier generators

      properties.put("hibernate.id.new_generator_mappings", "true");

      AUTO 实际上会使用 SequenceStyleGenerator,而在数据库不支持序列的情况下,您最终会改用 TABLE 生成器(这是一种可移植的解决方案,但效率低于 IDENTITY 或 SEQUENCE)。

    • 如果您使用旧的标识符生成器,​​那么您最终会使用“原生”生成策略,这意味着:

      public Class getNativeIdentifierGeneratorClass() {
          if ( supportsIdentityColumns() ) {
              return IdentityGenerator.class;
          }
          else if ( supportsSequences() ) {
              return SequenceGenerator.class;
          }
          else {
              return TableHiLoGenerator.class;
          }
      }   
      

    如果要添加新的 Oracle12gDialect 并且它将支持 IDENTITY,那么 AUTO 可能会切换到 IDENTITY 而不是 SEQUENCE,这可能会打破您当前的期望。目前没有这样的方言可用,所以在 Oracle 上你有 SEQUENCE,而在 MSSQL 中你有 IDENTITY。

    结论:

    试试这样:

     @Id
     @GenericGenerator(name = "native_generator", strategy = "native")
     @GeneratedValue(generator = "native_generator")
     private Long id;
    
    • 将 id 设为 Long 而不是 Integer,您可以让 HBMDDL 处理主键列类型。
    • 强制 Hibernate 使用“本机”生成器

    如果您的旧系统使用表来生成序列值并且没有使用过 hilo 优化,您可以使用表标识符生成器:

    @Id
    @GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
    @TableGenerator(name = "table", allocationSize = 1
    )
    private Long id;
    

    您也可以使用 JPA 表生成器,只需确保配置了正确的优化器。欲了解更多信息,请查看我的Hibernate tutorial

    【讨论】:

    • 感谢您的回答,但我无法正确回答我的问题。如何指示 hibernate 在支持时使用 SQL Server IDENTITY 策略,在不支持时回退到 SEQUENCE、TABLE 或底层数据库支持的任何策略?
    • 检查我的更新回复,“结论”部分。
    • 它也应该在 oracle 上工作,只是它会使用 hibernate_sequence。您不应该依赖 hbmddl 来发展您的架构。使用 flywaydb 或 liquibase 和增量脚本。这样您就可以很好地自定义 Oracle 数据库序列。这个通用生成器可以接受参数,因此您也可以为 Oracle 自定义模式名称
    • 检查我的更新回复。您可以设置表格生成器。 Hibernate 有很多,既有非 hilo 遗留,也有 hilo 一个,每个 table 使用 hilo 的多序列,以及一个采用可配置优化器的新增强序列。
    • 您需要编写自己的 Hibernate IdentifierGenerator 并从 IDENTITY 切换到 TABLE。如果数据库不支持身份,则切换到表。您可以使用 GenericGenerator 参数来自定义您的自定义生成器,以配置表生成器属性。在内部,您的自定义生成器可以简单地实例化遗留身份和表生成器,并且只需将生成委托给其中一个,具体取决于您当前使用的方言。
    【解决方案2】:

    我遇到了类似的问题,发现了这个information(在here 中有更深入的解释)。

    将此属性添加到我的 persistence.xml 文件中解决了这个问题:

    <property name="hibernate.id.new_generator_mappings" value="false" />
    

    【讨论】:

    • 这对我不起作用。 Hibernate 仍然希望它是该死的“hibernate_sequence”表.. 让我抓狂
    • @sashok_bg 您使用的是什么应用服务器?我已经用 Wildfly-8.2.1.Final 测试过了
    • 对我来说效果很好。谢谢。使用 Wildfly 10
    【解决方案3】:

    因为

    @GeneratedValue(strategy = GenerationType.AUTO)

    早期版本默认使用SequenceStyleGenerator

    你必须看看这个https://hibernate.atlassian.net/browse/HHH-11014

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 2017-05-11
      • 2018-07-10
      • 1970-01-01
      • 2019-10-12
      • 1970-01-01
      • 2014-05-08
      • 2019-08-26
      • 1970-01-01
      相关资源
      最近更新 更多