JPA GenerationType.AUTO没有考虑具有自动增量的列

时间:2014-07-30 21:23:49

标签: java sql-server database hibernate jpa

我有一个带有简单int id列的表,在SQL Server中具有Identity auto increment。

实体的ID已注明@Id@GeneratedValue

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

在SQL Server中,该列已正确设置为标识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生成使用触发器。触发器查询并更新自定义表,其中包含所有" next id"存储。该表称为SEQ。

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

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

所以我需要弄清楚的是将该实体配置为:

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

当我需要插入依赖于主实体的实体(通过外键)时,这可能会导致我在Oracle上出现严重问题,因为Hibernate不知道" external&#生成了哪个ID值34;触发。

3 个答案:

答案 0 :(得分:9)

我遇到了类似的问题并发现了informationhere中更深入解释)。

将此属性添加到我的persistence.xml文件中修复了问题:

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

答案 1 :(得分:6)

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(&#34; hibernate.id.new_generator_mappings&#34;,&#34; true&#34;);

    AUTO实际上会使用SequenceStyleGenerator,而数据库不支持序列,你最终会使用TABLE生成器(这是一个便携式解决方案,但它的效率低于IDENTITY或SEQUENCE)。

  • 如果您使用旧标识符生成器,​​则最终会使用&#34; native&#34;生成策略,意思是:

    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使用&#34; native&#34;发电机

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

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

您也可以使用JPA表生成器,只需确保配置正确的优化器。有关详细信息,请查看我的Hibernate tutorial

答案 2 :(得分:0)

因为

@GeneratedValue(strategy = GenerationType.AUTO)

默认情况下在早期版本中使用SequenceStyleGenerator

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