Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Nixic
Member

Откуда: Perm
Сообщений: 1871
Всем привет!
Переписываю тут с C# на Java сервис небольшой и вот используется Postgres и в нем процедура, которая принимает json на вход.

Я раньше такое не использовал, всегда простые типы передавал, а тут json парсится уже внутри процедуры и данные идут дальше.
Начал гуглить и ничего толком не нашел, кроме как переписать процедуру в постгресе) Но на это вряд ли пойдут, так как C# сервис тоже будет использоваться и придется и в нем переписывать коллегам.
В общем пытаюсь сделать так:
StoredProcedureQuery storedProcedure = entityManager.createStoredProcedureQuery("ch.dm_get_model_meta");
storedProcedure.registerStoredProcedureParameter("json", JsonObject.class, ParameterMode.IN);
storedProcedure.setParameter( "json", new JSONObject("{\"modelid\" : " + " 1489 }'")); // тут я уже напрямую строку слепил:) 


и получаю на 2-й строке ошибку
java.lang.IllegalArgumentException: Type cannot be null

Ну ладно, думаю, отдам тебе String. И получаю от постгреса отворот-поворот:
Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Error calling CallableStatement.getMoreResults] with root cause
org.postgresql.util.PSQLException: ERROR: function ch.dm_get_model_meta(character varying) does not exist
  Подсказка: No function matches the given name and argument types. You might need to add explicit type casts.
  Позиция: 15


Что вот с этим всем делать - не знаю :) Может кто подскажет? Спасибо.
Буду, конечно, еще искать варианты.
21 июн 19, 18:26    [21913170]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Lelouch
Member

Откуда: Москва
Сообщений: 1721
Nixic,

https://www.programcreek.com/java-api-examples/index.php?api=org.postgresql.util.PGobject
https://stackoverflow.com/questions/35844138/how-can-i-insert-json-object-into-postgres-using-java-preparedstatement
21 июн 19, 18:58    [21913181]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Lelouch
Member

Откуда: Москва
Сообщений: 1721
Хотя, это для JDBC, а у вас JPA.
21 июн 19, 19:01    [21913183]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Lelouch
Member

Откуда: Москва
Сообщений: 1721
Nixic
Буду, конечно, еще искать варианты.


ИМХО, ищите в сторону имплементации своего собственного типа (копать от org.hibernate.usertype.UserType).

Надеюсь, кто-то подскажет способ проще (для Hibernate).
21 июн 19, 19:14    [21913185]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic,
Ничего не понял.
В шарпе json в хранимку передается как строка. Так?
И хранимка уже есть в постгри.
Так?
21 июн 19, 21:01    [21913203]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Lelouch
проще (для Hibernate).
хибер вообще не нужен для json.
21 июн 19, 21:02    [21913205]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic
переписывать коллегам.
они зря json начали пихать в базу.
Вы можете написать рядом свою хранимку с параметром строка и внутри вызвать их хранимку с параметром json.
21 июн 19, 21:07    [21913207]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Lelouch
Member

Откуда: Москва
Сообщений: 1721
PetroNotC Sharp,
Ну лично у нас используется, как раз через UserType. Проблем не наблюдается.
22 июн 19, 01:11    [21913250]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Nixic
Member

Откуда: Perm
Сообщений: 1871
PetroNotC Sharp
В шарпе json в хранимку передается как строка. Так?
И хранимка уже есть в постгри.
Так?

create function dm_get_model_meta(req json) returns json
  language sql
as ... 

Не, в хранимку передается как раз тип json, судя по коду, хотя я не очень хорошо разбираюсь в этом коде, как и в C# ))
Затем из json вытаскивается значение, как-то так:
(req->>'modelId')::int8)

И используется уже другими процедурами.
22 июн 19, 08:30    [21913281]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Nixic
Member

Откуда: Perm
Сообщений: 1871
Lelouch
PetroNotC Sharp,
Ну лично у нас используется, как раз через UserType. Проблем не наблюдается.

Да, спасибо, видимо так и нужно делать.
Я уже использовал UserType для одной сущности, но для чтения, в GET запросе. Правда использовал com.google.gson.JsonObject,
а не org.json.JSONObject, нужно будет почитать про особенности и отличия этих типов в доках, а то я пока просто их использовал особо не задумываясь.
JSONObject отлично подошел, чтобы возвращать из GET запросов многоуровневые json'ы :)
+ JsonUserType
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.SerializationException;
import org.hibernate.usertype.UserType;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class JsonUserType implements UserType {

    @Override
    public int[] sqlTypes() {
        return new int[]{Types.CHAR};
    }

    @Override
    public Class<JsonObject> returnedClass() {
        return JsonObject.class;
    }

    @Override
    public boolean equals(final Object x, final Object y) {
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        return x.equals(y);
    }

    @Override
    public int hashCode(final Object x) {
        if (x == null) {
            return 0;
        }

        return x.hashCode();
    }

    @Nullable
    @Override
    public Object nullSafeGet(final ResultSet rs,
                              final String[] names,
                              final SharedSessionContractImplementor session,
                              final Object owner) throws SQLException {
        final String json = rs.getString(names[0]);
        if (json == null) {
            return null;
        }

        final JsonParser jsonParser = new JsonParser();
        return jsonParser.parse(json).getAsJsonObject();
    }

    @Override
    public void nullSafeSet(final PreparedStatement st,
                            final Object value,
                            final int index,
                            final SharedSessionContractImplementor session) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.OTHER);
            return;
        }

        st.setObject(index, value.toString(), Types.OTHER);
    }

    @Nullable
    @Override
    public Object deepCopy(@Nullable final Object value) {
        if (value == null) {
            return null;
        }
        final JsonParser jsonParser = new JsonParser();
        return jsonParser.parse(value.toString()).getAsJsonObject();
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(final Object value) {
        final Object deepCopy = deepCopy(value);

        if (!(deepCopy instanceof Serializable)) {
            throw new SerializationException(
                    String.format("deepCopy of %s is not serializable", value), null);
        }

        return (Serializable) deepCopy;
    }

    @Nullable
    @Override
    public Object assemble(final Serializable cached, final Object owner) {
        return deepCopy(cached);
    }

    @Nullable
    @Override
    public Object replace(final Object original, final Object target, final Object owner) {
        return deepCopy(original);
    }
}


