使用JPA将对象列表作为单列JSON存储的教程

使用JPA将对象列表作为单列JSON存储的教程

本教程详细介绍了如何在spring boot和jpa应用中,将一个对象列表(json数组)高效地存储到数据库的单个列中,而非分散到多个列或单独的表中。核心解决方案是利用jpa的`attributeconverter`机制,结合jackson库实现对象列表与json字符串之间的双向转换,从而灵活地处理复杂数据结构,满足特定存储需求。

在现代应用开发中,经常需要将复杂的嵌套数据结构(例如对象列表)存储到关系型数据库中。虽然JPA提供了@Embeddable和@ElementCollection等注解来处理嵌入式对象或集合,但它们通常会将数据展开成多个列或存储到单独的表中。当需求是将整个对象列表作为一个JSON字符串存储在主表的单个列中时,这些标准JPA注解就不再适用。此时,JPA的AttributeConverter机制便成为理想的解决方案。

理解问题:为什么标准JPA注解不适用?

原始问题中尝试使用@Embeddable注解来处理Sensors[]数组,并期望它能以JSON形式存储在单个列中。然而,@Embeddable注解通常用于将一个类的属性嵌入到另一个实体类中,这些属性会直接映射到主表的相应列。如果@Embeddable类是一个集合类型(如数组或列表),JPA会尝试将其映射到多个独立的列(对于数组的每个元素),或者在结合@ElementCollection时映射到一个单独的关联表。这与将整个集合序列化为单个JSON字符串的需求相悖,从而导致类型转换错误。

解决方案:使用JPA AttributeConverter

AttributeConverter是JPA 2.1引入的一个强大特性,它允许开发者自定义实体属性类型与数据库列类型之间的转换逻辑。通过实现这个接口,我们可以将复杂的Java对象(如List)在存储到数据库时序列化为简单的字符串(JSON),并在从数据库读取时反序列化回Java对象。

1. 定义 Sensors 类

首先,我们需要确保Sensors类是一个普通的Java类,不再需要@Embeddable注解,因为它的序列化和反序列化将由我们自定义的转换器处理,而不是JPA的嵌入式对象机制。

