当前位置: 首页 > 知识库问答 >
问题:

JPA世代类型。自动不考虑具有自动增量的列

滑令
2023-03-14

我有一个表,其中有一个简单的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中,列被正确设置为标识,SeedIncrement等于1。

当我试图持久化这个实体的一个实例时,Hibernate会尝试查询Hibernate_sequence表以获取ID值。由于我没有在模式中创建该表,所以出现了一个错误:

无法读取高值:com。微软sqlserver。jdbc。SQLServerException:对象名“MySchema”无效。Hibernate\u序列'

如果我将generation type更改为IDENTITY,那么一切都会按预期进行

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

我不能这样更改,因为我的应用程序将同时在MS SQL和ORACLE上运行,后者不支持自动递增列。

据我所知,如果基础数据库支持自动增量行为,自动类型应该使用自动增量行为,所以我不知道为什么不工作。

更新:

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

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

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

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

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

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

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

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

共有3个答案

申屠晟
2023-03-14

是因为

@GeneratedValue(策略=GenerationType.AUTO)

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

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

洪鹏海
2023-03-14

Orcale 12c支持IDENTITY,SQLSERVER 2012支持SEQUENCES。我相信序列总是比身份更好的选择。IDENTITY禁用批处理,SEQUENCES允许您提供优化器,例如pooled-lo优化策略。

以下是为配置的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";
}

>

  • 如果使用新的标识符生成器:

    财产。put(“hibernate.id.new_generator_mappings”,“true”);

    AUTO实际上将使用序列样式生成器,如果数据库不支持序列,您最终将使用表生成器来代替(这是一个可移植的解决方案,但它的效率低于IDENTITY或SEQUENCE)。

    如果您使用遗留标识符生成器,那么您最终会使用“本机”生成策略,这意味着:

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

    如果要添加一个新的Oracle12gDialect,并且它将支持标识,那么AUTO可能会切换到标识而不是顺序,这可能会打破您当前的预期。目前还没有这样的方言,所以在Oracle上你有序列,在MSSQL上你有身份。

    结论:

    试着这样做:

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

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

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

    您也可以使用JPA表生成器,只要确保配置了正确的优化器。更多信息查看我的Hibernate教程

  • 张承颜
    2023-03-14

    我有一个类似的问题,发现了这个信息(在这里有更深入的解释)。

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

    <property name="hibernate.id.new_generator_mappings" value="false" />
    
     类似资料:
    • 问题内容: 我有多个结构相同的数据库,有时会在其中复制数据。为了保持数据完整性,我使用两列作为主键。一个是数据库ID,它链接到包含有关每个数据库的信息的表。另一个是表键。它不是唯一的,因为它可能有多个行,且此值相同,但database_id列中的值不同。 我打算将这两列变成一个联合主键。但是,我也想将表键设置为自动递增-但基于database_id列。 EG,使用以下数据: 如果我要添加包含dab

    • 问题内容: 使用Postgres,我试图用SQL自动编号主键。但是,这给了我一个错误。 错误: 知道为什么吗? 问题答案: Postgres 10或更高版本 列(请参见下文)保持不变。但是考虑一个 专栏。Postgres10实现了此标准SQL功能。 手册中的基本语法和信息。 __用列 创建 表 将 列 添加 到现有表 表可能填充也可能不填充行。 同时使它成为PK(表还不能拥有PK): 有关的: 如

    • 这就是我所拥有的 当程序运行时,会出现一个添加新类别的屏幕,用户必须写入类别的名称和描述,但ID应该是自动的,并且应该在每次用户输入新类别时增加ID。我这样做并没有达到预期效果,它只增加了一次,但由于初始值保持在5,所以它总是6。

    • 问题内容: 我刚遇到一个我无法解决的问题。 我有一个用作队列的数据库表,我在其中存储一些记录。处理记录后,它们将被删除。删除是由Rails构建在一个触发MySql数据库的循环中调用的。 但是现在我注意到,在表中将 autoIncrement Id (主键)设置回 1 。(这损坏了我在审计表中的引用。相同记录现在指向多个不同的项目队列) 我不使用或删除表构造,而是在代码中再次创建它。 是否有人遇到像

    • 问题内容: 我正在尝试在mysql数据库中创建一列,该列自动递增1但从0-Z开始然后滚动。 例如000、001、002,…,009、00A,00B,…,00Z,010,…,0ZZ,…,100。 我想让数据库通过自动递增字段创建列。 我的想法是: 为从0到36的每个字符创建一列,然后将行N(其中N是最低有效数字)自动递增1。然后在每列上添加触发器,以在第N列达到36时向N-1列添加1 。 创建一个包

    • 问题内容: 我有下面的SQL,我正在尝试使用它创建一个表和一些列。作为它的一部分,我希望其中两列自动递增一个整数。当我尝试使用以下代码时,它给我一个错误。 当我从SQL中删除关键字“ AUTOINCREMENT”时,它可以正常工作。 是否可以有两个自动增量列?如果不是,是否有办法让一列在插入时自动从另一列(自动递增)中获取值? 谢谢 问题答案: 您不能有两个自动增量字段。您应该使用一个自动增量字段