В ентити прописал так:

+ SomeEntity
@Entity
@Data
@NoArgsConstructor
@Table(name = "some_view", schema = "some_schema")
@TypeDefs({@TypeDef(name = "JsonUserType", typeClass = JsonUserType.class)})
public class SomeEntity {

    @Id
    private Long id;
    @Type(type = "JsonUserType")
    private JsonObject src;

}


В итоге получаю json в нормальном его виде для src, что-то типа { "src": {...}} со множеством вложенных элементов, как раз такой как нужно фронту.
А вот с отправкой через POST да еще в процедуру пока не использовал.
22 июн 19, 08:48    [21913283]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic
Затем из json вытаскивается значение, как-то так:
я и написал что зря они так сделали. Такой тип не тащат в базу.
UserType я считаю оверхед и ненужное усложнение.
Как и хибернейт. Он для маппинга.
22 июн 19, 12:51    [21913329]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic,
Почему нельзя добавить хранимку обертку вы не ответили.
22 июн 19, 12:52    [21913332]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic
как раз такой как нужно фронту.
в шарпе и java модель в базе делают без json, xml и так далее. Простыми типами.
А потом уже в аппСервере только для передачи на фронт КОНВЕРТИРУЮТ в json.
22 июн 19, 12:56    [21913334]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic
Да, спасибо, видимо так и нужно делать.
-1
22 июн 19, 13:02    [21913336]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Nixic
Member

Откуда: Perm
Сообщений: 1871
PetroNotC Sharp
Nixic
как раз такой как нужно фронту.
в шарпе и java модель в базе делают без json, xml и так далее. Простыми типами.
А потом уже в аппСервере только для передачи на фронт КОНВЕРТИРУЮТ в json.

Так и есть частично, но есть такая штука как различные графики джаваскриптовые с кучей полей своих и все их в бд тащить,
как-то не очень хорошо, в силу постоянно меняющегося API javascript проще закинуть туда json, и да, лучше это делать в String.

И так сейчас фронт иногда ловит баги в виду того, что старые сохраненные ранее json в БД имеют/или уже не имеют тех полей, которые требуются джаваскриптовым компонентам.
Частично в БД реализовано так, что на вход идёт именно json, в C# видимо с таким проблем особо нет и какие-то их либы это легко отправляют, я подробнее не изучал.

Еще судя по десяткам процедур, которые уже есть в бд, они работают именно с json типом, своими методами работы с json,
всё это переписывать явно не будут, тем более, что сервис на C# будет дальше работать. Но так как не у всех сервера на винде, то нужен сервис, который будет работать под линукс.

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

Кроме того, вся эта махина используется для OLAP, который так же выдвигает некоторые требования к типу данных.

В общем всё сложно)))
22 июн 19, 13:54    [21913344]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Nixic
Member

Откуда: Perm
Сообщений: 1871
PetroNotC Sharp
Nixic,
Почему нельзя добавить хранимку обертку вы не ответили.

Это я уточню, спасибо. В понедельник уже.
22 июн 19, 13:54    [21913345]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
Nixic,
Да. OLAP это денормализованные данные. Там пофиг что хранить, пусть даже сам блоб.
Тогда свою хранимку со строкой и сказать что в java знать о типе json совсем нет необходимости.
Валидацию json не делаем. Что есть в бд, строкой прочитали и отправили на http клиент.
Аппсервер olap в java в роли импотента REST.
22 июн 19, 15:08    [21913365]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 552
PetroNotC Sharp
UserType
если просто читать из хранимки и передать json то это не нужно.
Это нужно, если в базе ОLTP неизветный в java тип его НАДО В КЛАССЫ ИЛИ КОЛЛЕКЦИЮ.
22 июн 19, 15:15    [21913371]     Ответить | Цитировать Сообщить модератору
 Re: StoredProcedureQuery registerStoredProcedureParameter JSONObject как? )  [new]
Nixic
Member

Откуда: Perm
Сообщений: 1871
Решение оказалось относительно простым, hibernate не мог кастануть ответ от функции в json.
То есть получалось отправить входящий параметр в функцию в формате json через свой UserType.
А вот возвращала функция тип Types.OTHER
И никак нельзя кастануть это в Types.JAVA_OBJECT
В итоге сделали по другому, кастуем объекты в нужные типы на уровне постгреса:
       Query q = entityManager.createNativeQuery("select CAST(f.* as text) from ch.dm_get_model_meta(CAST (? as json)) f");
       q.setParameter(1, "{\"modelId\" : " + " 1489 }");
       return q.getResultList(); // здесь можно взять и вернуть строку resultList.get(0).toString() и преобразовать в JsonObject и отдать на клиента


Вопрос закрыт, всем спасибо :)
24 июн 19, 13:41    [21914043]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить