#java #spring
#java #spring
Вопрос:
Мне нужно сохранить некоторые объекты в базе данных с пользовательскими (динамическими созданными) столбцами. Для достижения этой цели я создал дополнительный столбец в таблицах с динамическими столбцами, а тип столбца — JSONB (я работаю с postgresql в этом проекте). На стороне приложения я сохраняю эти дополнительные столбцы на карте, используя библиотеку типов гибернации от Влада Михалча. Чтобы вернуть требуемые веб-ресурсы из моего приложения, мне нужна таблица map в pojo Java, и после этого просто отправьте ее. Но я столкнулся с проблемой при преобразовании моего pojo в json. Веб требует простого JSON без вложенных свойств, например:
{
"name": "name",
"1": 100,
"2": true
}
В JSON выше 1 и 2 указаны пользовательские столбцы (да, мы решили использовать идентификаторы для этой цели). Но, если я сопоставляю свой класс с Map в качестве свойства класса, ответ будет содержать вложенную структуру:
{
"name": "name",
"customAttributes": {
"1": 100,
"2": "test"
}
}
На самом деле, нет ничего сложного в том, чтобы привести класс к карте, удалить ключ «CustomAttributes» и вернуть значение этой карты обратно. Мое решение в коде ниже:
public Map<String, Object> mapToResource(Account account) {
var accountMap = objectMapper.convertValue(account, new TypeReference<Map<String, Object>>() {});
if (isClassHasCustomAttributes(account.getClass())) {
var customAttributesMap = account.getCustomAttributes();
accountMap.remove("customAttributes");
accountMap.putAll(customAttributesMap);
}
return accountMap;
}
private boolean isClassHasCustomAttributes(Class<?> classUnderCheck) {
return Arrays.stream(classUnderCheck.getDeclaredFields()).anyMatch(field -> field.getName().equals("customAttributes"));
}
Я добавил проверку свойства с именем CustomAttributes, потому что не все объекты содержат карту как свойство, и я использую общие для всех моих таблиц в приложении. Я не уверен, что приведение всех моих классов в Map на уровне контроллера является хорошим решением для этой задачи.
В общем, мне нужно знать — есть ли лучший способ вернуть запрошенный объект без приведения классов в Map<Строка, объект> или нет?
Комментарии:
1. Проблема с использованием карты заключается в том, что у вас должны быть уникальные ключи. Что, если у вас есть пользовательский атрибут с ключом ‘name’?
2. Разве люди обычно не используют ObjectMapper от Jackson для выполнения подобных действий? Для меня это похоже на повторное изобретение колеса.
3. @RoddyoftheFrozenPeas вы определенно правы. Чтобы избежать этой ситуации — идентификатор пользовательского атрибута вместо имени в столбце JSONB, а также в карте.
4. @djangofan спасибо за ваш комментарий. Да, ObjectMapper позволяет отображать классы в JSON, но, возможно, мне следует использовать другой метод ObjectMapper или что-то еще, чтобы избежать вложенной структуры в конечном JSON idk
Ответ №1:
На самом деле, решение было так близко (или, может быть, нет, не имеет значения). В Jackson есть аннотация @JsonAnyGetter
. Все, что вам нужно сделать (если вы столкнулись с подобной проблемой, как я), это аннотировать ваше свойство карты. Джексон добавит все ключевые значения из Map в JSON, когда объект будет сериализован из pojo в json.
Вот пример:
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Boolean isBlocked;
private Integer childCount;
private LocalDateTime createdAt;
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
private Map<String, Object> customAttributes;
@JsonAnyGetter
public Map<String, Object> getCustomAttributes() {
return customAttributes;
}
}