You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@IdClass时如何定义长整型ID生成器?适配MariaDB与Oracle

问题解决:@IdClass复合主键下适配MariaDB与Oracle的ID自增

报错原因分析

你遇到的IllegalArgumentException本质是:使用@IdClass定义复合主键时,Hibernate的ID生成器返回的是包装类型的标识符对象,无法直接赋值给主键类(ApiChannelConnPk)中的long基本类型字段。再加上你混用了GenerationType.IDENTITY和native生成策略,导致Hibernate在处理复合主键的ID生成时出现类型不匹配。

核心解决方案

用Hibernate提供的SequenceStyleGenerator替代原生策略,这个生成器能自动识别数据库方言:在MariaDB中自动使用AUTO_INCREMENT(IDENTITY),在Oracle中自动使用序列,无需手动切换配置。同时修正复合主键的字段赋值逻辑。

步骤1:修改实体类的ID生成配置

去掉原有的@GenericGenerator和GenerationType.IDENTITY混用配置,换成适配性更强的序列风格生成器:

@Entity
@Table(name = "api_channel_connection")
@GenericGenerator(
    name = "API_CHANNEL_CONN_GEN",
    strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
    parameters = {
            @Parameter(name = "sequence_name", value = "API_CHANNEL_CONN_SEQ"),
            @Parameter(name = "initial_value", value = "1"),
            @Parameter(name = "increment_size", value = "1"),
            // 关键配置:当数据库支持IDENTITY时自动切换,无需序列
            @Parameter(name = "force_use_sequence", value = "false")
    }
)
@IdClass(ApiChannelConnPk.class)
public class ApiChannelConn implements Serializable {

    private static final long serialVersionUID = -1704230312029411642L;

    @Id
    @Column(name="id", nullable=false)
    // 绑定自定义生成器
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "API_CHANNEL_CONN_GEN")
    private long id;

    @Id
    @Column(name = "api_channel_name", nullable = false)
    private String apiChannelName;

    // 补充所有字段的getter/setter方法
    // ...
}

步骤2:完善主键类(ApiChannelConnPk)

主键类必须满足以下要求,否则Hibernate无法正确赋值:

  1. 补充所有字段的getter/setter方法
  2. 实现equals()和hashCode()方法(基于主键字段)
  3. 字段类型必须和实体类完全一致(比如实体类是long,主键类也必须是long,不能用Long)

修改后的主键类示例:

import java.util.Objects;

public class ApiChannelConnPk implements Serializable {

    private static final long serialVersionUID = -9009457097300195124L;

    private long id;
    private String apiChannelName;

    // 补充getter/setter
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
    public String getApiChannelName() { return apiChannelName; }
    public void setApiChannelName(String apiChannelName) { this.apiChannelName = apiChannelName; }

    // 实现equals和hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ApiChannelConnPk that = (ApiChannelConnPk) o;
        return id == that.id && apiChannelName.equals(that.apiChannelName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, apiChannelName);
    }
}

步骤3:数据库脚本适配

  • MariaDB:保留原有的自增配置
CREATE TABLE api_channel_connection (
    id INT(11) NOT NULL AUTO_INCREMENT,
    api_channel_name VARCHAR(255) NOT NULL,
    node VARCHAR(255) NOT NULL,
    url VARCHAR(255) NOT NULL,
    PRIMARY KEY (id, api_channel_name)
);
  • Oracle:可以保留原有的序列和触发器,也可以去掉触发器(SequenceStyleGenerator会直接调用序列生成ID),推荐保留触发器以兼容手动插入场景:
CREATE TABLE API_CHANNEL_CONNECTION (
    ID NUMBER(11,0) NOT NULL,
    API_CHANNEL_NAME VARCHAR2(255) NOT NULL,
    NODE VARCHAR2(255) NOT NULL,
    URL VARCHAR2(255) NOT NULL,
    PRIMARY KEY (ID, API_CHANNEL_NAME)
);

CREATE SEQUENCE API_CHANNEL_CONN_SEQ START WITH 1 INCREMENT BY 1;

CREATE OR REPLACE TRIGGER API_CHANNEL_CONN_TR
BEFORE INSERT ON API_CHANNEL_CONNECTION FOR EACH ROW
WHEN (NEW.ID IS NULL)
BEGIN
    SELECT API_CHANNEL_CONN_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END;
/

关键注意事项

  • 不要在复合主键的单个字段上混用GenerationType.IDENTITY和自定义生成器,Hibernate对复合主键的ID生成逻辑只支持基于整个主键对象的处理
  • 确保Hibernate的方言配置正确:MariaDB用org.hibernate.dialect.MariaDBDialect,Oracle用org.hibernate.dialect.Oracle12cDialect(根据版本调整)
  • 主键类的序列化和equals/hashCode是必须的,否则Hibernate无法缓存和识别主键对象

内容的提问来源于stack exchange,提问作者tv.tony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:30:02