Параметризованный конструктор объекта гибернации - PullRequest
1 голос
/ 08 мая 2020

TL; DR

Есть ли способ чисто передать некоторые аргументы объекту, созданному в спящем режиме, после загрузки?

Что у меня есть

@Entity
public class MyEntity {
    @Lob
    private String mapAsJson;

    @Transient
    private Map<Long, Double> map = new HashMap<>();

    // Getters, null-checks, and all that. Always keeping these two in sync, or at least before it persists
}

Что я хочу

@Entity
public class MyEntity {
    @Embedded
    private MyEmbedded<Long, Double> embeddedMap = new MyEmbedded<>();
}

@Embeddable
public class MyEmbedded<K, V> {
    @Lob
    private String mapAsJson;
    @Transient
    private final Map<K, V> map = new HashMap<>();

    @PreUpdate @PrePersist
    private void storeAsJson() {
        // JsonUtil encapsulates com.fasterxml.jackson.databind.ObjectMapper logic
        mapAsJson = JsonUtil.toString(map);
    }

    @PostLoad
    private void loadFromJson() {
        map.clear();
        if (mapAsJson != null) 
            // JsonUtil encapsulates com.fasterxml.jackson.databind.ObjectMapper logic
            map.putAll(JsonUtil.fromString(mapAsJson, HashMap.class));
    }
}

Зачем мне это

Просто для красивой инкапсуляции гидратация в / из этого рабочего объекта карты. Если бы у меня было еще 2 таких поля, это было бы много повторяющегося кода. Это, конечно, очень специфический пример c, но его можно распространить на другие объекты, кроме карты.

Почему это не работает

Этот JsonUtil.fromString(mapAsJson, HashMap.class) в основном угадывает типы. В настоящее время он возвращает карту <String, Integer>, но мне действительно нужно Double.

Возможно не удалось решение # 1

Это конечно, не работает, потому что hibernate не знает, какие у меня параметры для встроенного объекта.

@Entity
public class MyEntity {
    @Embedded
    private MyEmbedded<Long, Double> embeddedMap = new MyEmbedded<>(Long.class, Double.class);
}

@Embeddable
public class MyEmbedded<K, V> {
    @NotNull
    private Class<K> keyClass;
    @NotNull
    private Class<V> valClass;

    protected MyEmbedded() {

    }

    public MyEmbedded(Class<K> keyClass, Class<V> valClass) {
        this.keyClass = keyClass;
        this.valClass = valClass;
    }

    // [...]

    @PostLoad
    private void loadFromJson() {
        map.clear();
        if (mapAsJson != null)
            // NOTICE THIS DIFFERENT LINE
            map.putAll(JsonUtil.fromString(mapAsJson, HashMap.class, keyClass, valClass));
    }
}

Возможное решение # 2

@Embeddable
public class MyEmbedded<K, V> {
    @NotNull
    private Class<K> keyClass;
    @NotNull
    private Class<V> valClass;

    // [...]

    // Call this in the entity once, on the embedded. Maybe on @PostLoad?
    public MyEmbedded<K, V> init(Class<K> keyClass, Class<V> valClass) {
        this.keyClass = keyClass;
        this.valClass = valClass;
    }
}

1 Ответ

0 голосов
/ 08 мая 2020

Поскольку вы дополнительно указали, что JsonUtil является собственным классом, я бы предложил следующую подпись для JsonUtil.fromString:

public static <K, V> Map<K, V> fromString(String json, Class<K> keyClass,
        Class<V> valueClass, @SuppressWarnings("rawtypes") Class<? extends Map> mapClass) {
// ...

Таким образом, ваша возвращенная карта будет выведена с правильными типами , и вы можете использовать свой вариант "Что я хочу". Вы можете иметь разные именованные подписи для другого, менее громоздкого доступа, который делегирует этому методу:

public static <V> Map<V> fromStringLongKey(String json,
        Class<V> valueClass, @SuppressWarnings("rawtypes") Class<? extends Map> mapClass) {
    return fromString(json, Long.class, valueClass, mapClass);
}
//...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...