枚举类型(enum)是一种方便的数据类型,允许我们指定一个常量列表,对象字段或数据库列可以设置为该列表中的值。
枚举的美妙之处在于我们可以通过提供人类可读格式的枚举常量来确保数据完整性。因此,Java和PostgreSQL原生支持这种数据类型并不令人惊讶。
但是,Java和PostgreSQL枚举之间的转换并不是开箱即用的。JDBC API不将枚举识别为一个独特的数据类型,这使得如何处理转换的决策留给了JDBC驱动程序。通常,驱动程序对此无能为力 —— 这是一个先有鸡还是先有蛋的问题。
有许多解决方案可以帮助您在Java和PostgreSQL枚举之间进行映射,但大多数解决方案都是ORM或JDBC特定的。这意味着适用于Spring Data的建议可能不适用于Quarkus,反之亦然。
public class PizzaOrder {private Integer id;private OrderStatus status;private Timestamp orderTime;getters and setters are omitted}
public enum OrderStatus {Ordered,Baking,Delivering,YummyInMyTummy}
CREATE TABLE pizza_order (id int PRIMARY KEY,status order_status NOT NULL,order_time timestamp NOT NULL DEFAULT now());
CREATE TYPE order_status AS ENUM('Ordered','Baking','Delivering','YummyInMyTummy');
insert into pizza_order (id, status, order_time)values (1, 'Ordered', now());
PreparedStatement statement = conn.prepareStatement("INSERT INTO pizza_order (id, status, order_time) VALUES(?,?,?)");statement.setInt(1, 1);statement.setString(2, OrderStatus.Ordered.toString());statement.setTimestamp(3, Timestamp.from(Instant.now()));statement.executeUpdate();
org.postgresql.util.PSQLException: ERROR: column "status" is of type order_status but expression is of type character varyingHint: You will need to rewrite or cast the expression.Position: 60at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2675)at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2365)at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:355)at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:490)at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:408)
PreparedStatement statement = conn.prepareStatement("INSERT INTO pizza_order (id, status, order_time) VALUES(?,?,?)");statement.setInt(1, 1);statement.setObject(2, OrderStatus.Ordered, java.sql.Types.OTHER);statement.setTimestamp(3, Timestamp.from(Instant.now()));statement.executeUpdate();
CREATE CAST (varchar AS order_status) WITH INOUT AS IMPLICIT;
PreparedStatement statement = conn.prepareStatement("INSERT INTO pizza_order (id, status, order_time) VALUES(?,?,?)");statement.setInt(1, 1);statement.setString(2, OrderStatus.Ordered.toString());statement.setTimestamp(3, Timestamp.from(Instant.now()));statement.executeUpdate();
PreparedStatement statement = conn.prepareStatement("SELECT id, status, order_time " +"FROM pizza_order WHERE id = ?");statement.setInt(1, 1);ResultSet resultSet = statement.executeQuery();resultSet.next();PizzaOrder order = new PizzaOrder();order.setId(resultSet.getInt(1));order.setStatus(OrderStatus.valueOf(resultSet.getString(2)));order.setOrderTime(resultSet.getTimestamp(3));
@Entitypublic class PizzaOrder {@Idprivate Integer id;@Enumerated(EnumType.STRING)private OrderStatus status;@CreationTimestampprivate Timestamp orderTime;// getters and setters are omitted}
// The repository interfacepublic interface PizzaOrderRepository extends JpaRepository<PizzaOrder, Integer> {}// The service class@Servicepublic class PizzaOrderService {@AutowiredPizzaOrderRepository repo;@Transactionalpublic void addNewOrder(Integer id) {PizzaOrder order = new PizzaOrder();order.setId(id);order.setStatus(OrderStatus.Ordered);repo.save(order);}...// Somewhere in the source codepizzaService.addNewOrder(1);}
PizzaOrder order = repo.findById(orderId).get();System.out.println("Order status: " + order.getStatus());
@Entity(name = "pizza_order")public class PizzaOrder {@Idprivate Integer id;@Enumerated(EnumType.STRING)private OrderStatus status;@CreationTimestamp@Column(name = "order_time")private Timestamp orderTime;// getters and setters are omitted}
@ApplicationScopedpublic class PizzaOrderService {@InjectEntityManager entityManager;@Transactionalpublic void addNewOrder(Integer id) {PizzaOrder order = new PizzaOrder();order.setId(id);order.setStatus(OrderStatus.Ordered);entityManager.persist(order);}...// Somewhere in the source codepizzaService.addNewOrder(1);
PizzaOrder order = entityManager.find(PizzaOrder.class, 1);System.out.println("Order status: " + order.getStatus());
<dependency><groupId>io.micronaut.data</groupId><artifactId>micronaut-data-jdbc</artifactId></dependency>// other dependencies
@MappedEntitypublic class PizzaOrder {@Idprivate Integer id;@Enumerated(EnumType.STRING)private OrderStatus status;private Timestamp orderTime;// getters and setters are omitted}
@JdbcRepository(dialect = Dialect.POSTGRES)public interface PizzaRepository extends CrudRepository<PizzaOrder, Integer> {}
PizzaOrder order = new PizzaOrder();order.setId(1);order.setStatus(OrderStatus.Ordered);order.setOrderTime(Timestamp.from(Instant.now()));repository.save(order);
PizzaOrder order = repository.findById(id).get();System.out.println("Order status: " + order.getStatus());
findById(ID id)方法从数据库检索记录并重新创建PizzaOrder实体,包括enum类型的PizzaOrder.status字段。
如今,你很可能会在应用程序逻辑中使用Java枚举,并因此需要将它们持久化到一个PostgreSQL数据库。你可以使用特定于ORM的解决方案进行Java和PostgreSQL枚举之间的转换,或者你可以利用基于PostgreSQL的转换操作符的通用方法。
基于转换操作符的方法适用于所有ORM,包括Spring Data、Hibernate、Quarkus和Micronaut,以及受欢迎的PostgreSQL兼容数据库,如Amazon Aurora、Google AlloyDB和YugabyteDB。
文章转载自云原生数据库,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




