Репозиторий Jpa и JsonIgnoreProperties по-разному возвращают один и тот же объект в методах getOne () и findAll () - PullRequest
0 голосов
/ 29 октября 2018

Краткая справка

У меня есть два метода в API, созданном в Spring Boot для извлечения данных из базы данных mySQL (через Hibernate и JpaRepository). Существует метод, который возвращает все вхождения в таблице с именем test , а другой метод возвращает test , соответствующий идентификатору, переданному в качестве параметра в вызове GET. Обе точки входа API (посредством сервисов и репозиториев) в итоге вызывают два метода JpaRepository ( findAll () и getOne () соответственно).

Описание проблемы

Проблема, которую я наблюдаю, состоит в том, что в методе findAll () JpaRepository ведет себя иначе, чем getOne () , когда дело доходит до возврата списков внутренних объектов, соответствующих @ Многие-многие отношения в модели. Класс test содержит список обязательных объектов, соответствующих другому объекту модели. Оба класса следующие:

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>();
[...]

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

Здесь приведен фрагмент JSON, возвращенный отображением GET, которое возвращает все тесты. Можно заметить, что первый тестовый объект, объект с idtest 9, имеет коллекцию из 5 реквизитов объектов со свойством реквизиты, Второй тест (с идентификатором 10), напротив, показывает только полный обязательный объект в том, который не существует в предыдущем, показывая только значение идентификатора в другие:

[
{
    "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": ""
}
...
]

А вот и результат вызова метода GET для одного объекта через его идентификатор (в данном случае объект с идентификатором 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 только получает данный реквизит из базы данных в первый раз, а для остальных обнаруживает, что реквизит с этим идентификатором был запрошен, и не запускает запрос. Конечно, это хорошо, но как я могу использовать кэшированные объекты в моих результатах в последующих тестах объектов, с которыми связан реквизит ?

Редактировать : Хорошо, я публикую это как ответ, потому что проблема, которую я описываю в моем оригинальном сообщении, кажется, исчезает. Как я уже говорил в комментариях к исходному сообщению, я удалил аннотацию @JsonIdentityInfo для класса Requisite. Я использовал его, чтобы избежать бесконечной рекурсии из-за отношений @ManyToMany между классами моделей.

Однако я не думаю, что это решение, и определенно не понимаю, как работает @JsonIdentityInfo. Я прочитаю документацию, но я был бы признателен за объяснение, если кто-нибудь сможет его предоставить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...