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

如何使用Spring Data / JPA插入Postgres Array类型列中?

叶鸿振
2023-03-14
问题内容

说我有一个postgres表,如下所示:

CREATE TABLE sal_emp (
    name            text,
    pay_by_quarter  integer[],
    schedule        text[][]
);

我什至可以使用Spring
Data插入列pay_by_quarterschedule?如果可能的话,它看起来如何作为存储库和实体?我还没有找到任何解决此问题的文档或示例,可能是因为它与更常见的用例重叠,并以一对多关系插入到多个表中。说到这,我完全打算使用Postgresql
array数据类型,并且不使用任何关系表。


问题答案:

您需要创建自己的类型并实现UserType interface。根据下一个响应,我编写了一个泛型UserType,可以在所有数组中使用,并且可以使用,但是您必须使用
非原始数据类型 (Integer,Long,String等)。否则,请参见上述Boolean类型更新。

public class GenericArrayUserType<T extends Serializable> implements UserType {

    protected static final int[] SQL_TYPES = { Types.ARRAY };
    private  Class<T> typeParameterClass;

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

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

    @SuppressWarnings("unchecked")
    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (T) this.deepCopy(value);
    }

    @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 {
        return x.hashCode();
    }

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

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
            throws HibernateException, SQLException {
        if (resultSet.wasNull()) {
            return null;
        }
        if (resultSet.getArray(names[0]) == null) {
            return new Integer[0];
        }

        Array array = resultSet.getArray(names[0]);
        @SuppressWarnings("unchecked")
        T javaArray = (T) array.getArray();
        return javaArray;
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session)
            throws HibernateException, SQLException {
        Connection connection = statement.getConnection();
        if (value == null) {
            statement.setNull(index, SQL_TYPES[0]);
        } else {
            @SuppressWarnings("unchecked")
            T castObject = (T) value;
            Array array = connection.createArrayOf("integer", (Object[]) castObject);
            statement.setArray(index, array);
        }
    }

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

    @Override
    public Class<T> returnedClass() {
        return typeParameterClass;
    }

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


}

然后,数组属性将是具有相同维的相同类型的数据库:

  • integer[] -> Integer[]
  • text[][]-> String[][]

在这种特殊情况下,将GenericType类放在属性之上

@Type(type = "packageofclass.GenericArrayUserType")

那么您的实体将是:

@Entity
@Table(name="sal_emp")
public class SalEmp {

    @Id
    private String name;

    @Column(name="pay_by_quarter")
    @Type(type = "packageofclass.GenericArrayUserType")
    private Integer[] payByQuarter;

    @Column(name="schedule")
    @Type(type = "packageofclass.GenericArrayUserType")
    private String[][] schedule;

    //Getters, Setters, ToString, equals, and so on

}

如果您不想使用此泛型UserType,请Integer[]输入String[][]类型。您需要编写自己的类型,在您的情况下,将如下所示:

  • 整数[]

    public class IntArrayUserType implements UserType {
    

    protected static final int[] SQL_TYPES = { Types.ARRAY };

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

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

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

    @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 {
    return x.hashCode();
    }

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

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
    throws HibernateException, SQLException {
    if (resultSet.wasNull()) {
    return null;
    }
    if (resultSet.getArray(names[0]) == null) {
    return new Integer[0];
    }

    Array array = resultSet.getArray(names[0]);
    Integer[] javaArray = (Integer[]) array.getArray();
    return javaArray;
    

    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session)
    throws HibernateException, SQLException {
    Connection connection = statement.getConnection();
    if (value == null) {
    statement.setNull(index, SQL_TYPES[0]);
    } else {
    Integer[] castObject = (Integer[]) value;
    Array array = connection.createArrayOf(“integer”, castObject);
    statement.setArray(index, array);
    }
    }

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

    @Override
    public Class returnedClass() {
    return Integer[].class;
    }

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

  • 文本[][]

    public class StringMultidimensionalArrayType implements UserType {
    

    protected static final int[] SQL_TYPES = { Types.ARRAY };

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

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

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

    @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 {
    return x.hashCode();
    }

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

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
    throws HibernateException, SQLException {
    if (resultSet.wasNull()) {
    return null;
    }
    if (resultSet.getArray(names[0]) == null) {
    return new String[0][];
    }

    Array array = resultSet.getArray(names[0]);
    String[][] javaArray = (String[][]) array.getArray();
    return javaArray;
    

    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session)
    throws HibernateException, SQLException {
    Connection connection = statement.getConnection();
    if (value == null) {
    statement.setNull(index, SQL_TYPES[0]);
    } else {
    String[][] castObject = (String[][]) value;
    Array array = connection.createArrayOf(“integer”, castObject);
    statement.setArray(index, array);
    }
    }

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

    @Override
    public Class returnedClass() {
    return String[][].class;
    }

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

    }

在这种情况下,您的媒体资源具有不同的类型:

@Column(name="pay_by_quarter")
@Type(type = "packageofclass.IntArrayUserType")
private Integer[] payByQuarter;

@Column(name="schedule")
@Type(type = "packageofclass.StringMultidimensionalArrayType")
private String[][] schedule;

更新休眠用户类型

使用Boolean或boolean似乎无法使用GenericArrayUserType,因此可以在类型的CREATE DDL声明boolean中创建解决方案bytea

CREATE TABLE sal_emp (
    name text,
    pay_by_quarter  integer[],
    schedule        text[][],
    wow_boolean     bytea
    );

而您的财产没有任何类型:

private boolean[][][] wowBoolean;

它解析没有任何非常好TypeConverter。输出:wowBoolean=[[[true, false], [true, false]], [[true, true], [true, true]]])

