在Java和PostgreSQL枚举之间进行转换的通用方法

枚举类型(enum)是一种方便的数据类型,允许我们指定一个常量列表,对象字段或数据库列可以设置为该列表中的值。

枚举的美妙之处在于我们可以通过提供人类可读格式的枚举常量来确保数据完整性。因此,Java和PostgreSQL原生支持这种数据类型并不令人惊讶。

但是,Java和PostgreSQL枚举之间的转换并不是开箱即用的。JDBC API不将枚举识别为一个独特的数据类型,这使得如何处理转换的决策留给了JDBC驱动程序。通常,驱动程序对此无能为力 —— 这是一个先有鸡还是先有蛋的问题。

有许多解决方案可以帮助您在Java和PostgreSQL枚举之间进行映射,但大多数解决方案都是ORM或JDBC特定的。这意味着适用于Spring Data的建议可能不适用于Quarkus,反之亦然。

在本文中,我将回顾处理Java和PostgreSQL枚举转换的通用方法。此方法适用于普通的JDBC API和流行的ORM框架,如Spring Data、Hibernate、Quarkus和Micronaut。此外,它还被构建在PostgreSQL上的数据库支持,包括Amazon Aurora、Google AlloyDB和YugabyteDB。

创建Java实体对象和枚举 

假设我们有一个代表披萨订单的Java实体对象:

public class PizzaOrder {    private Integer id;    private OrderStatus status;    private Timestamp orderTime;    // getters and setters are omitted}

该对象的status字段是一个如下定义的枚举类型:

public enum OrderStatus {    Ordered,    Baking,    Delivering,    YummyInMyTummy}

当我们在线订购披萨时,应用程序将状态设置为Ordered。一旦厨师开始处理我们的订单,状态就会变为Baking。一旦披萨刚出炉,就会有人取走并送到我们的门口 - 状态随后更新为Delivering。最后,状态被设置为YummyInMyTummy,意味着我们享受了披萨(希望如此!)

创建数据库表和枚举 

为了在PostgreSQL中持久化披萨订单,让我们创建以下与我们的PizzaOrder实体类映射的表:

CREATE TABLE pizza_order (   id int PRIMARY KEY,   status order_status NOT NULL,   order_time timestamp NOT NULL DEFAULT now() );

该表带有一个名为order_status的自定义类型。该类型是如下定义的枚举:

CREATE TYPE order_status AS ENUM(  'Ordered',   'Baking',   'Delivering',   'YummyInMyTummy');

该类型定义与 Java 对应的常量(状态)类似。

解决转换问题

如果我们使用psql(或其他SQL工具)连接到PostgreSQL并执行以下INSERT语句,它将成功执行:

insert into pizza_order (id, status, order_time) values (1, 'Ordered', now());

该语句很好地接受文本表示形式的订单状态(枚举数据类型)Ordered。

看到这一点后,我们可能会想以这种格式将 Java 枚举值发送到 PostgreSQL String。如果我们直接使用JDBC APIPreparedStatement ,可以如下所示:


 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 varying
Hint: You will need to rewrite or cast the expression.
Position: 60
at 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)

尽管 PostgreSQL 在INSERT/UPDATE通过 psql 会话直接执行语句时接受枚举文本表示,但它不支持varchar(由 Java 传递)和我们的枚举类型之间的转换。

对于普通 JDBC API 修复此问题的一种方法是将 Java 枚举保留为以下类型的对象java.sql.Types.OTHER:


 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();

但是,正如我之前所说,这种方法并不通用。虽然它适用于普通的 JDBC API,但如果您使用Spring Data、Quarkus或其他ORM,则需要寻找其他解决方案。

数据库级别的类型转换

数据库提供了通用的解决方案。

PostgreSQL 支持强制转换运算符,可以自动执行两种数据类型之间的转换。

因此,在我们的例子中,我们需要做的就是创建以下运算符:

CREATE CAST (varchar AS order_status) WITH INOUT AS IMPLICIT;

创建的运算符将在varchar类型(由 JDBC 驱动程序传递)和我们的数据库级order_status枚举类型之间进行映射。该WITH INOUT AS IMPLICIT子句确保对于使用该order_status类型的所有语句,强制转换将透明且自动地发生。

