首页 > 解决方案 > Jpa 存储库和 JsonIgnoreProperties 在 getOne() 和 findAll() 方法中以不同方式返回相同的对象

问题描述

简要背景

我在 Spring Boot 上创建的 API 中有两种方法可以从 mySQL 数据库中检索数据(通过 Hibernate 和 JpaRepository)。有一种方法可以返回名为test的表中的所有出现,另一种方法返回与在 GET 调用中作为参数传递的 id 对应的测试。两个 API 入口点(通过服务和存储库)最终都会调用两个 JpaRepository 方法(分别是findAll()getOne())。

问题描述

我观察到的问题是,在findAll()方法中,当返回与模型中的 @ManyToMany 关系对应的内部对象列表时,JpaRepository 的行为与getOne()不同。测试类具有与模型的不同实体相对应的必需对象列表两个类如下:

Pectest.java

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="idtest")
public class Pectest {
@Id
@Column(name="idtest")
private long idtest;
private String testname;
private String testdescription;

[...]
// Other properties and fields
[...]

@ManyToMany(fetch = FetchType.LAZY
    )
@JoinTable(name = "test_checks_requisite",
        joinColumns = @JoinColumn(name = "test_idtest"),
        inverseJoinColumns = @JoinColumn(name = "requisite_idrequisite")
    )
@JsonProperty(access=Access.READ_ONLY)
private Set<Requisite> requisites = new HashSet<Requisite>();
[...]

必需的.java

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="idrequisite")
public class Requisite {
@Id
@Column(name="idrequisite")
private long idrequisite;
private String Name;
private String Title;
private String Description;
@ManyToOne(cascade = {CascadeType.PERSIST}, fetch=FetchType.LAZY)
@JoinColumn(name="functionality_idfunctionality")
@JsonProperty(access=Access.WRITE_ONLY)
private Functionality functionality; 

@ManyToMany(mappedBy="requisites")
private Set<Pectest> tests = new HashSet<Pectest>();

这是返回所有测试的 GET 映射返回的 JSON 片段。可以观察到,第一个测试对象,即idtest 为9 的测试对象在 Requirements 属性下具有 5 个必需对象的集合。相反,第二个测试(id 为 10)仅显示前一个中不存在的完整必需对象,仅显示其他对象中的 id 值:

[
{
    "idtest": 9,
    "testtype": {
        "idTestType": 5,
        "testTypeName": "Manual"
    },
    "datatype": null,
    "requisites": [
        {
            "idrequisite": 7,
            "name": "REQ-0006",
            "description": "Campo para traducción de nombres, debe mostrar el nombre en el idioma seleccionado por el cliente.",
            "title": "DisplayName"
        },
        {
            "idrequisite": 4,
            "name": "REQ-0003",
            "description": "Se ofrece acceso a parámetros a través de este tipo de nodo",
            "title": "Parameter Type"
        },
        {
            "idrequisite": 5,
            "name": "REQ-0004",
            "description": "El BrowseName de las variables debe ser sólo el último campo de las variables exportadas por FW de BR (ItemName)",
            "title": "BrowseName"
        },
        {
            "idrequisite": 3,
            "name": "REQ-0002",
            "description": "Se ofrece acceso a variables analógicas a través de este tipo de nodo",
            "title": "Analog Type"
        },
        {
            "idrequisite": 2,
            "name": "REQ-0001",
            "description": "Se ofrece acceso a variables digitales a través de este tipo de nodo",
            "title": "Digital Type"
        }
    ],
    "testDescription": "El servidor es capaz de devolver correctamente todos los tipos de dato.",
    "lastUpdated": null,
    "testName": "Lectura de DataTypes",
    "dateCreated": null,
    "testURL": ""
},
{
    "idtest": 10,
    "testtype": {
        "idTestType": 5,
        "testTypeName": "Manual"
    },
    "datatype": {
        "idDataType": 2,
        "dataTypeName": "Boolean"
    },
    "requisites": [
        7,
        5,
        {
            "idrequisite": 10,
            "name": "REQ-0009",
            "description": "Se generan a partir de los niveles de acceso de la variable. Se deben escalar los valores, de 256 a 1000.",
            "title": "AccessLevel & UserAccessLevel"
        },
        2
    ],
    "testDescription": "El servidor es capaz de admitir escrituras correctamente de todos los tipos de dato.",
    "lastUpdated": null,
    "testName": "Escritura de DataTypes",
    "dateCreated": null,
    "testURL": ""
}
...
]

下面是通过其 id 调用单个对象的 GET 方法的结果(在这种情况下,id 为 10 的对象,这在前面的 JSON 中是不正确的):

{
"idtest": 10,
"testtype": {
    "idTestType": 5,
    "testTypeName": "Manual"
},
"datatype": {
    "idDataType": 2,
    "dataTypeName": "Boolean"
},
"requisites": [
    {
        "idrequisite": 7,
        "name": "REQ-0006",
        "description": "Campo para traducción de nombres, debe mostrar el nombre en el idioma seleccionado por el cliente.",
        "title": "DisplayName"
    },
    {
        "idrequisite": 2,
        "name": "REQ-0001",
        "description": "Se ofrece acceso a variables digitales a través de este tipo de nodo",
        "title": "Digital Type"
    },
    {
        "idrequisite": 5,
        "name": "REQ-0004",
        "description": "El BrowseName de las variables debe ser sólo el último campo de las variables exportadas por FW de BR (ItemName)",
        "title": "BrowseName"
    },
    {
        "idrequisite": 10,
        "name": "REQ-0009",
        "description": "Se generan a partir de los niveles de acceso de la variable. Se deben escalar los valores, de 256 a 1000.",
        "title": "AccessLevel & UserAccessLevel"
    }
],
"testDescription": "El servidor es capaz de admitir escrituras correctamente de todos los tipos de dato.",
"lastUpdated": null,
"testName": "Escritura de DataTypes",
"dateCreated": null,
"testURL": ""

}

附加信息

我观察到以下行为:

由于这两点,我认为这似乎是一些缓存问题(我没有启用二级缓存,也没有启用查询级缓存)。我的意思是,似乎hibernate第一次只从数据库中检索给定的必要条件,其余的检测到已请求具有该id的必要条件并且不启动查询。当然,这很好,但是我怎样才能在我的结果中的缓存对象在具有该必要条件的后续测试对象中?

编辑:好的,我将其发布为答案,因为我在原始帖子中描述的问题似乎消失了。正如我在对原始帖子的评论中所说,我删除了 Requisite 类上的 @JsonIdentityInfo 注释。由于模型类之间的@ManyToMany 关系,我使用它来避免无限递归。

但是,我不认为这是一个解决方案,而且我绝对不明白 @JsonIdentityInfo 是如何工作的。我会阅读文档,但如果有人可以提供解释,我将不胜感激。

标签: hibernatejpaspring-data-jpa

解决方案


推荐阅读