使用GenerationType.TABLE策略时,如何在外部SQL文件自动生成ID插入数据?
问题
在学校项目中使用Hibernate,采用TABLE_PER_CLASS继承策略,父类Mensajes通过GenerationType.TABLE策略(对应mid_seq表)生成ID,数据库为MySQL。通过外部SQL文件插入数据时遇到两个问题:
- 省略
id字段插入时,提示字段'id'没有默认值 - 直接取
mid_seq的next_val作为ID插入时,出现主键重复,条目'0'违反唯一约束报错
相关代码与SQL
父类Mensajes代码
@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Mensajes { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "id_gen") @TableGenerator(table = "mid_seq", name = "id_gen", initialValue = 0, allocationSize = 1) private Long id; private String fecha, cont; //...getters, setters, etc
子类Resenia代码
@Entity public class Resenia extends Mensajes{ @ColumnDefault("0") private int rating; //...getters, setters, etc
子类Post代码
@Entity public class Post extends Mensajes { @ColumnDefault("0") private int votos; //...getters, setters, etc
Hibernate生成的mid_seq表SQL
create table mid_seq ( next_val bigint, sequence_name varchar(255) not null, primary key (sequence_name) ) engine=InnoDB insert into mid_seq(sequence_name, next_val) values ('mensajes',0)
尝试的插入语句及报错
直接取next_val插入:
insert into resenia(id, fecha, cont, rating) values ((select next_val from mid_seq), "01/01/2000", "BUU", 2); insert into resenia(id, fecha, cont, rating) values ((select next_val from mid_seq), "01/01/2003", "bien", 4);
报错:
SQL Error [1062] [23000]: Duplicate entry '0' for key 'PRIMARY'
省略id插入:
insert into resenia(fecha, cont, rating) values ("01/01/2000", "BUU", 2);
报错:
SQL Error [1364] [HY000]: Field 'id' doesn't have a default value
解决方案与分析
一、SQL插入时自动生成ID的方法
问题核心是手动取next_val不会自动更新序列值,而Hibernate的TABLE生成策略是自行维护序列,因此手动插入时需要同步维护序列的递增:
事务式更新+插入
先更新序列值,再用更新后的值插入,确保每次ID唯一:-- 先递增序列 update mid_seq set next_val = next_val + 1 where sequence_name = 'mensajes'; -- 获取递增后的ID select next_val from mid_seq where sequence_name = 'mensajes'; -- 执行插入 insert into resenia(id, fecha, cont, rating) values ([上述查询结果], "01/01/2000", "BUU", 2);也可以用
23598简化为单流程操作:update mid_seq set next_val = LAST_INSERT_ID(next_val + 1) where sequence_name = 'mensajes'; insert into resenia(id, fecha, cont, rating) values (23598, "01/01/2000", "BUU", 2);给表ID字段绑定序列函数
创建MySQL函数自动获取并递增序列值,再将子类表的id字段默认值设为该函数返回值:-- 创建序列函数 DELIMITER // CREATE FUNCTION get_next_mensajes_id() RETURNS BIGINT BEGIN DECLARE next_id BIGINT; UPDATE mid_seq SET next_val = next_val + 1 WHERE sequence_name = 'mensajes'; SELECT next_val INTO next_id FROM mid_seq WHERE sequence_name = 'mensajes'; RETURN next_id; END // DELIMITER ; -- 修改子类表的ID字段默认值 ALTER TABLE resenia MODIFY COLUMN id BIGINT DEFAULT get_next_mensajes_id(); ALTER TABLE post MODIFY COLUMN id BIGINT DEFAULT get_next_mensajes_id();之后即可直接省略
id字段插入:insert into resenia(fecha, cont, rating) values ("01/01/2000", "BUU", 2);
二、是否需要改用@MappedSuperClass?
不需要,除非你不需要将Mensajes作为独立实体类使用:
@MappedSuperClass仅用于定义子类的映射模板,父类本身不会生成数据库表,而当前的TABLE_PER_CLASS策略允许父类作为独立实体被查询操作,更符合项目需求。- 即使改用
@MappedSuperClass,手动SQL插入时的ID生成问题依然存在,还是需要维护序列或设置默认值,无法从根本上解决当前问题。
内容的提问来源于stack exchange,提问作者dantecito
相关产品推荐
相关产品推荐

