[Feature Request] - Add information about AbstractScalarProvider for working with JSON to the documentation
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
there will be an error when inserting the field.
Stacktrace please
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
<===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(