我如何配置grails域类属性以存储为(postgres 9.4)jsonb?

问题描述 投票:5回答:3

我已经尝试像这样配置域类:

class Test {

    String data

    static constraints = {
    }

    static mapping = {
        data type: 'jsonb'
    }
}

这将引发异常(最终的原因是Invocation of init method failed; nested exception is org.hibernate.MappingException: Could not determine type for: jsonb, at table: test, for columns: [org.hibernate.mapping.Column(data)]。]

我也尝试过column: 'data', sqlType: 'jsonb',它创建了一个名为textdata列。

如何正确告诉grails使用jsonb作为sql列类型?有可能吗?

(PostgreSQL的jdbc驱动程序在9.4-1200.jdbc4和休眠4中使用。

hibernate grails gorm postgresql-9.4
3个回答
3
投票
要配置域以将jsonb类型映射到String,您可以:

  1. 声明自己的org.hibernate.usertype.UserType。添加到src/java

    public class JSONBType implements UserType { @Override public int[] sqlTypes() { return new int[] { Types.OTHER }; } @SuppressWarnings("rawtypes") @Override public Class returnedClass() { return String.class; } @Override public boolean equals(Object x, Object y) throws HibernateException { return (x != null) && x.equals(y); } @Override public int hashCode(Object x) throws HibernateException { return x.hashCode(); } @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor sessionImplementor, Object owner) throws HibernateException, SQLException { return rs.getString(names[0]); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor sessionImplementor) throws HibernateException, SQLException { st.setObject(index, value, (value == null) ? Types.NULL : Types.OTHER); } @Override public Object deepCopy(Object value) throws HibernateException { if (value == null) return null; return new String((String)value); } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable)value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return deepCopy(original); } }

  2. 之后,您可以简单地在域中声明映射:

    static mapping = { data type: "your.package.JSONBType", sqlType: "jsonb" }

也可以将jsonb映射到String,而不是直接映射到JSONObject或现有的类或接口。在那种情况下,GORM将负责序列化/反序列化json,而您不再需要在应用程序中显式地执行它。 Here is an example of such UserType implementation

1
投票
您可以使用UserType插件在您的域类中使用某些Postgresql本机类型。

目前,该插件支持Json,但不支持Jsonb类型。您可以在Grails Postgresql Extensions

中获得有关json支持的更多信息。

免责声明:我是该插件的开发人员之一。


0
投票
尽管,我回答得很晚,但是我设法通过一种非常简单的方式实现了这一点,并且运行起来非常顺畅-

我创建了一个实现plugin documentation的自定义Hibernate类型:

UserType

我正在使用package com.wizpanda.hibernate

import groovy.transform.CompileStatic
import org.grails.web.json.JSONObject
import org.hibernate.HibernateException
import org.hibernate.engine.spi.SessionImplementor
import org.hibernate.usertype.UserType

import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.sql.Types

/**
 * An implementation of {@link org.grails.web.json.JSONObject} column using Hibernate custom types.
 * https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#_custom_type
 * https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/usertype/UserType.html
 *
 * @author Shashank Agrawal
 */
@CompileStatic
class JSONObjectFooType implements UserType {

    @Override
    int[] sqlTypes() {
        return [Types.OTHER] as int[]
    }

    //@SuppressWarnings("rawtypes")
    @Override
    Class returnedClass() {
        return JSONObject.class
    }

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

    @Override
    int hashCode(Object x) throws HibernateException {
        return x.hashCode()
    }

    @Override
    Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
        String value = rs.getString(names[0])
        if (!value) {
            return null
        }

        return new JSONObject(value)
    }

    @Override
    void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
        String valueToPersist

        if (value) {
            if (value instanceof JSONObject) {
                valueToPersist = value.toString()
            } else if (value instanceof String) {
                valueToPersist = new JSONObject(value).toString(0)
            } else {
                throw new HibernateException("Unknown type received for JSONObject based column")
            }
        }

        st.setObject(index, valueToPersist, Types.OTHER)
    }

    @Override
    Object deepCopy(Object value) throws HibernateException {
        if (!value) {
            return null
        }
        if (value instanceof JSONObject) {
            return new JSONObject(value.toString(0))
        }

        return value
    }

    @Override
    boolean isMutable() {
        return false
    }

    @Override
    Serializable disassemble(Object value) throws HibernateException {
        if (value instanceof JSONObject) {
            return value?.toString(0)
        }

        return value?.toString()
    }

    @Override
    Object assemble(Serializable cached, Object owner) throws HibernateException {
        if (!cached) {
            return null
        }

        return new JSONObject(cached.toString())
    }

    @Override
    Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original)
    }
}
,因为它是Grails内部的,您可以使用org.grails.web.json.JSONObject或Groovy json之类的东西,并替换上面出现的内容。

现在,在您的域类中简单使用它-

org.json.JSONObject

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