后端之JPA(EntityGraph+JsonView)

news/2025/2/25 20:10:56

不同表之间的级联操作或者说关联查询是很多业务场景都会用到的。

对于这种需求最朴素的方法自然是手动写关联表,然后对被关联的表也是手动插入数据。但是手写容易最后写成一堆shit代码,而且修改起来也是非常麻烦的。

学会使用现成的工具还是非常有利的。接下来就来说一说JPA如何关联查询和实现级联操作。

@JsonManagedReference和@JsonBackReference

这两个注解配套使用,一般用在一对多关系,也就是外键级联操作。
在注解@OneToMany和@ManyToOne的基础上,一是用@JsonManagedReference,多用@JsonBackReference。同时也可以搭配其他的设置,如cascade和orphanRemoval来实现级联操作。

下面为一个示例
 

java">@Entity
@Data
@Table(name="math_questions")
public class Question {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonManagedReference("question-questionTags") // 指定唯一的引用名称
    private List<QuestionTag> questionTags = new ArrayList<>();
}
@Entity
@Data
@Table(name="tags")
public class Tag {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "tag", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonManagedReference("tag-questionTags") // 指定唯一的引用名称
    private List<QuestionTag> questionTags = new ArrayList<>();
}
@Entity
@Data
@Table(name = "question_tag")
public class QuestionTag {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;  // 主键

    @ManyToOne
    @JoinColumn(name = "question_id")
    @JsonBackReference("question-questionTags") // 与 Question 中的 @JsonManagedReference 对应
    private Question question;

    @ManyToOne
    @JoinColumn(name = "tag_id")
    @JsonBackReference("tag-questionTags") // 与 Tag 中的 @JsonManagedReference 对应
    private Tag tag;
}

那为什么要用到这两个注解呢?直接使用@OneToMany和@ManyToOne不就可以实现级联操作了吗?原因是我这里用的是双向关联,也就是Question中有Question_tag,Tags中有Question_tag,而Question_tag中又有Question和Tags。这样就会导致查询的时候无限递归下去,查到任何一个都可以继续级联查下去。

所以使用@JsonManagedReference和@JsonBackReference可以做到只查一层,也就是对Question表操作,只会影响question_tag表。同样对tags表操作,也只会影响question_tag表。这样就不会一直递归下去。

@EntityGraph

但是这样在之后又遇到一个问题,那就是在使用@EntityGraph进行关联查询的时候,发现只能从question查询到question_tag,再往下就查询不到了,实际上也是查询到了,只不过是因为又@JsonManagedReference和@JsonBackReference导致在最后被过滤掉了。

于是我就在想,有没有一种办法,即能保证不无限递归下去,又能做到查询到我想要的结果:通过question查询到与之相关的标签?

@JsonIdentityInfo

之后发现用@JsonIdentityInfo可以在查询到无限递归时自动停下来。乍一听似乎很好,但实际操作结果是,可能会有很多层嵌套,只能说最后结果对了,但是基本不可操作。

javascript">    {
        "id": 17,
        "questionType": "LATEX",
        "questionImagePath": null,
        "questionLatex": "测试添加标签",
        "answerType": "BOTH",
        "answerImagePath": "images/42f8099e-00ef-4485-a833-65541cb9b0d8_S2的配置命令.png",
        "answerLatex": "1",
        "uploadTime": "2025-02-24 11:25:41",
        "lastPracticeTime": null,
        "correctCount": 0,
        "incorrectCount": 0,
        "questionTags": [
            {
                "id": 1,
                "question": 17,
                "tag": {
                    "id": 1,
                    "questionTags": [
                        1,
                        {
                            "id": 2,
                            "question": {
                                "id": 19,
                                "questionType": "LATEX",
                                "questionImagePath": null,
                                "questionLatex": "多加一个标签",
                                "answerType": "LATEX",
                                "answerImagePath": null,
                                "answerLatex": "现在是能搞到标签的id但是搞不到name",
                                "uploadTime": "2025-02-24 12:09:04",
                                "lastPracticeTime": null,
                                "correctCount": 0,
                                "incorrectCount": 0,
                                "questionTags": [
                                    {
                                        "id": 3,
                                        "question": 19,
                                        "tag": {
                                            "id": 2,
                                            "questionTags": [
                                                3
                                            ],
                                            "tag_name": "测试用例1"
                                        }
                                    },
                                    2
                                ]
                            },
                            "tag": 1
                        }
                    ],
                    "tag_name": "测试用例2"
                }
            }
        ]
    },

这么深的嵌套就不说了,重复的内容还会再下次直接用数字代替,这不又得多建立一层映射关系,想想都麻烦。

