@GeneratedValue在MySQL上的多态抽象超类

在使用Hibernate和MySQL的Spring MVC应用程序中,我有一个抽象超类BaseEntity ,它pipe理模型中所有其他实体的ID的值。 id字段使用@GeneratedValue 。 每当我的代码尝试保存任何扩展BaseEntity的子类时,都遇到问题。 问题出现在@GeneratedValueGenerationType的select上。

在我的代码中BaseEntity一个子类试图保存到底层MySQL数据库的每一个地方,我得到以下错误:

 ERROR SqlExceptionHelper - Table 'docbd.hibernate_sequences' doesn't exist 

我已经阅读了许多关于这个在谷歌上的post,但是他们要么处理其他数据库(不是MySQL),要么处理抽象的超类。 我不能通过使用GenerationType.IDENTITY来解决问题,因为我正在使用抽象超类来pipe理模型中所有实体的id字段。 同样,我不能使用GenerationType.SEQUENCE因为MySQL不支持序列。

那么我该如何解决这个问题呢?

这里是BaseEntity.java的代码:

 @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public abstract class BaseEntity { @Id @GeneratedValue(strategy = GenerationType.TABLE) protected Integer id; public void setId(Integer id) {this.id = id;} public Integer getId() {return id;} public boolean isNew() {return (this.id == null);} } 

以下是一个扩展BaseEntity的实体的代码示例:

 @Entity @Table(name = "ccd") public class CCD extends BaseEntity{ //other stuff } 

这是DDL:

 CREATE TABLE IF NOT EXISTS ccd( id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, #other stuff )engine=InnoDB;SHOW WARNINGS; 

这里是DAO中的JPQL代码:

  @Override @Transactional public void saveCCD(CCD ccd) { if (ccd.getId() == null) { System.out.println("[[[[[[[[[[[[ about to persist CCD ]]]]]]]]]]]]]]]]]]]]"); this.em.persist(ccd); this.em.flush(); } else { System.out.println("]]]]]]]]]]]]]]]]]] about to merge CCD [[[[[[[[[[[[[[[[[[[[["); this.em.merge(ccd); this.em.flush(); } } 

编辑:

我不能在这种情况下使用@MappedSuperClass的原因是我需要有ManyToOne关系,允许多个子types交替使用。 以下面的AccessLog类为例。 它有一个actor_entity和一个target_entity 。 可以有许多types的actor实体和许多types的目标实体,但是它们都inheritance自BaseEntity 。 这种inheritance使得MySQL中的底层访问accesslogs数据表只有一个actor_entity_id字段和一个target_entity_id字段,而不必为每个字段分配多个字段。 当我将BaseEntity上方的@Entity更改为@MappedSuperClass ,会引发另一个错误,指出AccessLog无法findBaseEntityBaseEntity需要@Entity注解才能使AccessLog具有多态属性。

 @Entity @Table(name = "accesslogs") public class AccessLog extends BaseEntity{ @ManyToOne @JoinColumn(name = "actorentity_id") private BaseEntity actor_entity; @ManyToOne @JoinColumn(name = "targetentity_id") private BaseEntity target_entity; @Column(name="action_code") private String action; //getters, setters, & other stuff } 

第二次编辑:

根据JBNizet的build议,我创build了一个hibernate_sequences表,如下所示:

 CREATE TABLE IF NOT EXISTS hibernate_sequences( sequence_next_hi_value int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY )engine=InnoDB;SHOW WARNINGS; 

但是现在我收到以下错误:

 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause' 

这里是导致错误的hibernate sql,随后是堆栈跟踪的下两行:

 Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'BaseEntity' for update ERROR MultipleHiLoPerTableGenerator - HHH000351: Could not read or init a hi value com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause' 

我如何解决这个问题?

因为您使用TABLE标识符生成器,​​所以您需要创build该表。 如果您不使用增强型标识符生成器 ,则可能要使用MultipleHiLoPerTableGenerator 。

MultipleHiLoPerTableGenerator可以为所有表标识符生成器使用一个表。

我的build议是从集成testing中获取表ddl,以防您使用hbmddl构buildtesting模式。 如果您使用flyway或liquibase进行testing,则可以添加一个maven插件来生成ddl模式。

一旦你有了模式,你需要采取确切的创build表命令,并将其添加到您的MySQL数据库。

真是一团糟…… AUTO_INCREMENT是MySQL的隐藏序列。 根本的问题是MySQL不能同时插入和返回PK,但是Hibernate在INSERT一个新实体的时候需要这个。

遇到的问题:

  1. 如果Hibernate保存一个新的Entity,他会试着将id设置为新的EntityBean。 因此,hibernate必须读取数据库在hibernate状态下将新的元组保存到表之前使用的ID。
  2. 如果你有多个访问数据库的服务器,你应该让hibernate的session-factory决定使用内置的序列(AUTO-INCREMENT)还是让hibernate决定( GenerationType.AUTO / GenerationType.IDENTITY )保留的开放范围有多大PK的是(DBbuild筑师的工作)。 (我们有大约20个服务器到一个数据库,所以在一个使用良好的表上我们使用+100的PK距离)。 如果只有一个服务器可以访问数据库GenerationType.TABLE应该是正确的。

Hibernate必须使用max(*)+1自己计算下一个id,但是:

  • 如果两个请求同时要求max(*)+1 /相同的结果? 正确:最后一次尝试insert将失败。

所以你需要在数据库中有一个表LAST_IDS存储最后一个Table-PK。 如果您想添加一个,则必须执行以下步骤:

  1. 开始阅读乐观的交易。
  2. SELECT MAX(address_id)FROM LAST_IDS
  3. 将最大值存储在一个javavariables中:$ OldID。
  4. $ NewID = $ OldID + 1.(悲观locking+100)
  5. UPDATE LAST_IDS SET address_id = $newID WHERE address_id = $oldID
  6. 提交阅读乐观的事务。
  7. 如果提交成功, $newID存储到您希望保存的HibernateBean中的setID()
  8. 最后让Hibernate调用插入。

这是我知道的唯一方法。

BTW:如果数据库支持像PostgreSQLOracle这样的表之间的inheritance,Hibernate-Entitys只能使用inheritance。