当前位置: 首页 > 面试题库 >

储存清单 Hibernate在文本字段中将其作为JSON

徐经武
2023-03-14
问题内容

我在Hibernate中有类似以下模型的内容:

class Person {
    String name;
    List<Address> addresses;
}

class Address {
    String street;
    String city;
}

我现在想将Person持久化到一个表,该表中所有person的地址都被序列化为JSON字符串并存储在Person表的一列中。数据库中的“个人”记录如下所示:

name: 'Benjamin Franklin', addresses: '[{"street"="...","city"="..."}, {...}]'

有没有办法使用Hibernate来做到这一点?

如果地址不是列表,我可以注册一个UserType来执行序列化。

我也不能使用JPA的@Converter,因为Hibernate实现不会检测到更改,请参见HHH-10111。


问题答案:

您可以创建一个自定义类型:

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.DynamicParameterizedType;
import org.hibernate.usertype.UserType;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.*;

public class JsonListType implements UserType, DynamicParameterizedType {

    private static final int[] SQL_TYPES = new int[]{Types.LONGVARCHAR};
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private JavaType valueType = null;
    private Class<?> classType = null;

    @Override
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    @Override
    public Class<?> returnedClass() {
        return classType;
    }

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

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

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

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

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
        String value = rs.getString(names[0]);
        Object result = null;
        if (valueType == null) {
            throw new HibernateException("Value type not set.");
        }
        if (value != null && !value.equals("")) {
            try {
                result = OBJECT_MAPPER.readValue(value, valueType);
            } catch (IOException e) {
                throw new HibernateException("Exception deserializing value " + value, e);
            }
        }
        return result;
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        StringWriter sw = new StringWriter();
        if (value == null) {
            st.setNull(index, Types.VARCHAR);
        } else {
            try {
                OBJECT_MAPPER.writeValue(sw, value);
                st.setString(index, sw.toString());
            } catch (IOException e) {
                throw new HibernateException("Exception serializing value " + value, e);
            }
        }
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null) {
            return null;
        } else if (valueType.isCollectionLikeType()) {
            try {
                Object newValue = value.getClass().newInstance();
                Collection newValueCollection = (Collection) newValue;
                newValueCollection.addAll((Collection) value);
                return newValueCollection;
            } catch (InstantiationException e) {
                throw new HibernateException("Failed to deep copy the collection-like value object.", e);
            } catch (IllegalAccessException e) {
                throw new HibernateException("Failed to deep copy the collection-like value object.", e);
            }
        }

        return null;
    }

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

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

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

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }

    @Override
    public void setParameterValues(Properties parameters) {
        try {

            // Get entity class
            Class<?> entityClass = Class.forName(parameters.getProperty(DynamicParameterizedType.ENTITY));
            Field property = null;

            // Find the field
            while(property == null && entityClass != null){
                try {
                    property = entityClass.getDeclaredField(parameters.getProperty(DynamicParameterizedType.PROPERTY));
                } catch (NoSuchFieldException e) {
                    entityClass = entityClass.getSuperclass();
                }
            }

            if(property != null){
                ParameterizedType listType = (ParameterizedType) property.getGenericType();
                Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
                valueType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, listClass);
                classType = List.class;
            }

        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException(e);
        }
    }

}

并像这样使用它:

@Type(type = "com.company.util.JsonListType")
private List<MyCustomClass> myCustomClasses;

解决方案不是特定于数据库的,您可以轻松扩展它以支持Maps和自定义可克隆实体。



 类似资料:
  • 问题内容: 有什么方法可以读取文本文件并将内容存储在Jtable中?我有一个文本文件,其中包含有关某些过程的某些信息。就像一个具有列和各自值的表。是否可以获取.txt文件的内容并以Jtable的形式显示?我正在使用Eclipse和Window Builder。任何帮助将不胜感激。谢谢! 问题答案: 我将研究Oracle的教程: 读/写文本文件 JTable教程 当从文本文件中获取数据时,您需要将其

  • 我有两个类,有两个单独的表,“员工”和“公司”。我想在Company类中保留一个员工列表。这很容易,但我不知道如何在数据库端表示此列表。 “公司”类别: 类“员工”: 如果没有hibernate,我会选择创建另一个包含两列“employeeId”和“companyId”的表的设计,并尝试让所有员工的“employeeId”与“companyId”匹配。我不知道是否有可能在冬眠时做同样的事情。如果是

  • etc此文件1有许多字段,这些字段由“#”字符分隔,文件中有许多行。 文件2有用逗号分隔的字段,它有许多行。 我需要比较文件中的字段是否与文件2中的匹配。这两个文件中有许多字段是相同的,所以我需要编写代码来匹配这两个字段是否相同? 我是否应该将文件1中的所有字段存储到字符串[] 假设我想比较第4行,第9栏,字符串[3] 或者我应该将个人字段存储在字符串变量中。。 如何在java中比较两个文本文件中

  • 本文向大家介绍选择MongoDB文档,其中字段不存在,为null或为false?,包括了选择MongoDB文档,其中字段不存在,为null或为false?的使用技巧和注意事项,需要的朋友参考一下 您可以为此使用$in运算符。让我们首先用文档创建一个集合。使用文档创建集合的查询如下- 在method的帮助下显示集合中的所有文档。查询如下- 以下是输出和减号 这是查询以选择其中不存在字段,为null或

  • 问题内容: 我正在尝试通过C#将数据插入到我的Sql- Server数据库中。我正在调用存储过程,然后希望将其添加。我不确定要进行哪些更改,但是最终我希望它在存储过程中完成。 我的存储过程现在: 还有我的C#代码: 我使用的表创建://不能改变它,老板给我的。 问题答案: 理想情况下,您只需通过更改表定义将TagID用作身份字段。如果您不能这样做,那么下一个最佳选择是: 事务确保您不会以唯一的Ta

  • 问题内容: 我正在尝试优化我的Elasticsearch方案。 我有一个URL字段-我不想查询或过滤它,而只是检索它。 我的理解是,定义为的字段未建立索引,但仍存储在索引中。(请参阅http://www.slideshare.net/nitin_stephens/lucene- basics中的 幻灯片5 )这应该与Lucene UnIndexed相匹配,对吗? 这使我感到困惑,是否有一种方法可以