java.sql.DataTruncation:数据截断异常与LIKE运算符关系
java.sql.DataTruncation: Data truncation exception and LIKE operator relation
我正在做一个 spring boot rest 项目。我有一个在多个字段中搜索文本的网络服务。我正在使用 java 规范来生成查询。
结果查询是这样的
select *
from V_AGENCY agencyview0_
where
agencyview0_.nationcode like '%ABCDEFGHIKLMN%
or agencyview0_.username like '%ABCDEFGHIKLMN%'
问题是我收到 java.sql.DataTruncation: 数据截断 异常,因为 nationcode 字段的长度有效 10 在数据库上。为什么我得到这个例外?我不是要向该字段插入值。
org.firebirdsql.jdbc.field.FBWorkaroundStringField class 抛出该错误。
public void setString(String value) throws SQLException {
byte[] data = this.setStringForced(value);
if (value != null) {
assert data != null : "Expected non-null data here";
if (data.length > this.fieldDescriptor.getLength() && !this.isSystemTable(this.fieldDescriptor.getOriginalTableName()) && (value.length() > this.fieldDescriptor.getLength() + 2 || value.charAt(0) != '%' && value.charAt(value.length() - 1) != '%')) {
throw new DataTruncation(this.fieldDescriptor.getPosition() + 1, true, false, data.length, this.fieldDescriptor.getLength());
}
}
}
更新 - Spring 添加了引导代码
控制器:
@GetMapping(value = {PATH_SEARCH, PATH_LIST, PATH_VIEW + "/" + PATH_SEARCH, PATH_VIEW + "/" + PATH_LIST}, params = {PARAM_TEXT, PARAM_FIELD})
public List<T> searchInMultipleFields(
@RequestParam(name = PARAM_START, required = false) String start,
@RequestParam(name = PARAM_LIMIT, required = false) String limit,
@RequestParam(name = PARAM_TEXT) String text,
@RequestParam(name = PARAM_FIELD) List<String> fields
) {
OoSpecificationsBuilder<T> builder = new MultipleSearchSpecificationBuilder<>();
for (String field : fields) {
builder.with(field, ":", text.toUpperCase());
}
Specification<T> spec = builder.build();
return mService.getAll(getValueOf(start), getValueOf(limit, MAX_PAGE_SIZE), spec);
}
服务:
@Override
public List<T> getAll(int aStart, int aSize, Specification<T> aSpec) {
return getRepository().findAll((Specification) aSpec, generatePageRequest(aStart, aSize)).getContent();
}
JpaSpecificationExecutor:
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
规格:
public class MultipleSearchSpecification<T extends BaseModel> implements Specification<T> {
private SearchCriteria criteria;
public MultipleSearchSpecification(SearchCriteria aCriteria) {
criteria = aCriteria;
}
@Override
public Predicate toPredicate
(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
if (root.get(criteria.getKey()).getJavaType() == String.class) {
return builder.like(
root.get(criteria.getKey()), "%" + criteria.getValue() + "%");
}
return null;
}}
你知道这个问题的解决方法吗?
您使用的代码不是生成该文字查询,而是使用参数(即 agencyview0_.nationcod like ?
)并将参数值设置为 "%ABCDEFGHIKLMN%"
。
不幸的是,Firebird 将参数的最大长度限制为与其进行比较的字段的声明长度,另请参阅 JDBC-477,并且 Jaybird 无法自动覆盖它。您要么需要超大列定义,要么找到一种方法将参数显式转换为更宽,即确保代码生成类似 agencyview0_.nationcod like cast(? as varchar(100))
.
的内容
将服务注册添加到路径
src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory
与数据:
systems.config.orm.CustomDataTypesRegistration
public class CustomDataTypesRegistration implements SessionFactoryBuilderFactory {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomDataTypesRegistration.class);
@Override
public SessionFactoryBuilder getSessionFactoryBuilder(final MetadataImplementor metadata, final SessionFactoryBuilderImplementor defaultBuilder) {
logger.info("Registering custom Hibernate data types");
//Deprecated. (since 5.3) No replacement, access to and handling of Types will be much different in 6.0
metadata.getTypeResolver().registerTypeOverride(new OoStringType());
return defaultBuilder;
}}
自定义字符串类型:
public class OoStringType extends StringType {
public OoStringType() {
setSqlTypeDescriptor(OoVarcharTypeDescriptor.INSTANCE);
}}
自定义 VarcharTypeDescriptor:
public class OoVarcharTypeDescriptor extends VarcharTypeDescriptor {
protected static final Logger log = LoggerFactory.getLogger(OoVarcharTypeDescriptor.class.getName());
public static final OoVarcharTypeDescriptor INSTANCE = new OoVarcharTypeDescriptor();
public static final String TRUNCATED_PARAMETER = "truncated parameter [%s] as [%s] - [%s]";
public static final char PERCENTAGE_CHAR = '%';
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
FBParameterMetaData parameterMetaData = (FBParameterMetaData) st.getParameterMetaData();
int precision = parameterMetaData.getPrecision(index);
String unwrappedValue = javaTypeDescriptor.unwrap(value, String.class, options);
if (unwrappedValue.charAt(0) == PERCENTAGE_CHAR && unwrappedValue.charAt(unwrappedValue.length() - 1) == PERCENTAGE_CHAR) {
String coreValue = unwrappedValue.substring(1, unwrappedValue.length() - 1);
if (coreValue.length() > precision) {
unwrappedValue = PERCENTAGE_CHAR + coreValue.substring(0, precision - 2) + PERCENTAGE_CHAR;
log.info(String.format(TRUNCATED_PARAMETER, index, JdbcTypeNameMapper.getTypeName(this.getSqlDescriptor().getSqlType()), unwrappedValue));
}
} else if (unwrappedValue.length() > precision) {
unwrappedValue = unwrappedValue.substring(0, precision);
log.info(String.format(TRUNCATED_PARAMETER, index, JdbcTypeNameMapper.getTypeName(this.getSqlDescriptor().getSqlType()), unwrappedValue));
}
st.setString(index, unwrappedValue);
}
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
st.setString(name, (String) javaTypeDescriptor.unwrap(value, String.class, options));
}
};
}}
请注意,此解决方案可能会导致不需要的结果。例如:
您正在搜索 "ABCD",但您有一个长度为 3 的字段。它将 return 一个包含该字段的结果 "ABC"。
我正在做一个 spring boot rest 项目。我有一个在多个字段中搜索文本的网络服务。我正在使用 java 规范来生成查询。
结果查询是这样的
select *
from V_AGENCY agencyview0_
where
agencyview0_.nationcode like '%ABCDEFGHIKLMN%
or agencyview0_.username like '%ABCDEFGHIKLMN%'
问题是我收到 java.sql.DataTruncation: 数据截断 异常,因为 nationcode 字段的长度有效 10 在数据库上。为什么我得到这个例外?我不是要向该字段插入值。
org.firebirdsql.jdbc.field.FBWorkaroundStringField class 抛出该错误。
public void setString(String value) throws SQLException {
byte[] data = this.setStringForced(value);
if (value != null) {
assert data != null : "Expected non-null data here";
if (data.length > this.fieldDescriptor.getLength() && !this.isSystemTable(this.fieldDescriptor.getOriginalTableName()) && (value.length() > this.fieldDescriptor.getLength() + 2 || value.charAt(0) != '%' && value.charAt(value.length() - 1) != '%')) {
throw new DataTruncation(this.fieldDescriptor.getPosition() + 1, true, false, data.length, this.fieldDescriptor.getLength());
}
}
}
更新 - Spring 添加了引导代码
控制器:
@GetMapping(value = {PATH_SEARCH, PATH_LIST, PATH_VIEW + "/" + PATH_SEARCH, PATH_VIEW + "/" + PATH_LIST}, params = {PARAM_TEXT, PARAM_FIELD})
public List<T> searchInMultipleFields(
@RequestParam(name = PARAM_START, required = false) String start,
@RequestParam(name = PARAM_LIMIT, required = false) String limit,
@RequestParam(name = PARAM_TEXT) String text,
@RequestParam(name = PARAM_FIELD) List<String> fields
) {
OoSpecificationsBuilder<T> builder = new MultipleSearchSpecificationBuilder<>();
for (String field : fields) {
builder.with(field, ":", text.toUpperCase());
}
Specification<T> spec = builder.build();
return mService.getAll(getValueOf(start), getValueOf(limit, MAX_PAGE_SIZE), spec);
}
服务:
@Override
public List<T> getAll(int aStart, int aSize, Specification<T> aSpec) {
return getRepository().findAll((Specification) aSpec, generatePageRequest(aStart, aSize)).getContent();
}
JpaSpecificationExecutor:
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
规格:
public class MultipleSearchSpecification<T extends BaseModel> implements Specification<T> {
private SearchCriteria criteria;
public MultipleSearchSpecification(SearchCriteria aCriteria) {
criteria = aCriteria;
}
@Override
public Predicate toPredicate
(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
if (root.get(criteria.getKey()).getJavaType() == String.class) {
return builder.like(
root.get(criteria.getKey()), "%" + criteria.getValue() + "%");
}
return null;
}}
你知道这个问题的解决方法吗?
您使用的代码不是生成该文字查询,而是使用参数(即 agencyview0_.nationcod like ?
)并将参数值设置为 "%ABCDEFGHIKLMN%"
。
不幸的是,Firebird 将参数的最大长度限制为与其进行比较的字段的声明长度,另请参阅 JDBC-477,并且 Jaybird 无法自动覆盖它。您要么需要超大列定义,要么找到一种方法将参数显式转换为更宽,即确保代码生成类似 agencyview0_.nationcod like cast(? as varchar(100))
.
将服务注册添加到路径 src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory 与数据: systems.config.orm.CustomDataTypesRegistration
public class CustomDataTypesRegistration implements SessionFactoryBuilderFactory {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomDataTypesRegistration.class);
@Override
public SessionFactoryBuilder getSessionFactoryBuilder(final MetadataImplementor metadata, final SessionFactoryBuilderImplementor defaultBuilder) {
logger.info("Registering custom Hibernate data types");
//Deprecated. (since 5.3) No replacement, access to and handling of Types will be much different in 6.0
metadata.getTypeResolver().registerTypeOverride(new OoStringType());
return defaultBuilder;
}}
自定义字符串类型:
public class OoStringType extends StringType {
public OoStringType() {
setSqlTypeDescriptor(OoVarcharTypeDescriptor.INSTANCE);
}}
自定义 VarcharTypeDescriptor:
public class OoVarcharTypeDescriptor extends VarcharTypeDescriptor {
protected static final Logger log = LoggerFactory.getLogger(OoVarcharTypeDescriptor.class.getName());
public static final OoVarcharTypeDescriptor INSTANCE = new OoVarcharTypeDescriptor();
public static final String TRUNCATED_PARAMETER = "truncated parameter [%s] as [%s] - [%s]";
public static final char PERCENTAGE_CHAR = '%';
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
FBParameterMetaData parameterMetaData = (FBParameterMetaData) st.getParameterMetaData();
int precision = parameterMetaData.getPrecision(index);
String unwrappedValue = javaTypeDescriptor.unwrap(value, String.class, options);
if (unwrappedValue.charAt(0) == PERCENTAGE_CHAR && unwrappedValue.charAt(unwrappedValue.length() - 1) == PERCENTAGE_CHAR) {
String coreValue = unwrappedValue.substring(1, unwrappedValue.length() - 1);
if (coreValue.length() > precision) {
unwrappedValue = PERCENTAGE_CHAR + coreValue.substring(0, precision - 2) + PERCENTAGE_CHAR;
log.info(String.format(TRUNCATED_PARAMETER, index, JdbcTypeNameMapper.getTypeName(this.getSqlDescriptor().getSqlType()), unwrappedValue));
}
} else if (unwrappedValue.length() > precision) {
unwrappedValue = unwrappedValue.substring(0, precision);
log.info(String.format(TRUNCATED_PARAMETER, index, JdbcTypeNameMapper.getTypeName(this.getSqlDescriptor().getSqlType()), unwrappedValue));
}
st.setString(index, unwrappedValue);
}
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
st.setString(name, (String) javaTypeDescriptor.unwrap(value, String.class, options));
}
};
}}
请注意,此解决方案可能会导致不需要的结果。例如: 您正在搜索 "ABCD",但您有一个长度为 3 的字段。它将 return 一个包含该字段的结果 "ABC"。