根据数据库产品在 Jakarta JPA 中使用不同的 ID 生成器
Use different ID generator in Jakarta JPA depending on DB product
我将应用程序从 Java EE 7/Glassfish 4 迁移到 Jakarta EE 9.1/Glassfish 6.1。我们的产品支持 Oracle 和 MSSQL 数据库。
对于 Oracle,我们使用序列来生成 ID。对于 MSSQL,我们使用标识列。
这是我们对 Glassfish 4 的注解。它对 Oracle 和 MSSQL 都有效。 Glassfish 4 忽略了 MSSQL 的序列并自动选择标识列。
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@SequenceGenerator(name = "METADATA_SEQUENCE_GENERATOR", sequenceName = "TBL_METADATA_ID_SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.AUTO, generator = "METADATA_SEQUENCE_GENERATOR")
@Column(name="id")
private long id;
}
这就是实体的持久化方式。
public ENTITY saveAndFlush(ENTITY entity) {
entityManager().persist(entity);
entityManager().flush();
return entity;
}
对于 Glassfish 6.1,我在程序刷新 MSSQL 上的实体时看到此错误。
Caused by: jakarta.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services -
3.0.2.v202107160933): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Ein expliziter Wert für die Identitätsspalte kann nicht in der tbl_logs-Tabelle eingefügt werden, wenn IDENTITY_INSERT auf OFF festgelegt ist. Error Code: 544 Call: INSERT INTO tbl_logs (id, str_message, dte_time, int_d1, id_line, id_user) VALUES (?, ?, ?, ?, ?, ?) bind => [6 parameters bound] Query: InsertObjectQuery(de.pharmacontrol.pilot.entities.LogEntry@5950c7bd) at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:980) at com.sun.enterprise.container.common.impl.EntityManagerWrapper.flush(EntityManagerWrapper.java:412)
身份插入已关闭,ID 应来自数据库,但 JPA 构造了一个尝试插入 ID 的查询。
我将注释更改为以下内容。
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private long id;
}
它在 MSSQL 上工作正常但在 Oracle 上失败。然后以下适用于 Oracle 但不适用于 MSSQL。
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@SequenceGenerator(name = "METADATA_SEQUENCE_GENERATOR", sequenceName = "TBL_METADATA_ID_SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "METADATA_SEQUENCE_GENERATOR")
@Column(name="id")
private long id;
}
如何注释我的 ID,以便它们在 MSSQL 上使用身份列并在 Oracle 上使用序列?
对于有特殊要求的数据库,您可以使用 orm.xml 定制程序文件。考虑在您的代码中使用 @GeneratorValue(strategy=IDENTITY)
,并为您的 oracle 配置文件添加一个不同的 orm.xml
,您可以在其中定义适当的序列生成器和策略。
orm.xml
for oracle 配置文件也可能有助于克服一些关于 table 和列名的特殊情况,以及许多其他注意事项。
使用 EclipseLink 3.0.2 我最终得到了注释
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "TBL_METADATA_ID_SEQ")
@Column(name="id")
private long id;
}
GeneratedValue.generator
属性 中没有引用 @SequenceGenerator
,GeneratedValue.generator
引用了 Oracle 数据库中的序列名称。这适用于具有标识列和 Oracle 序列的 Microsoft SQL 服务器。
我将应用程序从 Java EE 7/Glassfish 4 迁移到 Jakarta EE 9.1/Glassfish 6.1。我们的产品支持 Oracle 和 MSSQL 数据库。
对于 Oracle,我们使用序列来生成 ID。对于 MSSQL,我们使用标识列。
这是我们对 Glassfish 4 的注解。它对 Oracle 和 MSSQL 都有效。 Glassfish 4 忽略了 MSSQL 的序列并自动选择标识列。
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@SequenceGenerator(name = "METADATA_SEQUENCE_GENERATOR", sequenceName = "TBL_METADATA_ID_SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.AUTO, generator = "METADATA_SEQUENCE_GENERATOR")
@Column(name="id")
private long id;
}
这就是实体的持久化方式。
public ENTITY saveAndFlush(ENTITY entity) {
entityManager().persist(entity);
entityManager().flush();
return entity;
}
对于 Glassfish 6.1,我在程序刷新 MSSQL 上的实体时看到此错误。
Caused by: jakarta.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services -
3.0.2.v202107160933): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Ein expliziter Wert für die Identitätsspalte kann nicht in der tbl_logs-Tabelle eingefügt werden, wenn IDENTITY_INSERT auf OFF festgelegt ist. Error Code: 544 Call: INSERT INTO tbl_logs (id, str_message, dte_time, int_d1, id_line, id_user) VALUES (?, ?, ?, ?, ?, ?) bind => [6 parameters bound] Query: InsertObjectQuery(de.pharmacontrol.pilot.entities.LogEntry@5950c7bd) at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:980) at com.sun.enterprise.container.common.impl.EntityManagerWrapper.flush(EntityManagerWrapper.java:412)
身份插入已关闭,ID 应来自数据库,但 JPA 构造了一个尝试插入 ID 的查询。
我将注释更改为以下内容。
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private long id;
}
它在 MSSQL 上工作正常但在 Oracle 上失败。然后以下适用于 Oracle 但不适用于 MSSQL。
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@SequenceGenerator(name = "METADATA_SEQUENCE_GENERATOR", sequenceName = "TBL_METADATA_ID_SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "METADATA_SEQUENCE_GENERATOR")
@Column(name="id")
private long id;
}
如何注释我的 ID,以便它们在 MSSQL 上使用身份列并在 Oracle 上使用序列?
对于有特殊要求的数据库,您可以使用 orm.xml 定制程序文件。考虑在您的代码中使用 @GeneratorValue(strategy=IDENTITY)
,并为您的 oracle 配置文件添加一个不同的 orm.xml
,您可以在其中定义适当的序列生成器和策略。
orm.xml
for oracle 配置文件也可能有助于克服一些关于 table 和列名的特殊情况,以及许多其他注意事项。
使用 EclipseLink 3.0.2 我最终得到了注释
@Entity
@Table(name="tbl_metadata")
public class MetaData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "TBL_METADATA_ID_SEQ")
@Column(name="id")
private long id;
}
GeneratedValue.generator
属性 中没有引用 @SequenceGenerator
,GeneratedValue.generator
引用了 Oracle 数据库中的序列名称。这适用于具有标识列和 Oracle 序列的 Microsoft SQL 服务器。