jimmer icon indicating copy to clipboard operation
jimmer copied to clipboard

[Feature Request] - Add information about AbstractScalarProvider for working with JSON to the documentation

Open storympro opened this issue 1 year ago • 2 comments

Reason

I couldn't find an explanation for working with jsonb to make the custom field type work correctly.

https://babyfish-ct.github.io/jimmer-doc/docs/configuration/scala-provider

Description

I have custom type:

import com.fasterxml.jackson.annotation.JsonValue;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Translation implements Serializable {
    private final Map<String, String> translation;

    public Translation() {
        this.translation = new HashMap<>();
    }

    public Translation(Map<String, String> translation) {
        this.translation = new HashMap<>(translation);
    }

    public Translation set(String lang, String value) {
        this.translation.put(lang, value);
        return this;
    }

    public String get(String lang) {
        return this.translation.get(lang);
    }

    @JsonValue
    public Map<String, String> get() {
        return Collections.unmodifiableMap(this.translation);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Translation that = (Translation) o;
        return Objects.equals(translation, that.translation);
    }

    @Override
    public int hashCode() {
        return Objects.hash(translation);
    }

    @Override
    public String toString() {
        return "Translation{" +
                "translation=" + translation +
                '}';
    }
}

Implemented and working AbstractScalarProvider

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.babyfish.jimmer.sql.runtime.AbstractScalarProvider;
import org.springframework.stereotype.Component;

@Component
public class TranslationScalarProvider extends AbstractScalarProvider<Translation, String> {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Translation toScalar(String sqlValue) throws JsonProcessingException {
        if (sqlValue.isEmpty()) {
            return new Translation();
        }

        return new Translation(objectMapper.readValue(sqlValue, new TypeReference<>() {
        }));
    }

    @Override
    public String toSql(Translation scalarValue) throws JsonProcessingException {
        return objectMapper.writeValueAsString(scalarValue.get());
    }

    @Override
    public boolean isJsonScalar() { ///////////////////////// IS REQUIRED
        return true;                           ///////////////////////// NEED TRUE
    }
}

My entity

@Entity
public interface Example {

    /**
     * Назва
     */
    @Nonnull
    //@Serialized ///////////////////////// REMOVE IT
    Translation name();

    /**
     * Опис
     */
    @Nonnull
    //@Serialized ///////////////////////// REMOVE IT
    Translation description();
}

If you leave @Serialized , then in the absence of isJsonScalar() it will return a value, but there will be an error when inserting the field.

Existing solutions

No response

storympro avatar Jan 20 '25 18:01 storympro

there will be an error when inserting the field.

Stacktrace please

babyfish-ct avatar Jan 20 '25 21:01 babyfish-ct

there will be an error when inserting the field.

Stacktrace please

if I added @ Serialized. The name and description are empty, but the values ​​are present in the database. Insertion still works. "isJsonScalar" is ignored Image

Image <===Execute SQL 2025-01-21T07:03:46.787+02:00 INFO 7284 --- [nio-8080-exec-2] o.b.jimmer.sql.runtime.ExecutorForLog : Execute SQL===> Purpose: MUTATE SQL: insert into websites_templates(created_at, updated_at, NAME, DESCRIPTION, STATUS, id_website) values(? /* 2025-01-21 07:03:46.7720742 /, ? / 2025-01-21 07:03:46.7720742 /, ? / {"uk":"тест"} /, ? / {"uk":""} /, ? / 0 /, ? / 6 */) returning ID Affected row count: 1 JDBC response status: success Time cost: 6ms <===Execute SQL

But if you don't specify isJsonScalar and Seialized, there will be an error when inserting org.postgresql.util.PSQLException: ERROR: column "name" is of type jsonb and expression is character varying Hint: You will need to rewrite or quote the expression. Position: 110

Caused by: org.babyfish.jimmer.sql.exception.ExecutionException: Cannot execute the DDL statement at org.babyfish.jimmer.sql.ast.impl.mutation.Operator.lambda$executeAndGetRowCountsOneByOne$1(Operator.java:708) at org.babyfish.jimmer.sql.runtime.DefaultExecutor.execute(DefaultExecutor.java:60) at org.babyfish.jimmer.sql.runtime.ExecutorForLog.prettyLog(ExecutorForLog.java:125) at org.babyfish.jimmer.sql.runtime.ExecutorForLog.execute(ExecutorForLog.java:45) at org.babyfish.jimmer.sql.ast.impl.mutation.Operator.executeAndGetRowCountsOneByOne(Operator.java:676) at org.babyfish.jimmer.sql.ast.impl.mutation.Operator.executeAndGetRowCounts(Operator.java:609) at org.babyfish.jimmer.sql.ast.impl.mutation.Operator.execute(Operator.java:857) at org.babyfish.jimmer.sql.ast.impl.mutation.Operator.insert(Operator.java:190) at org.babyfish.jimmer.sql.ast.impl.mutation.Saver.saveSelf(Saver.java:207) at org.babyfish.jimmer.sql.ast.impl.mutation.Saver.saveAllImpl(Saver.java:106) at org.babyfish.jimmer.sql.ast.impl.mutation.Saver.lambda$save$0(Saver.java:48) at org.babyfish.jimmer.runtime.Internal.modifyDraft(Internal.java:173) at org.babyfish.jimmer.runtime.Internal.lambda$produce$1(Internal.java:42) at org.babyfish.jimmer.runtime.Internal.usingDraftContext(Internal.java:102) at org.babyfish.jimmer.runtime.Internal.produce(Internal.java:39) at org.babyfish.jimmer.sql.ast.impl.mutation.Saver.save(Saver.java:44) at org.babyfish.jimmer.sql.ast.impl.mutation.SimpleEntitySaveCommandImpl.executeImpl(SimpleEntitySaveCommandImpl.java:47) at org.babyfish.jimmer.spring.cfg.support.SpringConnectionManager.execute(SpringConnectionManager.java:31) at org.babyfish.jimmer.sql.ast.impl.mutation.SimpleEntitySaveCommandImpl.execute(SimpleEntitySaveCommandImpl.java:39) at org.babyfish.jimmer.sql.ast.impl.mutation.SimpleEntitySaveCommandImpl.execute(SimpleEntitySaveCommandImpl.java:18) at org.babyfish.jimmer.sql.ast.Executable.execute(Executable.java:12) at org.babyfish.jimmer.sql.Saver.insert(Saver.java:305) at org.babyfish.jimmer.sql.di.AbstractJSqlClientDelegate.insert(AbstractJSqlClientDelegate.java:225) at ....TemplateService.insert(TemplateService.java:105) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727) at .....TemplateService$$SpringCGLIB$$0.insert() ... 131 more Caused by: org.postgresql.util.PSQLException: ПОМИЛКА: стовпець "name" має тип jsonb, а вираз character varying Hint: Потрібно буде переписати або привести вираз. Position: 110 at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2733) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2420) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:517) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:434) at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:194) at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:155) at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) at org.babyfish.jimmer.sql.ast.impl.mutation.Operator.lambda$executeAndGetRowCountsOneByOne$1(Operator.java:696) ... 166 more

storympro avatar Jan 21 '25 05:01 storympro