Java 如何使用jdbc准备的语句将SqlDescriptor写入tsrange列?

Java 如何使用jdbc准备的语句将SqlDescriptor写入tsrange列?,java,postgresql,hibernate,jdbc,Java,Postgresql,Hibernate,Jdbc,我按照优秀的指南将特殊的sql类型tsrange映射到hibernate。我决定使用Java和SQL描述符,而不是用户类型,因为JDBCSQL处理应该更好 当我试图持久化一个列名为time range且类型为tsrange的实体时,我总是得到:错误[org.hibernate.engine.jdbc.spi.SqlExceptionHelper](默认任务2)错误:列“time_range”的类型为tsrange,但表达式的类型为character variable Hinweis:您需要重写或

我按照优秀的指南将特殊的sql类型
tsrange
映射到hibernate。我决定使用Java和SQL描述符,而不是用户类型,因为JDBCSQL处理应该更好

当我试图持久化一个列名为time range且类型为tsrange的实体时,我总是得到:
错误[org.hibernate.engine.jdbc.spi.SqlExceptionHelper](默认任务2)错误:列“time_range”的类型为tsrange,但表达式的类型为character variable
Hinweis:您需要重写或强制转换表达式。

我的理解是,我需要用setObject方法和type.OTHER或type.JAVA_对象编写一个特殊/非标准的sql类型。将范围sql类型放入PreparedStatement的首选方式是什么

BasicBinder源代码,我在其中填写jdbc准备好的语句,sqlString包含
“[2019-01-14 13:06:262019-01-14 13:12:39]”


我找到了解决办法。
tsrange
可以作为字符串写入
“[2019-01-14 13:06:262019-01-14 13:12:39]”
PreparedStatement st
中,格式为
st.setObject(index,sqlString,Type.OTHER)

以下是正在工作的
TsRangeSqlTypeDescriptor
类:

package com.example.galea.model.types;

import java.lang.reflect.InvocationTargetException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;

public class TsRangeSqlTypeDescriptor implements SqlTypeDescriptor {

    /**  */
    private static final long serialVersionUID = -4377165492827156136L;

    private static final Log log = LogFactory.getLog(TsRangeSqlTypeDescriptor.class);

    public static final TsRangeSqlTypeDescriptor INSTANCE = new TsRangeSqlTypeDescriptor();

    @Override
    public int getSqlType() {
        return Types.OTHER; // <--- This is importand!
    }

    @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 {
                String sqlString = javaTypeDescriptor.toString(value);

                // Here is the solution with Type.OTHER
                st.setObject(index, sqlString, getSqlType());
            }

            @Override
            protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
                st.setObject(name, javaTypeDescriptor.toString(value));
            }
        };
    }

    @Override
    public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<X>(javaTypeDescriptor, this) {

            @Override
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                if(javaTypeDescriptor instanceof TsRangeJavaTypeDescriptor) {
                    TsRangeJavaTypeDescriptor rangeJavaTypeDescriptor = (TsRangeJavaTypeDescriptor) javaTypeDescriptor;

                    Object pgObject = rs.getObject(name);
                    Object valueRaw;
                    // Ugly, but I can not cast PGobject
                    try {
                        valueRaw = pgObject.getClass().getMethod(getValue, null).invoke(pgObject);
                        if(valueRaw instanceof String) {
                            String value = (String) valueRaw;
                            return (X) rangeJavaTypeDescriptor.wrap(value, options);
                        }
                    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
                        log.error(Failed to parse pgObject,e);
                    }
                }
                return javaTypeDescriptor.wrap(rs.getObject(name), options);
            }

            @Override
            protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap(statement.getObject(index), options);
            }

            @Override
            protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap(statement.getObject(name), options);
            }
        };
    }

}
package com.example.galea.model.types;
导入java.lang.reflect.InvocationTargetException;
导入java.sql.CallableStatement;
导入java.sql.PreparedStatement;
导入java.sql.ResultSet;
导入java.sql.SQLException;
导入java.sql.Types;
导入org.apache.commons.logging.Log;
导入org.apache.commons.logging.LogFactory;
导入org.hibernate.type.descriptor.ValueBinder;
导入org.hibernate.type.descriptor.ValueExtractor;
导入org.hibernate.type.descriptor.WrapperOptions;
导入org.hibernate.type.descriptor.java.JavaTypeDescriptor;
导入org.hibernate.type.descriptor.sql.BasicBinder;
导入org.hibernate.type.descriptor.sql.BasicExtractor;
导入org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
公共类TsRangeSqlTypeDescriptor实现SqlTypeDescriptor{
/**  */
私有静态最终长serialVersionUID=-4377165492827156136L;
私有静态最终日志日志=LogFactory.getLog(TsRangeSqlTypeDescriptor.class);
公共静态最终TsRangeSqlTypeDescriptor实例=新的TsRangeSqlTypeDescriptor();
@凌驾
public int getSqlType(){
返回类型。其他;//
Hibernate类型项目提供了一种支持PostgreSQL
TSRANGE
列类型的方法

您需要做的第一件事是使用以下Maven依赖项:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version>
</dependency>

Vladmichalcea网站
hibernate-types-52
${hibernate types.version}
之后,您可以像这样映射PostgreSQL范围:

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    typeClass = PostgreSQLRangeType.class,
    defaultForType = Range.class
)
public class Book {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @NaturalId
    private String isbn;
 
    private String title;
 
    @Column(
        name = "price_cent_range",
        columnDefinition = "numrange"
    )
    private Range<BigDecimal> priceRange;
 
    @Column(
        name = "discount_date_range",
        columnDefinition = "daterange"
    )
    private Range<LocalDate> discountDateRange;
 
    //Getters and setters omitted for brevity
}
@实体(name=“Book”)
@表(name=“book”)
@类型定义(
typeClass=PostgreSQLRangeType.class,
defaultForType=Range.class
)
公共课堂用书{
@身份证
@生成值
私人长id;
@归化
专用字符串isbn;
私有字符串标题;
@纵队(
name=“价格/分/范围”,
columnDefinition=“numrange”
)
私人范围价格范围;
@纵队(
name=“折扣日期范围”,
columnDefinition=“日期范围”
)
私人范围折扣日期范围;
//为简洁起见省略了getter和setter
}
Range
PostgreSQLRangeType
类来自Hibernate类型项目

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version>
</dependency>
@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    typeClass = PostgreSQLRangeType.class,
    defaultForType = Range.class
)
public class Book {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @NaturalId
    private String isbn;
 
    private String title;
 
    @Column(
        name = "price_cent_range",
        columnDefinition = "numrange"
    )
    private Range<BigDecimal> priceRange;
 
    @Column(
        name = "discount_date_range",
        columnDefinition = "daterange"
    )
    private Range<LocalDate> discountDateRange;
 
    //Getters and setters omitted for brevity
}