使用纯 JDBC API 进行测试

在 PostgreSQL 中创建该强制转换运算符后,早期的 JDBC 代码片段会毫无问题地插入一个订单:


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();

我们所需要做的就是将 Java 枚举值作为 a 传递String,驱动程序会将其以表示形式发送到 PostgreSQL varchar,该表示形式会自动将该varchar值转换为order_status类型。

如果您从数据库读回订单,那么您可以轻松地从一个值重建 Java 级枚举String:


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));

使用 Spring Data 进行测试

接下来,让我们使用 Spring Data 验证基于强制转换运算符的方法。如今,您可能会直接使用 ORM 而不是 JDBC API。

首先,我们需要PizzaOrder用一些 JPA 和 Hibernate 注释来标记我们的实体类:

@Entitypublic class PizzaOrder {    @Id    private Integer id;    @Enumerated(EnumType.STRING)    private OrderStatus status;    @CreationTimestamp    private Timestamp orderTime;    // getters and setters are omitted}

指示@Enumerated(EnumType.STRING)JPA 实现(通常是 Hibernate)将枚举值作为 a 传递String给驱动程序。

其次,我们PizzaOrderRepository使用 Spring Data API 创建并保存实体对象:

// The repository interfacepublic interface PizzaOrderRepository extends JpaRepository<PizzaOrder, Integer> {}// The service class@Servicepublic class PizzaOrderService {    @Autowired    PizzaOrderRepository repo;    @Transactional    public void addNewOrder(Integer id) {      PizzaOrder order = new PizzaOrder();        order.setId(id);        order.setStatus(OrderStatus.Ordered);        repo.save(order);     }     ...   // Somewhere in the source code   pizzaService.addNewOrder(1);  }

当pizzaService.addNewOrder(1)我们的源代码中的某个位置调用该方法时,订单将被创建并成功保存到数据库中。Java 和 PostgreSQL 枚举之间的转换不会出现任何问题。

最后,如果我们需要从数据库读回订单,我们可以使用该JpaRepository.findById(ID id)方法,该方法根据其表示重新创建 Java 枚举String:

PizzaOrder order = repo.findById(orderId).get();System.out.println("Order status: " + order.getStatus());

使用 Quarkus 进行测试

Quarkus 怎么样,它可能是您的#1 ORM?只要 Quarkus 支持 Hibernate 作为 JPA 实现,与 Spring Data 就没有显着差异。

首先,我们PizzaOrder用 JPA 和 Hibernate 注解来注解我们的实体类:

@Entity(name = "pizza_order")public class PizzaOrder {    @Id    private Integer id;    @Enumerated(EnumType.STRING)    private OrderStatus status;    @CreationTimestamp    @Column(name = "order_time")    private Timestamp orderTime;    // getters and setters are omitted }

其次,我们介绍PizzaOrderService一下使用EntityManager实例进行数据库请求:

@ApplicationScopedpublic class PizzaOrderService {    @Inject    EntityManager entityManager;    @Transactional    public void addNewOrder(Integer id) {        PizzaOrder order = new PizzaOrder();        order.setId(id);        order.setStatus(OrderStatus.Ordered);        entityManager.persist(order);    }   ...   // Somewhere in the source code   pizzaService.addNewOrder(1);

当我们调用pizzaService.addNewOrder(1)应用程序逻辑中的某处时,Quarkus 将成功保留顺序,并且 PostgreSQL 将负责 Java 和 PostgreSQL 枚举转换。

最后,要从数据库读回订单,我们可以使用以下方法EntityManager将结果集中的数据映射到PizzaOrder实体类(包括枚举字段):

PizzaOrder order = entityManager.find(PizzaOrder.class, 1);System.out.println("Order status: " + order.getStatus());  

使用 Micronaut 进行测试

好吧,好吧, Micronaut怎么样?我喜欢这个框架,你可能也会喜欢它。

数据库端强制转换运算符对于 Micronaut 来说也是一个完美的解决方案。为了让事情有所不同,我们不会为 Micronaut 使用 Hibernate。相反,我们将通过使用该模块来依赖 Micronaut 自己的功能micronaut-data-jdbc:

<dependency>    <groupId>io.micronaut.data</groupId>    <artifactId>micronaut-data-jdbc</artifactId></dependency>// other dependencies

首先,我们对PizzaOrder实体进行注释:

@MappedEntitypublic class PizzaOrder {    @Id    private Integer id;    @Enumerated(EnumType.STRING)    private OrderStatus status;    private Timestamp orderTime;   // getters and setters are omitted}

接下来,定义PizzaRepository:

@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);

与Spring Data 和 Quarkus 一样,Micronaut将对象持久保存到 PostgreSQL,让数据库处理 Java 和 PostgreSQL 枚举类型之间的转换没有任何问题。

最后,每当我们需要从数据库读回订单时,我们可以使用以下 JPA API:

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。

作者:Denis Magda

更多内容请关注公号【云原生数据库

squids.cn,云数据库RDS,迁移工具DBMotion,云备份DBTwin等数据库生态工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/110708.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

详解—数据结构《树和二叉树》

目录 一.树概念及结构 1.1树的概念 1.2树的表示 二.二叉树的概念及结构 2.1概念 2.2二叉树的特点 2.3现实中的二叉树 2.4数据结构中的二叉树 2.5 特殊的二叉树 2.6二叉树的存储结构 2.6.1二叉树的性质 2.6.2 顺序结构 2.6.3链式存储 三. 二叉树的链式结构的遍历 …

【广州华锐视点】节省成本,提升效果!教你快速搭建一个元宇宙3D虚拟展厅!

在当今这个数字化的时代&#xff0c;拥有一个专业的网站或者小程序已经成为了企业展示形象、推广产品的重要手段。然而&#xff0c;对于许多小企业来说&#xff0c;高昂的开发费用和复杂的技术门槛往往成为了他们实现这一目标的最大阻碍。那么&#xff0c;有没有一种方式&#…

使用 puppeteer 库采集豆瓣音频简单代码示例

今天要给大家分享的采集代码&#xff0c;主要是使用 puppeteer 库进行编写的&#xff0c;用于采集豆瓣网相关音频。这段代码也是非常的简单实用&#xff0c;一起来看看吧。 // 引入 puppeteer 库 const puppeteer require(puppeteer);// 定义获取代理服务器的函数 function …

如果一定要在C++和JAVA中选择,是C++还是java?

如果一定要在C和JAVA中选择&#xff0c;是C还是java&#xff1f; 计算机专业的同学对这个问题有疑惑的&#xff0c;- 定要看一下这个回答! 上来直接给出最中肯的建议: 如果你是刚刚步入大学的大一时间非常充裕的同学&#xff0c;猪学长强烈建议先学C/C.因为C 非常 最近很多…

Android NDK开发详解之Application.mk探秘

Android NDK开发详解之Application.mk探秘 概览变量APP_ASFLAGSAPP_ASMFLAGSAPP_BUILD_SCRIPTAPP_CFLAGSAPP_CLANG_TIDYAPP_CLANG_TIDY_FLAGSAPP_CONLYFLAGSAPP_CPPFLAGSAPP_CXXFLAGSAPP_DEBUGAPP_LDFLAGSAPP_MANIFESTAPP_MODULESAPP_OPTIMAPP_PLATFORMAPP_PROJECT_PATHAPP_STL…

518抽奖软件,高质量缩放算法,照片显示更清晰

518抽奖软件简介 518抽奖软件&#xff0c;518我要发&#xff0c;超好用的年会抽奖软件&#xff0c;简约设计风格。 包含文字号码抽奖、照片抽奖两种模式&#xff0c;支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。([http://www.518cj.net/]http://www.518cj.net/) 高质量缩放…

最新JustMedia V2.7.3主题破解版去授权WordPress主题模板

JustMedia主题是一款针对有图片或者视频发布需求的网站量身定制开发的wordpress主题&#xff0c;适合各类图片展示类网站使用。 同时JustMedia主题首次加入了我们WPCOM团队独立自主开发的前端用户中心模块&#xff0c;相比用户中心插件可提供更好的体验效果。 新版用户中心为…

大数据平台发展及Hudi简要复习

第一代数据仓库——Vertica 最初&#xff0c;Uber使用MySQL作为他们的主要数据存储。然而&#xff0c;随着业务的扩展和数据量的增长&#xff0c;他们开始需要一个更强大的解决方案来进行大规模的数据分析和处理。 因此&#xff0c;Uber选择了Vertica作为他们的第一代数据仓库…

莫名其妙el-table不显示问题

完全复制element-ui中table代码&#xff0c;发现表格仍然不显示&#xff0c;看别人都说让降低版本&#xff0c;可我不想降低啊&#xff0c;不然其他组件有可能用不了&#xff0c;后来发现可以通过配置vite.config.js alias: {: path.resolve(__dirname, src),vue: vue/dist/vue…

关于息肉检测和识别项目的总结

前言 整体的思路&#xff1a;首先息肉数据集分为三类&#xff1a; 1.正常细胞 2. 增生性息肉 3. 肿瘤要想完成这个任务&#xff0c;首先重中之重是分割任务&#xff0c;分割结果的好坏&#xff0c; 当分割结果达到一定的准确度后&#xff0c;开始对分割后的结果进行下游分类…

Node.js的基本概念node -v 和npm -v 这两个命令的作用

Node.js 是一个开源且跨平台的 JavaScript 运行时环境&#xff0c;它可以让你在服务器端运行 JavaScript 代码。Node.js 使用了 Chrome 的 V8 JavaScript 引擎来执行代码&#xff0c;非常高效。 在 Node.js 出现之前&#xff0c;JavaScript 通常只在浏览器中运行&#xff0c;用…

谈思生物医疗直播 | 霍德生物研发中心负责人王安欣博士“iPSC衍生神经细胞产品全悬浮自动化工艺及特殊质控方法开发”

iPSC通过人体来源的终端体细胞重编程而来&#xff0c;其衍生细胞产品的生产与质控面临着诸多挑战&#xff0c;但也解决了许多自体细胞治疗的不稳定性和高成本等产业化难点。例如自体细胞不仅供体之间的差异对产品质量可能造成影响&#xff0c;即使同一个供体&#xff0c;体细胞…

SSM培训报名管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 培训报名管理系统是一套完善的信息系统&#xff0c;结合SSM框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主 要采用B/S模式开…

http1,https,http2,http3总结

1.HTTP 当我们浏览网页时&#xff0c;地址栏中使用最多的多是https://开头的url&#xff0c;它与我们所学的http协议有什么区别&#xff1f; http协议又叫超文本传输协议&#xff0c;它是应用层中使用最多的协议&#xff0c; http与我们常说的socket有什么区别吗&#xff1f; …

TSINGSEE青犀省级高速公路视频上云联网方案:全面实现联网化、共享化、智能化

一、需求背景 随着高速铁路的建设及铁路管理的精细化&#xff0c;原有的模拟安防视频监控系统已经不能满足视频监控需求&#xff0c;越来越多站点在建设时已开始规划高清安防视频监控系统。高速公路视频监控资源非常丰富&#xff0c;需要对其进行综合管理与利用。通过构建监控…

Java版 招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

【计算机网络】分层模型和应用协议

网络分层模型和应用协议 1. 分层模型 1.1 五层网络模型 网络要解决的问题是&#xff1a;两个程序之间如何交换数据。 四层&#xff1f;五层&#xff1f;七层&#xff1f; 2. 应用层协议 2.1 URL URL&#xff08;uniform resource locator&#xff0c;统一资源定位符&#…

基于深度学习的人脸表情识别 计算机竞赛

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸表情识别 该项目较…

网络工程综合试题(二)

1. SR技术有哪些缺点&#xff1f; SR&#xff08;Segment Routing&#xff09;技术是一种新兴的网络编程技术&#xff0c;它具有很多优点&#xff0c;但也存在一些缺点&#xff0c;包括&#xff1a; 部署复杂性&#xff1a;SR技术需要对网络进行改造和升级&#xff0c;包括更新…

拉扎维模拟CMOS集成电路设计西交张鸿老师课程P6~9视频学习记录

目录 p6 p7 CG放大器 输入阻抗的计算方法&#xff1b; 输出阻抗的计算​编辑​编辑 p8p9 为什么需要差动放大器 噪声 什么是差分信号 基础差动放大器 利用叠加法求解增益&#xff1b; 共模响应 CMRR 带其他类似负载的差动对 ------------------------------------…