PostgreSQL Hibernate-types 与 JSONB 相关的数据转换错误

Muh*_*ziz 5 string postgresql hibernate spring-boot jsonb

由于 hibernate 本身不支持 JSONB,因此我实现了自定义类型。我可以毫无问题地从表 ad_my_table 读取数据。但是,在编写时我收到数据转换错误。我还尝试使用第三方解决方案,例如“hibernate-types”库,但遇到了相同的错误。

MyTable 实体类

import com.sample.console.backend.dao.entity.JsonbUserType;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.GenerationType;
import javax.persistence.SequenceGenerator;
import javax.persistence.Column;
import javax.persistence.Enumerated;

@Entity
@Table(name = "ad_my_table")
@TypeDef(name = "jsonb", typeClass = JsonbUserType.class)
public class CustomClientEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_table_id_seq")
    @SequenceGenerator(name = "my_table_id_seq", sequenceName = "ad_my_table_id_seq", allocationSize = 1)
    private Long id;

    @Column(name = "filename")
    private String fileName;

    @Column(name = "config", columnDefinition = "jsonb")
    @Type(type = "jsonb")
    private String config;

    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

    public String getFileName()
    {
        return fileName;
    }

    public void setFileName(String fileName)
    {
        this.fileName = fileName;
    }

    public String getConfig()
    {
        return config;
    }

    public void setConfig(String config)
    {
        this.config = config;
    }
}
Run Code Online (Sandbox Code Playgroud)

JsonbUserType.class 实现 UserType

import org.apache.commons.lang3.StringUtils;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGobject;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class JsonbUserType implements UserType {

   public static final String JSONB_TYPE = "jsonb";

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

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

   @Override
   public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {

       if (names.length <= 0) { return null; }

       final String dbData = rs.getString(names[0]);

       String result = null;

       if (StringUtils.isNotBlank(dbData)) {
           String json = dbData.startsWith("\"") ? dbData.substring(1, dbData.length() - 1) : dbData;
           result = StringEscapeUtils.unescapeJson(json);
       }

       return result;
   }

   @Override
   public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
           throws HibernateException, SQLException {

       if (value instanceof String && StringUtils.isNotBlank((String)value))
       {
           PGobject pgObject = new PGobject();
           pgObject.setType(JSONB_TYPE);
           pgObject.setValue((String)value);
           st.setObject(index, pgObject, Types.OTHER);
       }
       else {
           st.setNull(index, Types.OTHER);
       }
   }

   @Override
   public Object deepCopy(Object value) throws HibernateException {
       return value;
   }

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

   @Override
   public boolean equals(Object x, Object y) throws HibernateException {

       if(x == null) { return y == null; }

       return x.equals(y);
   }

   @Override
   public int hashCode(Object x) throws HibernateException {
       assert (x != null);
       return x.hashCode();
   }

   @Override
   public Object assemble(Serializable cached, Object owner) throws HibernateException {
       return this.deepCopy( cached);
   }

   @Override
   public Serializable disassemble(Object value) throws HibernateException {
       return (String)this.deepCopy( value);
   }

   @Override
   public Object replace(Object original, Object target, Object owner) throws HibernateException {
       return original;
   }
}
Run Code Online (Sandbox Code Playgroud)

CustomPostgreSQLDialect.class 扩展 PostgreSQL10Dialect

import com.sample.console.backend.dao.entity.JsonbUserType;

import org.hibernate.dialect.PostgreSQL10Dialect;

import java.sql.Types;

public class CustomPostgreSQLDialect extends PostgreSQL10Dialect
{
    public CustomPostgreSQLDialect() {
        super();
        registerColumnType(Types.JAVA_OBJECT, JsonbUserType.JSONB_TYPE);
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到的错误

Caused by: org.h2.jdbc.JdbcSQLDataException: Data conversion error converting "X'aced000...74227d' (AD_MY_TABLE: ""CONFIG"" ""JSONB"")"; SQL statement: insert into ad_my_table (config, filename, id) values (?, ?, ?) [22018-200]

Caused by: org.h2.message.DbException: Data conversion error converting "OTHER to JSON" [22018-200]
Run Code Online (Sandbox Code Playgroud)

小智 0

使用连接字符串将 Jsonb 类型创建为 clob jdbc:h2:mem:<table>;INIT=create domain if not exists jsonb as CLOB;、. 只要您将字段定义为实体中的字符串,这种方法就可以正常工作。如果您将 Entity 中的字段定义为 CustomJson 对象,那么它仅支持数据库读取操作。

这有效:

@Column(name = "config", columnDefinition = "jsonb")
    @Type(type = "jsonb")
    private String config;
Run Code Online (Sandbox Code Playgroud)

这不支持读取操作

@Column(name = "config", columnDefinition = "jsonb")
    @Type(type = "jsonb")
    private MyJsonObject config;
Run Code Online (Sandbox Code Playgroud)