美文网首页
Java和PostgreSQL数据库SQL数组映射问题

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

作者: stray_dog | 来源:发表于2018-09-18 22:56 被阅读0次

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


SQL映射到JPA 

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

~~~sql

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数组转换):

~~~java

@Entity(name = "ArrayTestEntity")

@Table(name = "array_test")

@TypeDefs({

   @TypeDef(name = "string-array", typeClass = StringArrayType.class),

   @TypeDef(name = "int-array", typeClass = IntArrayType.class)

})

~~~

~~~sql

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:

~~~java

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类如下所示:

~~~java

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[])。

~~~java

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[])。

~~~java

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数据库SQL数组映射问题

      本文链接:https://www.haomeiwen.com/subject/ksjpnftx.html