bJava枚举/b
Java枚举是一种特殊类型的类,它表示固定数量的常量。枚举用于定义一组具有基础类型(如字符串或整数)的命名值。当我们需要在应用程序中定义一组具有特定含义的命名值时,枚举非常有用。
以下是 Java枚举的一个示例:
codepublic enum OrderStatus {
PENDING, IN_PROGRESS, COMPLETED, CANCELED
}/code
在这个例子中,OrderStatus 枚举定义了四个常量。这些常量可以在我们的应用程序中用来表示订单的状态。
b使用@Enumerated注解/b
当通过 JPA 使用 Java枚举时,我们需要用@Enumerated注释枚举字段来指定枚举值应如何存储在数据库中。
首先,我们定义一个名为CustomerOrder 的实体类,并用@Entity注释该类以将其标记为 JPA 持久性:
code@Entity
public class CustomerOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated()
private OrderStatus status;
// ... other fields and methods
}/code
默认情况下,JPA 将枚举值存储为整数,表示枚举常量的序数位置。例如,如果我们有一个枚举OrderStatus,其值为PENDING、IN_PROGRESS、COMPLETED和CANCELED,则默认行为会分别将它们存储为整数 0、1、2 和 3。生成的数据库表将具有smallint 类型的 状态列,其值为 0 到 3:
codecreate table customer_order (
id bigserial not null,
status smallint check (status between 0 and 3),
primary key (id)
);/code
但是,如果我们更改 Java 代码中枚举常量的顺序,这种默认行为可能会导致问题。例如,如果我们交换IN_PROGRESS和PENDING的顺序,数据库值仍将是 0、1、2 和 3,但它们将不再与更新后的枚举顺序匹配。这可能会导致我们的应用程序出现不一致和错误。
为了避免这个问题,我们可以使用EnumType.STRING将枚举值作为字符串存储在数据库中。这种方法确保枚举值以人类可读的格式存储,并且我们可以更改枚举常量的顺序而不影响数据库值:
code@Enumerated(EnumType.STRING)
private OrderStatus status;/code
这指示 JPA在数据库中存储OrderStatus枚举值的字符串表示形式(例如“ PENDING ”),而不是序数位置(整数索引)。生成的数据库表将具有varchar类型的状态列,该列可以保存枚举中定义的特定字符串值:
codecreate table customer_order (
id bigint not null,
status varchar(16) check (status in ('PENDING','IN_PROGRESS', 'COMPLETED', 'CANCELLED')),
primary key (id)
);/code
b 将 Java 枚举映射到 PostgreSQL 枚举的挑战/b
由于Java枚举和 PostgreSQL 枚举在处理和功能上的差异,将 Java 枚举映射到 PostgreSQL 枚举可能具有挑战性。即使使用EnumType.STRING,JPA 仍然不知道如何将 Java枚举映射到 PostgreSQL 枚举。为了演示这个问题,让我们创建一个 PostgreSQL 枚举类型:
codeCREATE TYPE order_status AS ENUM ('PENDING', 'IN_PROGRESS', 'COMPLETED', 'CANCELED');/code
接下来,我们创建一个使用 PostgreSQL 枚举类型的表:
codeCREATE TABLE customer_order (
id BIGINT NOT NULL,
status order_status,
PRIMARY KEY (id)
);/code
我们已更新status列以使用 PostgreSQL 枚举类型order_status。现在,让我们尝试将一些数据插入表中:
codeCustomerOrder order = new CustomerOrder();
order.setStatus(OrderStatus.PENDING);
session.save(order);/code
然而,当我们尝试插入数据时,我们会收到一个异常:
codeorg.hibernate.exception.SQLGrammarException: could not execute statement
ERROR: column "status" is of type order_status but expression is of type character varying/code
发生 SQLGrammarException是因为 JPA 不知道如何将 Java枚举 OrderStatus映射到 PostgreSQL 枚举order_status。
b使用@Type注释/b
在 Hibernate 5 中,我们可以利用 Hypersistence Utils 库来解决这一挑战。该库提供了其他类型,包括对 PostgreSQL 枚举的支持。
首先,我们需要将Hypersistence Utils依赖项添加到我们的pom.xml中:
code
Hypersistence Utils 库包含一个PostgreSQLEnumType类,用于处理 Java 枚举和 PostgreSQL 枚举类型之间的转换。我们将使用此类作为我们的自定义类型处理程序。
接下来,我们可以使用@Type注释注释枚举字段,并在实体类中定义自定义类型处理程序:
code@Entity
@TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
public class CustomerOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "order_status")
@Type(type = "pgsql_enum")
private OrderStatus status;
// ... other fields and methods
}/code
通过遵循这些步骤,我们可以有效地将 PostgreSQL 枚举类型映射到 Hibernate 5 中的 Java 枚举。
b使用PostgreSQLEnumJdbcType/b
在 Hibernate 6 中,我们可以在枚举字段上直接使用@JdbcType注释来指定自定义 JDBC 类型处理程序。此注释允许我们定义自定义JdbcType类来处理 Java枚举类型与相应 JDBC 类型之间的映射。
以下是我们如何在CustomerOrder实体中使用@JdbcType:
code@Enumerated(EnumType.STRING)
@JdbcType(type = PostgreSQLEnumJdbcType.class)
private OrderStatus status;/code
我们指定PostgreSQLEnumJdbcType类作为自定义类型处理程序。此类是 Hibernate 6 的一部分,负责处理 Java枚举字符串和 PostgreSQL 枚举类型之间的转换。
当我们持久化OrderStatus对象(例如order.setStatus(OrderStatus.PENDING))时,Hibernate 首先将枚举值转换为其字符串表示形式(“ PENDING ”)。然后,PostgreSQLEnumJdbcType类获取字符串值(“ PENDING ”)并将其转换为适合 PostgreSQL 枚举类型的格式。然后将转换后的值传递到数据库以存储在order_status列中。
b使用本机查询插入枚举值/b
使用原生查询将数据插入 PostgreSQL 表时,插入数据的类型必须与列类型匹配。对于枚举类型列,PostgreSQL 要求值是枚举类型,而不仅仅是纯字符串。
让我们尝试使用不带强制转换的本机查询:
codeString sql = "INSERT INTO customer_order (status) VALUES (:status)";
Query query = session.createNativeQuery(sql);
query.setParameter("status", OrderStatus.COMPLETED); // Use the string representation of the enum/code
我们得到以下错误:
codeorg.postgresql.util.PSQLException: ERROR: column "status" is of type order_status but expression is of type character varying/code
出现此错误的原因是 PostgreSQL 期望该值为order_status类型,但收到的却是字符变化。为了解决此问题,我们在本机查询中将值显式转换为枚举类型:
codeString sql = "INSERT INTO customer_order (status) VALUES (CAST(:status AS order_status))";
Query query = session.createNativeQuery(sql);
query.setParameter("status", OrderStatus.COMPLETED);/code
SQL 语句中的CAST (:status AS order_status)部分能确保:字符串值“ COMPLETED ”被明确转换为PostgreSQL 中的order_status枚举类型。