使用 MySQL 和 Oracle 进行休眠自动密钥生成
Posted
技术标签:
【中文标题】使用 MySQL 和 Oracle 进行休眠自动密钥生成【英文标题】:Hibernate auto key generation with MySQL and Oracle 【发布时间】:2015-08-24 05:55:06 【问题描述】:我正在开发一个 Java 应用程序,它应该在具有相同数据库架构的两个不同数据库上执行 CRUD 操作(使用 Hibernate 4.3.8)。 有一个 MySQL(版本 5.1.73)和一个 Oracle(11g Express Edition Release 11.2.0.2.0 - 64 位)数据库。
带有 JPA 注释 的 Java 类是使用 Hibernate Code Generation 从数据库表中生成的。
问题是我们现在需要使用自动主键生成,而 mysql 使用 GenerationType.IDENTITY 而 Oracle 使用 GenerationType.SEQUENCE。此外,在极少数情况下,我们还需要自己手动设置主键。
注释类中的后续代码适用于两个数据库的自动密钥生成,但如果主键是自设置的,则会失败。
@GeneratedValue(strategy=GenerationType.AUTO, generator="sequence_generator")
@SequenceGenerator(name="sequence_generator", sequenceName="SEQUENCE1")
@Column(name = "id", unique = true, nullable = false)
public Integer getId()
return this.id;
如果没有 @GeneratedValue 和 @SequenceGenerator 注释,可以手动设置主键,但自动生成不起作用。
【问题讨论】:
试试这个 [manually-specify-the-value-of-a-primary-key-in-jpa-generatedvalue-column][1]。有很多解决方案。 [1]:***.com/questions/12002260/… 此线程中以数据库为中心的解决方案对我不起作用,因为不应更改数据库架构。除了我之外,还有其他人相信数据库架构不会改变。而且我不知道这是否可能以及如何编写自己的自定义 ID 生成器,它同时支持数据库 MySQL 和 Oracle(身份和序列)。在所有示例中,他们要么使用 IdentifierGenerator 要么使用 Sequencegenerator。 这个问题不清楚。您是说您希望 Hibernate 为某些类自动生成标识符并手动将标识符分配给其他类;或者您是说您希望 Hibernate 在大多数情况下自动为类的实例分配标识符,但在某些情况下您希望手动为同一类的某些实例分配标识符? 你应该使用触发器来拥有一个同构的跨数据库解决方案。 我的旧 Spring 4 项目过去在 MySQL 和 Oracle 11g 上都可以正常工作,只是带有@SequenceGenerator(name = "id_generator", sequenceName = "sq_id_user")
注释,但现在我在 Spring Boot 2 和 Hibernate 5 上抱怨一个非10.1.24-MariaDB 中的现有序列。
【参考方案1】:
即使您在没有任何 SEQUENCE 特定参数的情况下使用 GenerationType.AUTO
,您也无法保存分配的标识符。
如果您愿意做出一些妥协,有一些解决方法:
一种方法是切换到分配的标识符。您可以使用 UUID 标识符,它适用于 MySQL 和 Oracle,您也可以手动分配值。
另一种方法是使用自定义表格生成器。
首先定义一个 Identifiable 接口:
public interface Identifiable<T extends Serializable>
T getId();
然后你扩展表生成器:
public class AssignedTableGenerator extends TableGenerator
@Override
public Serializable generate(SessionImplementor session, Object obj)
if(obj instanceof Identifiable)
Identifiable identifiable = (Identifiable) obj;
Serializable id = identifiable.getId();
if(id != null)
return id;
return super.generate(session, obj);
此生成器能够将分配的标识符与合成生成的标识符混合:
doInTransaction(session ->
for (int i = 0; i < 5; i++)
session.persist(new AssignTableSequenceIdentifier());
AssignTableSequenceIdentifier tableSequenceIdentifier = new AssignTableSequenceIdentifier();
tableSequenceIdentifier.id = -1L;
session.merge(tableSequenceIdentifier);
session.flush();
);
生成以下语句:
select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
insert into sequence_table (sequence_name, next_val) values (default,1)
update sequence_table set next_val=2 where next_val=1 and sequence_name=default
select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
update sequence_table set next_val=3 where next_val=2 and sequence_name=default
select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
update sequence_table set next_val=4 where next_val=3 and sequence_name=default
select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
update sequence_table set next_val=5 where next_val=4 and sequence_name=default
select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
update sequence_table set next_val=6 where next_val=5 and sequence_name=default
select identityvs0_.id as id1_0_0_ from assigneTableIdentifier identityvs0_ where identityvs0_.id=-1
insert into assigneTableIdentifier (id) values (1, 2)
insert into assigneTableIdentifier (id) values (2, 4)
insert into assigneTableIdentifier (id) values (5, -1)
对于 Oracle,您可以组合 SEQUENCE 和分配的生成器。简而言之,考虑以下生成器:
public class AssignedSequenceStyleGenerator
extends SequenceStyleGenerator
@Override
public Serializable generate(SessionImplementor session,
Object obj)
if(obj instanceof Identifiable)
Identifiable identifiable = (Identifiable) obj;
Serializable id = identifiable.getId();
if(id != null)
return id;
return super.generate(session, obj);
您可以将其映射到您的实体,如下所示:
@Id
@GenericGenerator(
name = "assigned-sequence",
strategy = "com.vladmihalcea.book.hpjp.hibernate.identifier.AssignedSequenceStyleGenerator",
parameters = @org.hibernate.annotations.Parameter(
name = "sequence_name",
value = "post_sequence"
)
)
@GeneratedValue(
generator = "assigned-sequence",
strategy = GenerationType.SEQUENCE
)
private Long id;
所有的代码都可以在GitHub 上找到,就像一个魅力。
【讨论】:
这种方法是否适用于 mysql 数据库?似乎我低于异常原因:org.hibernate.MappingException:org.hibernate.dialect.MySQL5Dialect 不支持序列 嗨弗拉德,你是说使用 TableGenerator 而不是序列生成器?我已经有一个实体类,并将其用于带有序列生成器的 oracle db。但我的目的是对 mysql db 也使用相同的类。据我了解,mysql 数据库中没有等效的序列,所以我应该更改我的 ID 生成策略(即使用另一个表而不是序列)? 嗨弗拉德。感谢您的建议。我点击了这个链接:vladmihalcea.com/…,并且可以将相同的实体与 oracle db 的序列和 mfsql db 的自动增量一起使用。谢谢 如果我将代码(名称和导入的所有更改)插入应用程序,服务器拒绝使用它。恕我直言,您忘记了一些重要的细节。 专业提示 1:从 Stack Overflow 复制代码并将其随机粘贴到您的应用程序中并非明智之举。专业提示 2:当您可以 fork GitHub repository 找到 package 并运行代码以查看它的工作原理时,为什么要从 Stack Overflow 复制代码。【参考方案2】:试试这样的:
@Id
@Column( name = "ID" )
@TableGenerator(
name = "AppSeqStore",
table = "APP_SEQ_STORE",
pkColumnName = "APP_SEQ_NAME",
pkColumnValue = "LISTENER_PK",
valueColumnName = "APP_SEQ_VALUE",
initialValue = 1,
allocationSize = 1 )
@GeneratedValue( strategy = GenerationType.TABLE, generator = "AppSeqStore" )
以及数据库中的这张表:
CREATE TABLE APP_SEQ_STORE (
APP_SEQ_NAME VARCHAR(255) NOT NULL,
APP_SEQ_VALUE NUMBER(10) NOT NULL,
PRIMARY KEY(APP_SEQ_NAME)
)
INSERT INTO APP_SEQ_STORE VALUES ('LISTENER_PK', 0)
这一切都可以在 Oracle、MS Sql Server 和 MySql 中使用 JBoss 作为 App Server。
更多信息在这里: http://www.developerscrappad.com/408/java/java-ee/ejb3-jpa-3-ways-of-generating-primary-key-through-generatedvalue/
【讨论】:
以上是关于使用 MySQL 和 Oracle 进行休眠自动密钥生成的主要内容,如果未能解决你的问题,请参考以下文章
oracle12c 上的休眠空间 - sdo_geometry 对象在 em.merge 上变为空