@GeneratedValue在MySQL上的多态抽象超类
在使用Hibernate和MySQL的Spring MVC应用程序中,我有一个抽象超类BaseEntity
,它pipe理模型中所有其他实体的ID的值。 id
字段使用@GeneratedValue
。 每当我的代码尝试保存任何扩展BaseEntity
的子类时,都遇到问题。 问题出现在@GeneratedValue
的GenerationType
的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
无法findBaseEntity
。 BaseEntity
需要@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
一个新实体的时候需要这个。
遇到的问题:
- 如果Hibernate保存一个新的Entity,他会试着将id设置为新的EntityBean。 因此,hibernate必须读取数据库在hibernate状态下将新的元组保存到表之前使用的ID。
- 如果你有多个访问数据库的服务器,你应该让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。 如果您想添加一个,则必须执行以下步骤:
- 开始阅读乐观的交易。
- SELECT MAX(address_id)FROM LAST_IDS
- 将最大值存储在一个javavariables中:$ OldID。
- $ NewID = $ OldID + 1.(悲观locking+100)
- UPDATE LAST_IDS SET address_id =
$newID
WHERE address_id =$oldID
? - 提交阅读乐观的事务。
- 如果提交成功,
$newID
存储到您希望保存的HibernateBean中的setID()
。 - 最后让Hibernate调用插入。
这是我知道的唯一方法。
BTW:如果数据库支持像PostgreSQL
或Oracle
这样的表之间的inheritance,Hibernate-Entitys只能使用inheritance。