Java和PostgreSQL数据库SQL数组映射问题

Hibernate自定义类型允许映射各种数据库特定的列类型,比如IP地址、JSON列、位集或SQL数组。定义定制Hibernate类型有两种方法: UserType接口和 Java and SQL descriptors。 Java and SQL descriptors是首选的,因为它允许更好地分离 Java-to-JDBC 和 JDBC-to-SQL 类型处理。在本文中,我们将了解如何将PostgreSQL数据库中的数组映射到它们的Java对应对象中。

SQL映射到JPA

首先我们在postgreSQL数据库中创建如下含有Array列的test表:

    create table array_test (
        id int8 not null, 
        version int4, 
        sensor_names text[], 
        sensor_values integer[], 
        primary key (id)
    )

我们希望使用JPA和Hibernate映射这个表。但是,JPA和Hibernate默认都不支持SQL数组,我们希望将这些数组分别映射到字符串和int Java数组。

这个数据库表的JPA实体映射如下(string-array和int-array是自定义类型,实现SQL数组与java数组转换):

    @Entity(name = "ArrayTestEntity")
    @Table(name = "array_test")
    @TypeDefs({
        @TypeDef(name = "string-array", typeClass = StringArrayType.class),
        @TypeDef(name = "int-array", typeClass = IntArrayType.class)
    })
    public class ArrayTestEntity {
    
        @Id   
        private Long id;
     
        @Version
        private int version;
     
        @Type( type = "string-array" )
        @Column(name = "sensor_names",columnDefinition = "text[]")
        private String[] sensorNames;
     
        @Type( type = "int-array" )
        @Column(name = "sensor_values", columnDefinition = "int[]")
        private int[] sensorValues;
     
        //Getters and setters omitted for brevity
    }

接下来完成自定义StringArrayType类与IntArrayType类,需要继承AbstractSingleColumnStandardBasicType类并完成定义 Java and SQL descriptors:

    public class StringArrayType
            extends AbstractSingleColumnStandardBasicType<String[]>
            implements DynamicParameterizedType {
     
        public StringArrayType() {
            super( 
                ArraySqlTypeDescriptor.INSTANCE, 
                StringArrayTypeDescriptor.INSTANCE 
            );
        }
     
        public String getName() {
            return "string-array";
        }
     
        @Override
        protected boolean registerUnderJavaType() {
            return true;
        }
     
        @Override
        public void setParameterValues(Properties parameters) {
            ((StringArrayTypeDescriptor) 
                getJavaTypeDescriptor())
                .setParameterValues(parameters);
        }
    }
    public class IntArrayType
            extends AbstractSingleColumnStandardBasicType<int[]>
            implements DynamicParameterizedType {
     
        public IntArrayType() {
            super( 
                ArraySqlTypeDescriptor.INSTANCE, 
                IntArrayTypeDescriptor.INSTANCE 
            );
        }
     
        public String getName() {
            return "int-array";
        }
     
        @Override
        protected boolean registerUnderJavaType() {
            return true;
        }
     
        @Override
        public void setParameterValues(Properties parameters) {
            ((IntArrayTypeDescriptor) 
                getJavaTypeDescriptor())
                .setParameterValues(parameters);
        }
    }

接下来还有最后一步就是descriptor的定义,从前面两个自定义数组类提到的Hibernate类型中可以看出,两个数组类共享用于处理jdbc到sql类型映射的相同SqlTypeDescriptor,那么可以写一个公用的实现SqlTypeDescriptor类如下所示:

    public class ArraySqlTypeDescriptor 
        implements SqlTypeDescriptor {
     
        public static final ArraySqlTypeDescriptor INSTANCE = 
            new ArraySqlTypeDescriptor();
     
        @Override
        public int getSqlType() {
            return Types.ARRAY;
        }
     
        @Override
        public boolean canBeRemapped() {
            return true;
        }
     
        @Override
        public <X> ValueBinder<X> getBinder(
            JavaTypeDescriptor<X> javaTypeDescriptor) {
            return new BasicBinder<X>( javaTypeDescriptor, this) {
                @Override
                protected void doBind(
                        PreparedStatement st, 
                        X value, 
                        int index, 
                        WrapperOptions options
                    ) throws SQLException {
     
                    AbstractArrayTypeDescriptor<Object> abstractArrayTypeDescriptor = 
                        (AbstractArrayTypeDescriptor<Object>) 
                            javaTypeDescriptor;
     
                    st.setArray( 
                        index, 
                        st.getConnection().createArrayOf(
                            abstractArrayTypeDescriptor.getSqlArrayType(),
                            abstractArrayTypeDescriptor.unwrap( 
                                value, 
                                Object[].class, 
                                options 
                            )
                        )
                    );
                }
     
                @Override
                protected void doBind(
                        CallableStatement st, 
                        X value, 
                        String name, 
                        WrapperOptions options
                    ) throws SQLException {
                    throw new UnsupportedOperationException( 
                        "Binding by name is not supported!"
                    );
                }
            };
        }
     
        @Override
        public <X> ValueExtractor<X> getExtractor(
            final JavaTypeDescriptor<X> javaTypeDescriptor) {
            return new BasicExtractor<X>(javaTypeDescriptor, this) {
                @Override
                protected X doExtract(
                        ResultSet rs, 
                        String name, 
                        WrapperOptions options
                    ) throws SQLException {
                    return javaTypeDescriptor.wrap(
                        rs.getArray(name), 
                        options
                    );
                }
     
                @Override
                protected X doExtract(
                        CallableStatement statement, 
                        int index, 
                        WrapperOptions options
                    ) throws SQLException {
                    return javaTypeDescriptor.wrap(
                        statement.getArray(index), 
                        options
                    );
                }
     
                @Override
                protected X doExtract(
                        CallableStatement statement, 
                        String name, 
                        WrapperOptions options
                    ) throws SQLException {
                    return javaTypeDescriptor.wrap(
                        statement.getArray(name), 
                        options
                    );
                }
            };
        }
    }

基本上,ArraySqlTypeDescriptor定义了Hibernate如何使用JDBC语句处理Statement.setArray 和ResultSet.

StringArrayTypeDescriptor

StringArrayTypeDescriptor很简单,因为大部分逻辑都封装在AbstractArrayTypeDescriptor中。StringArrayTypeDescriptor定义了期望的Java类型(如String[])和postgreSQL底层数据库数组类型(如text[])。

    public class StringArrayTypeDescriptor
            extends AbstractArrayTypeDescriptor<String[]> {
     
        public static final StringArrayTypeDescriptor INSTANCE = 
            new StringArrayTypeDescriptor();
     
        public StringArrayTypeDescriptor() {
            super( String[].class );
        }
     
        @Override
        protected String getSqlArrayType() {
            return "text";
        }
    }

IntArrayTypeDescriptor

IntArrayTypeDescriptor也是一样,因为它定义了预期的Java类型(例如int[])和底层数据库数组类型(例如int[])。

    public class IntArrayTypeDescriptor
            extends AbstractArrayTypeDescriptor<int[]> {
     
        public static final IntArrayTypeDescriptor INSTANCE = 
            new IntArrayTypeDescriptor();
     
        public IntArrayTypeDescriptor() {
            super( int[].class );
        }
     
        @Override
        protected String getSqlArrayType() {
            return "integer";
        }
    }

这样就可以实现Java数组与postgreSQL数据库数组列之间的映射。同理也可以完成其他数组的映射。

你可能感兴趣的:(JAVA,postgreSQL)