使用@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无法正确赋值:
- 补充所有字段的getter/setter方法
- 实现
equals()和hashCode()方法(基于主键字段) - 字段类型必须和实体类完全一致(比如实体类是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
相关产品推荐
相关产品推荐

