Hibernate 如何将grails域类属性配置为存储为(postgres 9.4)jsonb?
我尝试过如下配置域类:Hibernate 如何将grails域类属性配置为存储为(postgres 9.4)jsonb?,hibernate,grails,gorm,postgresql-9.4,Hibernate,Grails,Gorm,Postgresql 9.4,我尝试过如下配置域类: class Test { String data static constraints = { } static mapping = { data type: 'jsonb' } } 这会引发异常(最终原因是调用init方法失败;嵌套异常为org.hibernate.MappingException:无法确定以下列的类型:[org.hibernate.mapping.Column(data)]) 我还尝试了c
class Test {
String data
static constraints = {
}
static mapping = {
data type: 'jsonb'
}
}
这会引发异常(最终原因是调用init方法失败;嵌套异常为org.hibernate.MappingException:无法确定以下列的类型:[org.hibernate.mapping.Column(data)])
我还尝试了column:'data',sqlType:'jsonb'
,它创建了一个名为data
的text
列
如何正确地告诉grails使用jsonb
作为sql列类型?有可能吗
(带有hibernate 4的9.4-1200.jdbc4版本中使用了postgresql jdbc驱动程序。)您可以使用该插件在域类中使用一些postgresql本机类型
目前,插件支持Json,但不支持Jsonb类型。您可以在中获得有关json支持的更多信息
免责声明:我是该插件的开发人员之一。要配置域以将
jsonb
类型映射到String
,您可以:
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);
}
}
static mapping = {
data type: "your.package.JSONBType", sqlType: "jsonb"
}
您还可以将
jsonb
映射到现有的类或接口,而不是String
,而是直接映射到JSONObject
。在这种情况下,GORM将负责序列化/反序列化json,您不再需要在应用程序中显式地执行此操作 虽然,我很晚才回答这个问题,但我还是用一种非常简单的方法实现了这一点,而且工作非常顺利-
我创建了一个自定义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)
}
}
我使用的是org.grails.web.json.JSONObject
,因为它是grails内部的,所以您可以使用其他类似org.json.JSONObject
或Groovy json来替换上面的引用
现在,在您的域类中简单地使用它-
class User {
String email
String name
JSONObject settings
static mapping = {
settings type: JSONObjectFooType, sqlType: "text"
}
}
纳马斯特 游戏有点晚了,但对于后代来说,这里是@jasp提供的解决方案的我的版本。不同之处在于,此解决方案将
Map
对象持久化为JSON格式的文本列。它使用了包括Jackson libs在内的grails
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Map;
public class JSONStringType implements UserType {
private static final ObjectMapper _mapper = new ObjectMapper();
@Override
public int[] sqlTypes() {
return new int[] { Types.VARCHAR };
}
@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, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
try {
String val = rs.getString(names[0]);
return _mapper.readValue(val, Map.class);
} catch (IOException e) {
throw new HibernateException(e);
}
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
try {
String val;
if (value == null)
val = "{}";
else if (value instanceof String)
val = (String)value;
else
val = _mapper.writeValueAsString(value);
st.setObject(index, val, (value == null) ? Types.NULL : Types.VARCHAR);
} catch (JsonProcessingException e) {
throw new HibernateException(e);
}
}
@Override
public Object deepCopy(Object value) throws HibernateException {
if (value == null) return null;
try {
String val = _mapper.writeValueAsString(value);
return val;
} catch (JsonProcessingException e) {
throw new HibernateException(e);
}
}
@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);
}
}
用法:
import your.package.JSONStringType
class Book {
String name
String isbn
Map attributes = [:]
static constraints = {
}
static mapping = {
attributes type: JSONStringType, sqlType: 'nvarchar(4000)'
}
}
更改
sqlType
以匹配数据库列类型。对于SQL Server,nvarchar(4000)
用于高效的JSON文档查询,或者nvarchar(MAX)
用于大型JSON文档存储。由于不支持jsonb,严格来说,这并没有回答我的问题(无意冒犯)。我知道postgresql扩展;但grails根本不需要对该字段中的数据做任何事情,它可以将其内容视为文本。只是数据库应该知道它是jsonb。我们仍然非常感谢您的输入。如果您添加了一个插件未涵盖但在postgres sql中实现的功能列表,那就太好了。例如:查询json列的子元素。