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

我有一个表与一个简单的int id列在SQL Server中具有标识自动增量。

实体的Id使用@Id@GeneratedValue注释

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

在SQL Server中,该列已正确设置为Identity with 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身份function或
  • 使用Oracle时,不要自动为ID变量设置任何值,并将所有内容保留到预先存在的触发器

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

我遇到了类似的问题并找到了这些信息 ( 这里有更深入的解释)。

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

  

Orcale 12c支持IDENTITY,SQL SERVER 2012支持SEQUENCES。 我相信SEQUENCE总是比IDENTITY更好的选择 。 IDENTITY禁用批处理,SEQUENCES允许您提供优化程序,例如池化优化策略 。

这是为配置的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"; } 
  • 如果您使用新的标识符生成器 :

    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教程

因为

@GeneratedValue(strategy = GenerationType.AUTO)

在早期版本中默认使用SequenceStyleGenerator

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