最后感觉没啥办法了,想着保留级联操作,然后老老实实自己写SQL吧。手写的唯一好处就是可以定制化,虽然可能会有很多bug,不好维护,但是在功能都实现不了的情况下就不要求这么多了。 

不过,自己动手造轮子是不可能的,是绝对不允许的。

这不,又找到了一个不错的好玩意,起码能解决我的问题。依旧是使用cascade来保证级联操作。然后使用EntityGraph来关联查询。但是不使用@JsonManagedReference和@JsonBackReference,因为这个其实适用于比较有明显的主次关系的结构。我的业务两者只能说使用频率有差距,但地位是相同的。所以其实这个不合适。

那不使用@JsonManagedReference和@JsonBackReference如何保证不无限循环的呢?

答案是@JsonView

有点小麻烦,需要在每个字段都标注(不知道能不能批量标注),但是正因为是具体到每个字段,而且可以标注多个,就可以非常灵活的选择展示哪些,不展示哪些,进而避免双向关联的循环。说白了就是把DTO换成了注解。靠人工手动标注来防止出错。

起码,这个方法是解决了我的问题。

最后补充几点,上述被我否定的几种方法,不是不好,只是不适用于我的业务罢了。其实EntityGraph挺好的,就是搭配JsonView,可以做到类似版本控制,就是可以写多个controller,给每个controller不同的JsonView就行,不需要修改原有的就可以灵活调整展示内容,有点类似增量开发。

再说一句,JsonView不同模式之间可以继承挺不错的。


http://www.niftyadmin.cn/n/5865911.html

相关文章

硬件工程师入门教程

1.欧姆定律 测电压并联使用万用表测电流串联使用万用表&#xff0c;红入黑出 2.电阻的阻值识别 直插电阻 贴片电阻 3.电阻的功率 4.电阻的限流作用 限流电阻阻值的计算 单位换算关系 5.电阻的分流功能 6.电阻的分压功能 7.电容 电容简单来说是两块不连通的导体加上中间的绝…

Ubuntu20.04之VNC的安装使用与常见问题

Ubuntu20.04之VNC的安装与使用 安装图形桌面选择安装gnome桌面选择安装xface桌面 VNC-Server安装配置开机自启 VNC Clientroot用户无法登入问题临时方案永久方案 安装图形桌面 Ubuntu20.04主流的图形桌面有gnome和xface两种&#xff0c;两种桌面的安装方式我都会写&#xff0c…

PHP-综合4

[题目信息]&#xff1a; 题目名称题目难度PHP-综合42 [题目考点]&#xff1a; PHP综合训练[Flag格式]: SangFor{Ouk3i63BuShgxqdRcn_9kMNqKFDe5j4f}[环境部署]&#xff1a; docker-compose.yml文件或者docker tar原始文件。 http://分配ip:2087[题目writeup]&#xff1a;…

自学Linux系统软件编程第四天

进程 概念 进程&#xff1a;就是程序执行的过程&#xff0c;包括创建、调度和消亡&#xff0c;是活的 程序&#xff1a;一段数据的…

vue2中,打包报错ERROR in /node_modlules/@types/lodash/common/common.d.ts 26

报错如下&#xff1a; 解决 尝试安装lodash另外一个版本&#xff0c;然后重新打包 npm install --save types/lodash4.14.74

WPS计算机二级•文档的页面设置与打印

听说这是目录哦 纸张大小页边距和装订线❤️‍&#x1f525;打印界面讲解❤️缩印&#x1f495;打印作文稿纸&#x1f49e;将文档打印成书籍&#x1f493;限制编辑设置&#x1f497;给文字文档加密&#x1f496;文档导出为 PDF格式&#x1f498;协作编辑模式&#x1f49d;能量站…

使用 Apache Jena 构建 RDF 数据处理与查询服务

一、引言 随着语义网和知识图谱技术的不断发展&#xff0c;RDF&#xff08;Resource Description Framework&#xff09;作为一种用于描述资源的框架&#xff0c;被广泛应用于知识表示和数据集成。Apache Jena 是一个功能强大的 Java 框架&#xff0c;用于处理 RDF 数据和 SPA…

Spring Boot 中的日志管理

一、日志框架选择 1. 主流框架对比 框架特点Spring Boot 默认支持Logback- 性能优异&#xff0c;Spring Boot 默认集成- 支持自动热更新配置文件✅ (默认)Log4j2- 异步日志性能更强- 支持插件扩展- 防范漏洞能力更好❌ (需手动配置)JUL (JDK自带)- 无需额外依赖- 功能简单&am…