从 Hibernate 6 原生查询读取 jsonb

问题描述 投票:0回答:2

以下代码在 postgresql 上的 Hibernate 6.1 和 6.4 之间的行为不同:

var rows =
   session
     .createNativeQuery("SELECT CAST('[1,2,3]' as jsonb) as data, 2 as id", Object[].class)
      .addScalar("data")
      .addScalar("id", StandardBasicTypes.INTEGER)
      .list();
rows.forEach(row -> System.out.println(row[0].getClass() + " -> " + row[0]));

6.1 的输出:

class java.util.ArrayList -> [1, 2, 3]

6.4 的输出:

class java.lang.String -> [1, 2, 3]

在6.1中,它将jsonb列转换为ArrayList;但在 6.4 上它只是将其读取为字符串。我尝试传递一堆不同的

StandardBasicTypes
,但没有一个能让 6.4 表现得像 6.1。 那么,我该如何在 6.4 中做到这一点?

这里是重现此问题的代码: https://github.com/chadselph/hibernate-json-native-query

java hibernate jsonb hibernate-native-query
2个回答
1
投票

使用用户类型!

var rows =
   session
     .createNativeQuery("SELECT CAST('[1,2,3]' as jsonb) as data, 2 as id", Object[].class)
      .addScalar("data", JsonbType.class)
      .addScalar("id", StandardBasicTypes.INTEGER)
      .list();
rows.forEach(row -> System.out.println(row[0].getClass() + " -> " + row[0]));

*** JsonbType 实现 UserType ***

public class JsonbType implements UserType {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public int getSqlType() {
        // Postgres specific type for jsonb
        return Types.OTHER;
    }

    @Override
    public Class returnedClass() {
        return ArrayList.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return Objects.equals(x, y);
    }

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

    @Override
    public Object nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException{
        try {
            final String json = rs.getString(position);
            return json == null ? null : objectMapper.readValue(json, ArrayList.class);
        } catch (IOException e) {
            throw new HibernateException("Error converting jsonb to ArrayList", e);
        }
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
            throws HibernateException, SQLException {
        try {
            if (value == null) {
                st.setNull(index, Types.OTHER);
            } else {
                st.setObject(index, objectMapper.writeValueAsString(value), Types.OTHER);
            }
        } catch (JsonProcessingException e) {
            throw new HibernateException("Error converting ArrayList to jsonb", e);
        }
    }

    @Override
    public Object deepCopy(Object value) {
        try {
            final String json = objectMapper.writeValueAsString(value);
            return objectMapper.readValue(json, ArrayList.class);
        } catch (IOException e) {
            throw new HibernateException("Error deep copying jsonb", e);
        }
    }

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

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

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

    @Override
    public Object replace(Object detached, Object managed, Object owner) {
        return this.deepCopy(detached);
    }

}

0
投票

我将 Object 更改为 JsonNode 并且它有效))

© www.soinside.com 2019 - 2024. All rights reserved.