Jackson / Hibernate, мета-методы и сериализация - PullRequest
1 голос
/ 27 марта 2019

У нас есть много классов спящих сущностей с вложенными отношениями. Я пытаюсь найти лучший способ конвертировать данную сущность в эквивалентный формат json.

Я знаю о JsonIgnore, миксинах Джексона и JsonFilters и экспериментировал с ними.

Проблемы, с которыми мы сталкиваемся, следующие:

  1. Объекты, связанные друг с другом с помощью OneToMany / JoinColumn или аналогичных аннотаций - создают бесконечную рекурсию.

  2. Полезные или мета методы. Джексон, похоже, использует методы получения, а не поля. Некоторые из методов являются мета-методами, которые не связаны ни с какими столбцами. Примером метода getTotal может быть суммирование значений пары фактических полей без фактического итогового поля. Аналогичный случай для других случаев, таких как getIncomeAccounts, который фильтрует учетные записи на основе некоторых критериев.

Фильтр Джексона, который я написал, немного помогает - он проверяет, существует ли поле класса на основе имени свойства Джексона. Он также проверяет наличие аннотаций типа JoinColumn, чтобы избежать рекурсии, если поле существует.

Есть ли способ получить метаданные из спящего режима и использовать их в своих фильтрах? В основном для данного объекта сущности мне интересно знать, будет ли свойство, которое Джексон хочет сериализовать, сопоставляться со столбцом и сериализироваться, только если с ним связано значение столбца. Hibernate наверняка знает свойства и сопоставления столбцов.

Опции mixins и jsonignore работоспособны, но тогда мы зависим от того, что отдельный разработчик помнит о размещении аннотаций в соответствующем месте. Обычно забытые аннотации обнаруживаются слишком поздно, когда мы действительно хотим получить экспортированные данные для анализа некоторой проблемы и локального создания контрольного примера.

Ответы [ 2 ]

1 голос
/ 28 марта 2019

Если вы не хотите создавать новую модель POJO для представления JSON на уровне REST API, вам необходимо подготовить модель ORM перед переходом на слой Jackson.

Включить HibernateXModule

Вы должны начать с включения Hibernate module , который наилучшим образом соответствует вашей Hibernate версии. Это решает многие проблемы с lazy-loadings и внутренними типами данных.

Двунаправленные отношения

Узнайте о параметрах , которые Jackson имеют для решения проблемы циклов во время сериализации. Основные аннотации:

Определить правильную видимость

Вы можете определить глобальную видимость в ObjectMapper, , как указать Джексона для использования только полей - предпочтительно глобально и настроить его, если необходимо для данного класса, используя аннотацию JsonAutoDetect .

Вид POJO модель

Вероятно, в большинстве случаев вы сможете повторно использовать POJO модель, созданную для ORM. В случаях, когда настройка вывода JSON с аннотацией будет сложной, вы всегда можете создать собственный класс и сопоставить модель ORM с этой моделью вручную на дополнительном уровне отображения / бизнес-уровне.

Настройка сериализаторов

Если вам нужно обрабатывать некоторые пользовательские аннотации или некоторые поля в общем виде, вы можете использовать BeanSerializerModifier и BeanPropertyWriter . Это нелегко реализовать, но это очень мощно. Пример использования здесь: Пользовательская сериализация и десериализация Джексона .

Простой пример того, как это можно сделать для двунаправленных отношений и настройки видимости:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.util.Arrays;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Item item0 = new Item();
        item0.setId(1);
        item0.setItemName("Item 0");

        Item item1 = new Item();
        item1.setId(2);
        item1.setItemName("Item 1");

        List<Item> items = Arrays.asList(item0, item1);
        User user = new User();
        user.setId(123);
        user.setName("Rick");
        user.setUserItems(items);
        items.forEach(item -> item.setOwner(user));

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));

        System.out.println(mapper.writeValueAsString(user));
    }
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class User {

    private int id;
    private String name;
    private List<Item> userItems;

    // getters, setters
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class Item {

    private int id;
    private String itemName;
    private User owner;

    // getters, setters
}

Над отпечатками кодов:

{"id":123,"name":"Rick","userItems":[{"id":1,"itemName":"Item 0","owner":123},{"id":2,"itemName":"Item 1","owner":123}]}
1 голос
/ 27 марта 2019

Что я обычно делаю, так это сопоставление сущностей с DTO вручную или с помощью таких инструментов, как MapStruct .
Это даст вам максимумгибкость, с небольшим перерасходом в начале, к сожалению.
Однако со временем вы увидите, что оно того стоило.

Джексон, GSON и другие инструменты сериализации явно ограничены в своих возможностях.сделать из коробки, и этот вид настройки требует слишком много работы, imho, и в то же время его трудно понять и поддерживать.

Сохраняйте это простым.

...