public class Sensors {    private String amplitudos;    private Double displacement;    private String frequencies;    private Integer sensorId;    public Sensors() {    }    public Sensors(String amplitudos, Double displacement, String frequencies, Integer sensorId) {        this.amplitudos = amplitudos;        this.displacement = displacement;        this.frequencies = frequencies;        this.sensorId = sensorId;    }    // Getters and Setters    public String getAmplitudos() {        return amplitudos;    }    public void setAmplitudos(String amplitudos) {        this.amplitudos = amplitudos;    }    public Double getDisplacement() {        return displacement;    }    public void setDisplacement(Double displacement) {        return displacement;    }    public String getFrequencies() {        return frequencies;    }    public void setFrequencies(String frequencies) {        this.frequencies = frequencies;    }    public Integer getSensorId() {        return sensorId;    }    public void setSensorId(Integer sensorId) {        this.sensorId = sensorId;    }}

2. 实现自定义 AttributeConverter

接下来,创建一个实现AttributeConverter接口的类。在这个例子中,ENTITY_ATTRIBUTE_TYPE是List,DATABASE_COLUMN_TYPE是String。我们将使用Jackson库来处理JSON的序列化和反序列化。

import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.core.type.TypeReference;import com.fasterxml.jackson.databind.ObjectMapper;import jakarta.persistence.AttributeConverter;import jakarta.persistence.Converter;import java.io.IOException;import java.util.List;import java.util.Collections; // 导入 Collections@Converterpublic class SensorsConverter implements AttributeConverter<List, String> {    private final ObjectMapper objectMapper = new ObjectMapper();    /**     * 将实体属性(List)转换为数据库列类型(String JSON)     * @param sensors 要转换的Sensors列表     * @return 转换后的JSON字符串     */    @Override    public String convertToDatabaseColumn(List sensors) {        if (sensors == null) {            return null;        }        try {            return objectMapper.writeValueAsString(sensors);        } catch (JsonProcessingException e) {            // 生产环境中应记录日志并抛出更具体的运行时异常            throw new IllegalArgumentException("Error converting List to JSON string", e);        }    }    /**     * 将数据库列类型(String JSON)转换为实体属性(List)     * @param sensorsJSON 数据库中的JSON字符串     * @return 转换后的Sensors列表     */    @Override    public List convertToEntityAttribute(String sensorsJSON) {        if (sensorsJSON == null || sensorsJSON.trim().isEmpty()) {            return Collections.emptyList(); // 返回空列表而不是null        }        try {            return objectMapper.readValue(sensorsJSON, new TypeReference<List>() {});        } catch (IOException e) {            // 生产环境中应记录日志并抛出更具体的运行时异常            throw new IllegalArgumentException("Error converting JSON string to List", e);        }    }}

注意

@Converter注解标记这个类是一个JPA转换器。convertToDatabaseColumn方法负责将Java对象(List)序列化为JSON字符串。convertToEntityAttribute方法负责将JSON字符串反序列化回Java对象(List)。在实际应用中,应添加更健壮的异常处理和日志记录。对于空值处理,convertToEntityAttribute返回Collections.emptyList()通常比返回null更安全,可以避免后续的NullPointerException。

3. 在实体类中使用转换器

最后,在Sensor实体类中,将sensors字段的类型从Sensors[]改为List,并使用@Convert注解指定我们刚刚创建的SensorsConverter。

网易人工智能 网易人工智能

网易数帆多媒体智能生产力平台

网易人工智能 206 查看详情 网易人工智能

import jakarta.persistence.*;import java.io.Serializable;import java.sql.Timestamp;import java.util.List; // 使用 List 代替数组@Entity@Table(name = "SENSOR")public class Sensor implements Serializable {    @Id    @GeneratedValue(strategy = GenerationType.AUTO)    private long id;    @Column(name = "TIMERECEIVED")    private Timestamp timereceived;    // 使用 @Convert 注解指定自定义转换器    // 数据库列类型为 VARCHAR2/CLOB,Java实体属性类型为 List    @Column(name = "SENSORS", columnDefinition = "CLOB") // 或 VARCHAR2(4000) 根据实际JSON大小调整    @Convert(converter = SensorsConverter.class)    private List sensors; // 类型改为 List    @Column(name = "LOC")    private String location;    public Sensor() {    }    public Sensor(Timestamp timereceived, List sensors, String location) {        this.timereceived = timereceived;        this.sensors = sensors;        this.location = location;    }    // Getters and Setters    public long getId() {        return id;    }    public void setId(long id) {        this.id = id;    }    public Timestamp getTimereceived() {        return timereceived;    }    public void setTimereceived(Timestamp timereceived) {        this.timereceived = timereceived;    }    public List getSensors() { // 返回类型也改为 List        return sensors;    }    public void setSensors(List sensors) { // 参数类型也改为 List        this.sensors = sensors;    }    public String getLocation() {        return location;    }    public void setLocation(String location) {        this.location = location;    }}

注意

@Column(name = “SENSORS”, columnDefinition = “CLOB”):这里我们显式地指定了数据库列的类型为CLOB(字符大对象),以确保能够存储较长的JSON字符串。如果JSON字符串较短,也可以使用VARCHAR2(N),其中N是足够大的长度。一些现代数据库(如PostgreSQL、Oracle 12c+、MySQL 5.7+)也支持原生JSON数据类型,如果使用这些数据库,可以考虑将columnDefinition设置为相应的JSON类型,例如JSON。

4. 控制器和JSON体

控制器和服务层代码基本无需修改,因为转换器在JPA层自动处理了List与String之间的映射。

import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class SensorController {    private final SensorRepository sensorRepository; // 假设有一个 SensorRepository    public SensorController(SensorRepository sensorRepository) {        this.sensorRepository = sensorRepository;    }    @PostMapping("/sendData")    public ResponseEntity sendData(@RequestBody Sensor sensor) {        Sensor newSensor = sensorRepository.save(sensor);        System.out.println("Saved Sensor: " + newSensor.getId());        // 可以打印出 newSensor.getSensors() 来验证是否正确反序列化        System.out.println("Sensors data: " + newSensor.getSensors());        return ResponseEntity.ok("Sensor received and saved successfully");    }}

客户端发送的JSON请求体保持不变:

{    "timereceived": "2022-11-29T12:04:42.166",    "sensors": [        {            "amplitudos": "a1#a2#a3#a4",            "displacement": 0.002,            "frequencies": "f1#f2#f3#f4",            "sensorid": 1        },        {            "amplitudos": "a1#a2#a3#a4",            "displacement": 0.002,            "frequencies": "f1#f2#f3#f4",            "sensorid": 2        }    ],    "location": "lokasi"}

当这个JSON体到达Spring Boot控制器时,Jackson会自动将其中的sensors数组映射到Sensor实体中的List字段。然后,当JPA尝试将Sensor实体保存到数据库时,SensorsConverter会自动将List转换为JSON字符串,存储到数据库的SENSORS列。反之,从数据库读取时,也会自动将JSON字符串转换回List。

注意事项与最佳实践

数据库列类型选择:对于较小的JSON数据,VARCHAR2(N)(Oracle)、VARCHAR(N)(MySQL/PostgreSQL)可能足够。对于不确定长度或可能很大的JSON数据,推荐使用CLOB(Oracle)、TEXT(PostgreSQL)、LONGTEXT(MySQL)等大文本类型。如果数据库支持原生JSON数据类型(如PostgreSQL的JSONB,Oracle的JSON类型,MySQL的JSON),优先考虑使用这些类型,它们通常提供更好的查询和索引性能。错误处理:在AttributeConverter中,序列化和反序列化过程可能会抛出JsonProcessingException或IOException。应捕获这些异常,并根据业务需求进行日志记录或抛出自定义的运行时异常,以便更好地诊断问题。性能考虑:频繁地序列化和反序列化大型JSON字符串可能会带来一定的性能开销。如果需要对JSON内部的数据进行复杂查询,直接将JSON存储为字符串可能不是最高效的方式。考虑使用数据库的原生JSON查询功能,或将关键字段抽取为独立列。Schema演进:JSON数据的灵活性意味着其内部结构可以随时变化。但这也给应用带来了挑战,特别是当旧的JSON数据与新的Java对象结构不兼容时。需要有策略来处理JSON结构的演进,例如版本控制或数据迁移脚本。依赖管理:确保项目中包含了Jackson库的依赖,例如:

    com.fasterxml.jackson.core    jackson-databind    2.15.2 

总结

通过使用JPA的AttributeConverter,我们可以优雅地解决将对象列表作为JSON字符串存储到数据库单个列的需求。这种方法提供了极大的灵活性,允许开发者在不牺牲JPA便利性的前提下,处理复杂的非结构化或半结构化数据。理解何时以及如何使用AttributeConverter是构建健壮且灵活的Spring Boot数据持久化层的关键技能之一。

以上就是使用JPA将对象列表作为单列JSON存储的教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1106479.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月2日 18:22:34
下一篇 2025年12月2日 18:22:56

相关推荐

  • CSS mask属性无法获取图片:为什么我的图片不见了?

    CSS mask属性无法获取图片 在使用CSS mask属性时,可能会遇到无法获取指定照片的情况。这个问题通常表现为: 网络面板中没有请求图片:尽管CSS代码中指定了图片地址,但网络面板中却找不到图片的请求记录。 问题原因: 此问题的可能原因是浏览器的兼容性问题。某些较旧版本的浏览器可能不支持CSS…

    2025年12月24日
    900
  • 为什么设置 `overflow: hidden` 会导致 `inline-block` 元素错位?

    overflow 导致 inline-block 元素错位解析 当多个 inline-block 元素并列排列时,可能会出现错位显示的问题。这通常是由于其中一个元素设置了 overflow 属性引起的。 问题现象 在不设置 overflow 属性时,元素按预期显示在同一水平线上: 不设置 overf…

    2025年12月24日 好文分享
    400
  • 网页使用本地字体:为什么 CSS 代码中明明指定了“荆南麦圆体”,页面却仍然显示“微软雅黑”?

    网页中使用本地字体 本文将解答如何将本地安装字体应用到网页中,避免使用 src 属性直接引入字体文件。 问题: 想要在网页上使用已安装的“荆南麦圆体”字体,但 css 代码中将其置于第一位的“font-family”属性,页面仍显示“微软雅黑”字体。 立即学习“前端免费学习笔记(深入)”; 答案: …

    2025年12月24日
    000
  • 为什么我的特定 DIV 在 Edge 浏览器中无法显示?

    特定 DIV 无法显示:用户代理样式表的困扰 当你在 Edge 浏览器中打开项目中的某个 div 时,却发现它无法正常显示,仔细检查样式后,发现是由用户代理样式表中的 display none 引起的。但你疑问的是,为什么会出现这样的样式表,而且只针对特定的 div? 背后的原因 用户代理样式表是由…

    2025年12月24日
    200
  • inline-block元素错位了,是为什么?

    inline-block元素错位背后的原因 inline-block元素是一种特殊类型的块级元素,它可以与其他元素行内排列。但是,在某些情况下,inline-block元素可能会出现错位显示的问题。 错位的原因 当inline-block元素设置了overflow:hidden属性时,它会影响元素的…

    2025年12月24日
    000
  • 为什么 CSS mask 属性未请求指定图片?

    解决 css mask 属性未请求图片的问题 在使用 css mask 属性时,指定了图片地址,但网络面板显示未请求获取该图片,这可能是由于浏览器兼容性问题造成的。 问题 如下代码所示: 立即学习“前端免费学习笔记(深入)”; icon [data-icon=”cloud”] { –icon-cl…

    2025年12月24日
    200
  • 为什么使用 inline-block 元素时会错位?

    inline-block 元素错位成因剖析 在使用 inline-block 元素时,可能会遇到它们错位显示的问题。如代码 demo 所示,当设置了 overflow 属性时,a 标签就会错位下沉,而未设置时却不会。 问题根源: overflow:hidden 属性影响了 inline-block …

    2025年12月24日
    000
  • 为什么我的 CSS 元素放大效果无法正常生效?

    css 设置元素放大效果的疑问解答 原提问者在尝试给元素添加 10em 字体大小和过渡效果后,未能在进入页面时看到放大效果。探究发现,原提问者将 CSS 代码直接写在页面中,导致放大效果无法触发。 解决办法如下: 将 CSS 样式写在一个单独的文件中,并使用 标签引入该样式文件。这个操作与原提问者观…

    2025年12月24日
    000
  • 为什么我的 em 和 transition 设置后元素没有放大?

    元素设置 em 和 transition 后不放大 一个 youtube 视频中展示了设置 em 和 transition 的元素在页面加载后会放大,但同样的代码在提问者电脑上没有达到预期效果。 可能原因: 问题在于 css 代码的位置。在视频中,css 被放置在单独的文件中并通过 link 标签引…

    2025年12月24日
    100
  • 为什么在父元素为inline或inline-block时,子元素设置width: 100%会出现不同的显示效果?

    width:100%在父元素为inline或inline-block下的显示问题 问题提出 当父元素为inline或inline-block时,内部元素设置width:100%会出现不同的显示效果。以代码为例: 测试内容 这是inline-block span 效果1:父元素为inline-bloc…

    2025年12月24日
    400
  • 网络进化!

    Web 应用程序从静态网站到动态网页的演变是由对更具交互性、用户友好性和功能丰富的 Web 体验的需求推动的。以下是这种范式转变的概述: 1. 静态网站(1990 年代) 定义:静态网站由用 HTML 编写的固定内容组成。每个页面都是预先构建并存储在服务器上,并且向每个用户传递相同的内容。技术:HT…

    2025年12月24日
    000
  • 为什么多年的经验让我选择全栈而不是平均栈

    在全栈和平均栈开发方面工作了 6 年多,我可以告诉您,虽然这两种方法都是流行且有效的方法,但它们满足不同的需求,并且有自己的优点和缺点。这两个堆栈都可以帮助您创建 Web 应用程序,但它们的实现方式却截然不同。如果您在两者之间难以选择,我希望我在两者之间的经验能给您一些有用的见解。 在这篇文章中,我…

    2025年12月24日
    000
  • 深入理解CSS框架与JS之间的关系

    深入理解CSS框架与JS之间的关系 在现代web开发中,CSS框架和JavaScript (JS) 是两个常用的工具。CSS框架通过提供一系列样式和布局选项,可以帮助我们快速构建美观的网页。而JS则提供了一套功能强大的脚本语言,可以为网页添加交互和动态效果。本文将深入探讨CSS框架和JS之间的关系,…

    2025年12月24日
    000
  • HTML+CSS+JS实现雪花飘扬(代码分享)

    使用html+css+js如何实现下雪特效?下面本篇文章给大家分享一个html+css+js实现雪花飘扬的示例,希望对大家有所帮助。 很多南方的小伙伴可能没怎么见过或者从来没见过下雪,今天我给大家带来一个小Demo,模拟了下雪场景,首先让我们看一下运行效果 可以点击看看在线运行:http://hai…

    2025年12月24日 好文分享
    500
  • 10款好看且实用的文字动画特效,让你的页面更吸引人!

    图片和文字是网页不可缺少的组成部分,图片运用得当可以让网页变得生动,但普通的文字不行。那么就可以给文字添加一些样式,实现一下好看的文字效果,让页面变得更交互,更吸引人。下面创想鸟就来给大家分享10款文字动画特效,好看且实用,快来收藏吧! 1、网页玻璃文字动画特效 模板简介:使用css3制作网页渐变底…

    2025年12月24日 好文分享
    000
  • tp5如何引入css文件

    tp5引入css文件的方法:1、将css文件放在public目录下的static文件里即可;2、在页面引入中写上“”语句即可。 本教程操作环境:windows7系统、CSS3&&HTML5版、Dell G3电脑。 其实很简单,只需要将css,js,image文件放在这个目录下即可 页…

    2025年12月24日
    000
  • 聊聊CSS 与 JS 是如何阻塞 DOM 解析和渲染的

    本篇文章给大家介绍一下css和js阻塞 dom 解析和渲染的原理。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 hello~各位亲爱的看官老爷们大家好。估计大家都听过,尽量将CSS放头部,JS放底部,这样可以提高页面的性能。然而,为什么呢?大家有考虑过么?很长一段时间,我都是知其…

    2025年12月24日
    200
  • js如何修改css样式

    js修改css样式的方法:1、使用【obj.className】来修改样式表的类名;2、使用【obj.style.cssTest】来修改嵌入式的css;3、使用【obj.className】来修改样式表的类名;4、使用更改外联的css。 本教程操作环境:windows7系统、css3版,DELL G…

    2025年12月24日
    000
  • 如何使用纯CSS、JS实现图片轮播效果

    本篇文章给大家详细介绍一下使用纯css、js实现图片轮播效果的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 .carousel {width: 648px;height: 400px;margin: 0 auto;text-align: center;position: a…

    2025年12月24日
    000
  • js如何修改css

    js修改css的方法:1、使用【obj.style.cssTest】来修改嵌入式的css;2、使用【bj.className】来修改样式表的类名;3、使用更改外联的css文件,从而改变元素的css。 本教程操作环境:windows7系统、css3版,DELL G3电脑。 js修改css的方法: 方法…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信