[英]How to write in a SqlDescriptor with a jdbc prepared statement into a tsrange column?
I followed the excellent guide How to map Java and SQL arrays with JPA and Hibernate to map the special sql type tsrange
to hibernate.我按照优秀指南如何使用 JPA 和 Hibernate 映射 Java 和 SQL 数组将特殊的 sql 类型tsrange
到 hibernate。 I decided to use Java and SQL descriptors and not a user type, because the jdbc sql handling should be better.我决定使用 Java 和 SQL 描述符而不是用户类型,因为 jdbc sql 处理应该更好。
When I try to persist an entity with a column named time range and the type tsrange, I got always: ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (default task-2) ERROR: column "time_range" is of type tsrange but expression is of type character varying Hinweis: You will need to rewrite or cast the expression.
当我尝试使用名为时间范围和类型 tsrange 的列持久化实体时,我总是得到: ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (default task-2) ERROR: column "time_range" is of type tsrange but expression is of type character varying Hinweis: You will need to rewrite or cast the expression.
My understanding is, that I need to write a special/non standard sql type with the setObject method and the type Type.OTHER or Type.JAVA_OBJECT.我的理解是,我需要使用 setObject 方法和类型 Type.OTHER 或 Type.JAVA_OBJECT 编写一个特殊/非标准的 sql 类型。 What is the prefered way, to put a range sql type into a PreparedStatement?将范围 sql 类型放入 PreparedStatement 的首选方法是什么?
The BasicBinder source, where I fill the jdbc prepared statement, sqlString contains "[2019-01-14 13:06:26, 2019-01-14 13:12:39]"
: BasicBinder源码,我在其中填写jdbc准备好的语句,sqlString包含"[2019-01-14 13:06:26, 2019-01-14 13:12:39]"
:
@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);
// I tried the following statements:
//st.setObject(index, sqlString, getSqlType());
//st.setObject(index, sqlString);
//st.setString(index, sqlString+"::tsrange");
st.setString(index, sqlString);
}
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
st.setObject(name, javaTypeDescriptor.toString(value));
}
};
}
Here are the column definition of the entity:以下是实体的列定义:
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Basic
@Column(nullable = false, name = "time_range", columnDefinition = "tsrange")
@Type(type="com.example.model.types.TsRange")
private PgTsRange timeRange;
The Hibernate Types project offers a
PostgreSQLRangeType
supports the PostgreSQLTSRANGE
column type. Hibernate Types 项目提供了一个PostgreSQLRangeType
支持 PostgreSQLTSRANGE
列类型。
The first thing you need to do is to use the following Maven dependency:您需要做的第一件事是使用以下 Maven 依赖项:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Afterward, you can map your PostgreSQL ranges like this:之后,您可以像这样映射 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
}
The Range
and PostgreSQLRangeType
classes are from the Hibernate Types project. Range
和PostgreSQLRangeType
类来自 Hibernate Types 项目。
I found a solution.我找到了解决方案。 The tsrange
can be written as string of the form "[2019-01-14 13:06:26, 2019-01-14 13:12:39]"
into the PreparedStatement st
with st.setObject(index, sqlString, Type.OTHER);
的tsrange
可以写成以下形式的字符串"[2019-01-14 13:06:26, 2019-01-14 13:12:39]"
进入PreparedStatement st
与st.setObject(index, sqlString, Type.OTHER);
Here is the working TsRangeSqlTypeDescriptor
class:这是有效的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);
}
};
}
}
One ugly part is the first doExtract
method.一个丑陋的部分是第一个doExtract
方法。 This is not in the scope of my question, but it was impossible for me to cast rs.getObject(name)
to PGobject.这不在我的问题范围内,但我不可能将rs.getObject(name)
为 PGobject。 I putted also org.postgresql:postgresql:jar:42.2.5
to my dependencies but I got strange classloading warnings and casting was impossible.我也将org.postgresql:postgresql:jar:42.2.5
了我的依赖项中,但是我收到了奇怪的类加载警告并且无法进行转换。 But the dirty hack with reflection works.但是带有反射的肮脏黑客有效。 I use wildfy 14.我使用野化14。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.