Java @MySQL上GeneratedValue多态抽象超类
在使用Hibernate和MySQL的Spring MVC应用程序中,我有一个抽象超类Java @MySQL上GeneratedValue多态抽象超类,java,mysql,database,hibernate,jpa,Java,Mysql,Database,Hibernate,Jpa,在使用Hibernate和MySQL的Spring MVC应用程序中,我有一个抽象超类BaseEntity,它管理模型中所有其他实体的ID值。id字段使用@GeneratedValue。每当我的代码试图保存扩展BaseEntity的任何子类时,我都会遇到问题。问题在于为@GeneratedValue选择GenerationType 在代码中,BaseEntity的子类试图保存到底层MySQL数据库的每个地方,我都会遇到以下错误: ERROR SqlExceptionHelper - Table
BaseEntity
,它管理模型中所有其他实体的ID值。id
字段使用@GeneratedValue
。每当我的代码试图保存扩展BaseEntity
的任何子类时,我都会遇到问题。问题在于为@GeneratedValue
选择GenerationType
在代码中,BaseEntity
的子类试图保存到底层MySQL数据库的每个地方,我都会遇到以下错误:
ERROR SqlExceptionHelper - Table 'docbd.hibernate_sequences' doesn't exist
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause'
我在某某和谷歌上读过很多关于这方面的帖子,但它们要么处理其他数据库(不是MySQL),要么不处理抽象超类。使用GenerationType.IDENTITY
无法解决此问题,因为我使用的是抽象超类来管理模型中所有实体的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
的原因是我需要有manytone
关系,允许多个子类型互换使用。以下面的AccessLog
类为例。它有一个actor\u实体
和一个target\u实体
。可以有多种类型的参与者实体和多种类型的目标实体,但它们都继承自BaseEntity
。这种继承使MySQL中的底层accesslogs
数据表只有一个actor\u entity\u id
字段和一个target\u entity\u id
字段,而不必每个字段都有几个字段。当我将@Entity
上面的BaseEntity
更改为@MappedSuperClass
时,会抛出另一个错误,指示AccessLog
无法找到BaseEntity
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的建议,我创建了一个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;
但现在我得到了以下错误:
ERROR SqlExceptionHelper - Table 'docbd.hibernate_sequences' doesn't exist
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'
我该如何解决这个问题?真是一团糟
AUTO_INCREMENT
是MySQL的隐藏序列。根本的问题是,MySQL
不能同时插入和返回PK,但是Hibernate在插入新实体时需要这样做
您遇到的问题:
GenerationType.AUTO
/GenerationType.IDENTITY
)保留PK的开放范围有多大(DB架构师的工作)。(一个数据库大约有20台服务器,所以在一个使用良好的表上,我们使用的PK距离为+100)。如果只有一台服务器可以访问数据库GenerationType。表应正确
max(*)+1自己计算下一个id,但是:
- 如果两个请求同时请求
max(*)+1
,结果相同,该怎么办?右:最后一次尝试插入将失败李>
因此,您需要在数据库中有一个存储最后一个表PK的表LAST\u id
。如果要添加一个,必须执行以下步骤:
开始读取乐观事务
从最后的\u id中选择MAX(地址\u id)
将最大值存储在java变量中,即:$OldID
$NewID=$OldID+1。(+100在悲观锁中)
更新上一个\u id设置地址\u id=$newID
其中地址\u id=$oldID
提交读乐观事务
如果提交成功,请将$newID
存储到要保存的HibernateBean中的setID()
最后让Hibernate调用insert
这是我唯一知道的方法
顺便说一句:如果数据库支持表之间的继承,例如PostgreSQL
或Oracle
,则Hibernate实体只能使用继承,因为您使用需要创建该表的表标识符生成器。如果您没有使用,那么您很可能会使用
MultipleHiLoPerTableGenerator可以对所有表标识符生成器使用一个表
我的建议是从集成测试中获取表ddl,以防使用hbmddl构建测试模式。如果使用flyway或liquibase进行测试,则可以添加一个来生成ddl模式
一旦有了模式,就需要使用确切的CREATETABLE命令并将其添加到MySQL数据库。您真的不应该使用实体继承来实现这一点。BaseEntity应该用@Mappedsuperclass
注释,而不是用@Entity
注释。是的,您正在寻找映射的超类。经验法则:当您需要对基类型执行查询时,使用继承,因为您不知道将返回哪些具体实例。为什么您有一个BaseEntity
,用于管理模型中所有其他实体的ID值?@JBNizet我刚刚在我的原始帖子末尾添加了一个编辑