更新@ConverterJPA 2.1

我已经尝试使用@ConverterJPA
2.1和EclipseLink和选项Hibernate。我只是这样尝试过integer[](不是text[][]Converter(*我已将该属性更改为a,List<Integer>但这并不重要):

@Converter
public class ConverterListInteger implements AttributeConverter<List<Integer>, Array>{

    @Override
    public Array convertToDatabaseColumn(List<Integer> attribute) {
        DataSource source = ApplicationContextHolder.getContext().getBean(DataSource.class);

        try {
            Connection conn = source.getConnection();
            Array array = conn.createArrayOf("integer", attribute.toArray());
            return  array;

        } catch (SQLException e) {
            e.printStackTrace();
        }

        return null;

    }

    @Override
    public List<Integer> convertToEntityAttribute(Array dbData) {
        List<Integer> list = new ArrayList<>();

        try {
            for(Object object : (Object[]) dbData.getArray()){
                list.add((Integer) object);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return list;

    }

}

然后,将转换器添加到实体的属性中:

@Convert(converter=ConverterListInteger.class)
private List<Integer> pay_by_quarter;

因此,基于的解决方案JPA specification不起作用。为什么?Hibernate不支持数据库数组(java.sql.Array)。

然后我尝试使用EclipseLink(请参阅此处的配置方法),它可以工作,但并不总是…似乎有一个错误,它第一次运行良好,但是下次无法更新或查询此行。然后,只要我成功添加新行,但是之后就无法更新或查询。

结论

目前,似乎没有JPA适当的供应商支持……只有解决方案Hibernate UserType可以正常使用,但仅适用于Hibernate



 类似资料:
  • 问题背景 之前做springboot项目在操作数据库方面一直在使用的是Mybatis,最近在查阅资料的时候接触到了SpringData JPA与SpringData JDBC,想问一下大佬们,这三个框架如何选型

  • 问题内容: 我有一堂课: 我想在“ JPA”专栏中使用它: 这就是Hibernate所说的: 如何使其了解我的自定义类型。我认为这很简单,但是在文档中找不到。 问题答案: 好吧,有很多方法: 用注释类,并具有: private Email email; 声明自定义值类型-参见此处(使用)

  • 我有一个postgres表,它正在使用

  • 我有一个mapper方法,定义如下 相应的SQLBuilder方法定义为 每当我调用我的mappermethod时,如下所示 我遇到了以下错误。 错误可能涉及ActivityMapper。insertIntoActivityComment Inline设置参数时出错\n####SQL:insert into activityComment(commenttype、commenttext、comme

  • 我在JPA上工作。我的要求是从表中获取列名和数据类型。我有查询来做这件事,但这些是原生查询的。如果我使用这些原生查询,它将支持任何数据库,如Oracle,MySql,....... 现在我正在使用MySql和JPA工作得很好。 下面的查询用于获取表列名 我使用JPA中的createNativeQuery()执行了上面的查询。它将支持所有的数据库。如果没有,我怎么能做到这一点。非常感谢。

  • 在任何情况下,我不想重复计数的每一个页面我需要,这个信息是需要的只是第一次调用。