解决数据管理关键字查询对于某些数据库报错的BUG

This commit is contained in:
datagear 2019-10-13 16:40:38 +08:00
parent 587bb51522
commit 5f78c26024
18 changed files with 609 additions and 56 deletions

View File

@ -227,6 +227,34 @@ public abstract class AbstractDevotedDatabaseInfoResolver implements DevotedData
return importedTabless;
}
@Override
public SqlTypeInfo[] getSqlTypeInfos(Connection cn) throws DatabaseInfoResolverException
{
DatabaseMetaData metaData = getDatabaseMetaData(cn);
ResultSet rs = null;
try
{
rs = this.getSqlTypeInfoResultSet(cn, metaData);
List<SqlTypeInfo> sqlTypeInfos = getSqlTypeInfoResultSetSpec().read(rs);
SqlTypeInfo[] array = sqlTypeInfos.toArray(new SqlTypeInfo[sqlTypeInfos.size()]);
postProcessSqlTypeInfo(array);
return array;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
protected TableInfo[] getTableInfos(Connection cn, DatabaseMetaData metaData, String schema)
throws DatabaseInfoResolverException
{
@ -672,6 +700,19 @@ public abstract class AbstractDevotedDatabaseInfoResolver implements DevotedData
return databaseMetaData.getExportedKeys(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName);
}
/**
* 构建{@linkplain SqlTypeInfo}结果集
*
* @param cn
* @param databaseMetaData
* @return
* @throws SQLException
*/
protected ResultSet getSqlTypeInfoResultSet(Connection cn, DatabaseMetaData databaseMetaData) throws SQLException
{
return databaseMetaData.getTypeInfo();
}
/**
* 获取{@linkplain TableInfo}{@linkplain ResultSetSpec}
*
@ -732,6 +773,16 @@ public abstract class AbstractDevotedDatabaseInfoResolver implements DevotedData
return new ExportedKeyInfoResultSetSpec();
}
/**
* 获取{@linkplain SqlTypeInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<SqlTypeInfo> getSqlTypeInfoResultSetSpec()
{
return new SqlTypeInfoResultSetSpec();
}
/**
* 获取表类型数组
*
@ -778,6 +829,18 @@ public abstract class AbstractDevotedDatabaseInfoResolver implements DevotedData
{
}
/**
* 后置处理{@link SqlTypeInfo}
* <p>
* 子类可以重写此方法对{@linkplain SqlTypeInfo}进行特殊后置处理
* </p>
*
* @param sqlTypeInfos
*/
protected void postProcessSqlTypeInfo(SqlTypeInfo[] sqlTypeInfos)
{
}
/**
* 后置处理{@link ExportedKeyInfo}
* <p>

View File

@ -7,6 +7,8 @@ package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
import java.util.List;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain ColumnInfo}结果集规范
*
@ -54,6 +56,7 @@ public class ColumnInfoResultSetSpec extends ResultSetSpec<ColumnInfo>
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项")
protected void addToList(List<ColumnInfo> list, ColumnInfo bean)
{
for (ColumnInfo ele : list)

View File

@ -157,4 +157,16 @@ public interface DatabaseInfoResolver
* @throws DatabaseInfoResolverException
*/
String[][] getImportedTables(Connection cn, String[] tables) throws DatabaseInfoResolverException;
/**
* 获取{@linkplain SqlTypeInfo}
* <p>
* 如果无法获取将返回空数组
* </p>
*
* @param cn
* @return
* @throws DatabaseInfoResolverException
*/
SqlTypeInfo[] getSqlTypeInfos(Connection cn) throws DatabaseInfoResolverException;
}

View File

@ -6,6 +6,8 @@ package org.datagear.dbinfo;
import java.util.List;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain ExportedKeyInfo}结果集规范
*
@ -32,6 +34,7 @@ public class ExportedKeyInfoResultSetSpec extends ResultSetSpec<ExportedKeyInfo>
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项")
protected void addToList(List<ExportedKeyInfo> list, ExportedKeyInfo bean)
{
for (ExportedKeyInfo ele : list)

View File

@ -155,6 +155,14 @@ public class GenericDatabaseInfoResolver implements DatabaseInfoResolver
return databaseInfoResolver.getImportedTables(cn, tables);
}
@Override
public SqlTypeInfo[] getSqlTypeInfos(Connection cn) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getSqlTypeInfos(cn);
}
/**
* 获取支持指定{@code url}{@linkplain DatabaseInfoResolver}
*

View File

@ -7,6 +7,8 @@ package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
import java.util.List;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain ImportedKeyInfo}结果集规范
*
@ -56,6 +58,7 @@ public class ImportedKeyInfoResultSetSpec extends ResultSetSpec<ImportedKeyInfo>
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项Mysql JDBC 5.1.47的getImportedKeys在设置了连接参数useInformationSchema为true时会返回重复记录")
protected void addToList(List<ImportedKeyInfo> list, ImportedKeyInfo bean)
{
for (ImportedKeyInfo ele : list)

View File

@ -6,6 +6,8 @@ package org.datagear.dbinfo;
import java.util.List;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain PrimaryKeyInfo}结果集规范
*
@ -23,6 +25,7 @@ public class PrimaryKeyInfoResultSetSpec extends ResultSetSpec<PrimaryKeyInfo>
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项")
protected void addToList(List<PrimaryKeyInfo> list, PrimaryKeyInfo bean)
{
for (PrimaryKeyInfo ele : list)

View File

@ -169,10 +169,6 @@ public abstract class ResultSetSpec<T extends ResultSetSpecBean>
* <p>
* 注意子类应该重写此方法并避免添加重复元素
* </p>
* <p>
* 比如Mysql的{@code mysql-connector-java-5.1.47}驱动{@code com.mysql.jdbc.DatabaseMetaData.getImportedKeys(String, String, String)}
* 在设置了连接参数{@code useInformationSchema}{@code true}竟然会返回重复记录
* </p>
*
* @param list
* @param bean

View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.dbinfo;
import java.io.Serializable;
import java.sql.DatabaseMetaData;
/**
* SQL类型信息
*
* @author datagear@163.com
*
*/
public class SqlTypeInfo extends ResultSetSpecBean implements Serializable
{
private static final long serialVersionUID = 1L;
private String name;
/** SQL类型对应java.sql.Types中的值 */
private int type;
/** 可搜索类型 */
private SearchableType searchableType;
public SqlTypeInfo()
{
super();
}
public SqlTypeInfo(String name, int type, SearchableType searchableType)
{
super();
this.name = name;
this.type = type;
this.searchableType = searchableType;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getType()
{
return type;
}
public void setType(int type)
{
this.type = type;
}
public SearchableType getSearchableType()
{
return searchableType;
}
public void setSearchableType(SearchableType searchableType)
{
this.searchableType = searchableType;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", type=" + type + ", searchableType=" + searchableType
+ "]";
}
/**
* 查找指定SQL类型的{@linkplain SqlTypeInfo}
* <p>
* 如果未找到返回{@code null}
* </p>
*
* @param sqlTypeInfos
* @param type
* @return
*/
public static SqlTypeInfo findByType(SqlTypeInfo[] sqlTypeInfos, int type)
{
if (sqlTypeInfos == null)
return null;
for (SqlTypeInfo sqlTypeInfo : sqlTypeInfos)
{
if (sqlTypeInfo.getType() == type)
return sqlTypeInfo;
}
return null;
}
/**
* 可搜索类型WHERE条件类型
* <p>
* 参考{@linkplain DatabaseMetaData#getTypeInfo()}结果集{@code SEARCHABLE}列说明
* </p>
*
* @author datagear@163.com
*
*/
public static enum SearchableType
{
/** 不可用于WHERE */
NO,
/** 仅可用于WHERE中的LIKE */
ONLY_LIKE,
/** 仅不可用于WHERE中的LIKE */
EXPCEPT_LIKE,
/** 可用于WHERE中的任何情况 */
ALL
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
import java.util.List;
import org.datagear.dbinfo.SqlTypeInfo.SearchableType;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain SqlTypeInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class SqlTypeInfoResultSetSpec extends ResultSetSpec<SqlTypeInfo>
{
public static final Converter<Integer, SearchableType> SEARCHABLETYPE_CONVERTER = new Converter<Integer, SearchableType>()
{
@Override
public SearchableType convert(Integer type) throws ResultSetIncompatibleException
{
if (DatabaseMetaData.typePredNone == type)
return SearchableType.NO;
else if (DatabaseMetaData.typePredChar == type)
return SearchableType.ONLY_LIKE;
else if (DatabaseMetaData.typePredBasic == type)
return SearchableType.EXPCEPT_LIKE;
else if (DatabaseMetaData.typeSearchable == type)
return SearchableType.ALL;
else
return SearchableType.NO;
}
};
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec<?, ?>[] {
new RsColumnSpec<String, String>("TYPE_NAME", String.class, true, false, "name"),
new RsColumnSpec<Integer, Integer>("DATA_TYPE", Integer.class, false, true, "type"),
new RsColumnSpec<Integer, SearchableType>("SEARCHABLE", Integer.class, true, true, "searchableType",
SEARCHABLETYPE_CONVERTER) };
public SqlTypeInfoResultSetSpec()
{
super();
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项")
protected void addToList(List<SqlTypeInfo> list, SqlTypeInfo bean)
{
for (SqlTypeInfo ele : list)
{
if (equalsWithNull(ele.getName(), bean.getName()) || equalsWithNull(ele.getType(), bean.getType()))
return;
}
list.add(bean);
}
@Override
protected Class<SqlTypeInfo> getRowType()
{
return SqlTypeInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -6,6 +6,8 @@ package org.datagear.dbinfo;
import java.util.List;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain TableInfo}结果集规范
*
@ -47,6 +49,7 @@ public class TableInfoResultSetSpec extends ResultSetSpec<TableInfo>
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项")
protected void addToList(List<TableInfo> list, TableInfo bean)
{
for (TableInfo ele : list)

View File

@ -6,6 +6,8 @@ package org.datagear.dbinfo;
import java.util.List;
import org.datagear.util.JDBCCompatiblity;
/**
* {@linkplain UniqueKeyInfo}结果集规范
*
@ -24,6 +26,7 @@ public class UniqueKeyInfoResultSetSpec extends ResultSetSpec<UniqueKeyInfo>
}
@Override
@JDBCCompatiblity("避免某些驱动程序的结果集出现重复项")
protected void addToList(List<UniqueKeyInfo> list, UniqueKeyInfo bean)
{
for (UniqueKeyInfo ele : list)

View File

@ -26,6 +26,7 @@ import org.datagear.dbinfo.EntireTableInfo;
import org.datagear.dbinfo.ExportedKeyInfo;
import org.datagear.dbinfo.ImportedKeyInfo;
import org.datagear.dbinfo.ImportedKeyRule;
import org.datagear.dbinfo.SqlTypeInfo;
import org.datagear.dbinfo.TableInfo;
import org.datagear.dbinfo.TableType;
import org.datagear.model.Label;
@ -66,6 +67,7 @@ import org.datagear.persistence.features.PropertyKeyDeleteRule;
import org.datagear.persistence.features.PropertyKeyPropertyName;
import org.datagear.persistence.features.PropertyKeyUpdateRule;
import org.datagear.persistence.features.RelationPoint;
import org.datagear.persistence.features.Searchable;
import org.datagear.persistence.features.TableName;
import org.datagear.persistence.mapper.Mapper;
import org.datagear.persistence.mapper.MapperResolver;
@ -362,7 +364,11 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
protected Model doResolve(Connection cn, ModelManager globalModelManager, ModelManager localModelManager,
EntireTableInfo entireTableInfo, String modelName) throws DatabaseModelResolverException
{
ModelBuilder modelBuilder = createModelBuilder();
SqlTypeInfo[] sqlTypeInfos = resolveSqlTypeInfos(cn);
ModelBuildContext buildContext = new ModelBuildContext();
buildContext.setSqlTypeInfos(sqlTypeInfos);
ModelBuilder modelBuilder = createModelBuilder(buildContext);
modelBuilder.init(modelName);
// 必须在解析之前加入防止后续Property解析因循环引用而导致死循环
@ -2000,6 +2006,8 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
propertyBuilder, entireTableInfo, columnInfo, columnIndex);
resolvePrimitivePropertyFeatureDescLabel(cn, globalModelManager, localModelManager, modelBuilder,
propertyBuilder, entireTableInfo, columnInfo, columnIndex);
resolvePrimitivePropertyFeatureSearchable(cn, globalModelManager, localModelManager, modelBuilder,
propertyBuilder, entireTableInfo, columnInfo, columnIndex);
resolvePrimitivePropertyFeatureToken(cn, globalModelManager, localModelManager, modelBuilder, propertyBuilder,
entireTableInfo, columnInfo, columnIndex);
resolvePrimitivePropertyFeatureColumnConverter(cn, globalModelManager, localModelManager, modelBuilder,
@ -2125,6 +2133,37 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
addDescLabelFeature(propertyBuilder, new Label(comment));
}
/**
* 解析基本{@linkplain Property}{@linkplain Searchable}
*
* @param cn
* @param globalModelManager
* @param localModelManager
* @param modelBuilder
* @param propertyBuilder
* @param entireTableInfo
* @param columnInfo
* @param columnIndex
* @throws DatabaseModelResolverException
*
*/
protected void resolvePrimitivePropertyFeatureSearchable(Connection cn, ModelManager globalModelManager,
ModelManager localModelManager, ModelBuilder modelBuilder, PropertyBuilder propertyBuilder,
EntireTableInfo entireTableInfo, ColumnInfo columnInfo, int columnIndex)
throws DatabaseModelResolverException
{
ModelBuildContext buildContext = modelBuilder.getBuildContext();
if (!buildContext.hasSqlTypeInfo())
return;
JdbcType jdbcType = propertyBuilder.getFeature(JdbcType.class);
SqlTypeInfo sqlTypeInfo = SqlTypeInfo.findByType(buildContext.getSqlTypeInfos(), jdbcType.getValue());
if (sqlTypeInfo != null)
propertyBuilder.addFeature(Searchable.class, new Searchable(sqlTypeInfo.getSearchableType()));
}
/**
* 解析基本{@linkplain Property}{@linkplain Token}
*
@ -2385,6 +2424,27 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
return this.databaseInfoResolver.getEntireTableInfo(cn, table);
}
/**
* 解析{@linkplain SqlTypeInfo}
* <p>
* 出现解析异常将返回空数组
* </p>
*
* @param cn
* @return
*/
protected SqlTypeInfo[] resolveSqlTypeInfos(Connection cn)
{
try
{
return this.databaseInfoResolver.getSqlTypeInfos(cn);
}
catch (DatabaseInfoResolverException e)
{
return new SqlTypeInfo[0];
}
}
/**
* 解析{@linkplain TableInfo}
*
@ -3094,11 +3154,12 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
/**
* 创建{@linkplain ModelBuilder}
*
* @param buildContext
* @return
*/
protected ModelBuilder createModelBuilder()
protected ModelBuilder createModelBuilder(ModelBuildContext buildContext)
{
return new ModelBuilder();
return new ModelBuilder(buildContext);
}
/**
@ -3198,6 +3259,43 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
this.features.put(key, true);
}
@SuppressWarnings("unchecked")
public <F> F getFeature(Object key)
{
return (this.features == null ? null : (F) this.features.get(key));
}
}
/**
* {@linkplain Model}构建上下文
*
* @author datagear@163.com
*
*/
protected static class ModelBuildContext
{
private SqlTypeInfo[] sqlTypeInfos;
public ModelBuildContext()
{
super();
}
public boolean hasSqlTypeInfo()
{
return (this.sqlTypeInfos != null && this.sqlTypeInfos.length > 0);
}
public SqlTypeInfo[] getSqlTypeInfos()
{
return sqlTypeInfos;
}
public void setSqlTypeInfos(SqlTypeInfo[] sqlTypeInfos)
{
this.sqlTypeInfos = sqlTypeInfos;
}
}
/**
@ -3208,6 +3306,8 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
*/
protected static class ModelBuilder extends AbstractFeaturedBuilder implements PropertyNameContext
{
private ModelBuildContext buildContext;
private DefaultModel model;
private Class<?> type;
@ -3223,6 +3323,12 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
super();
}
public ModelBuilder(ModelBuildContext buildContext)
{
super();
this.buildContext = buildContext;
}
public void init(String name)
{
this.model = new DefaultModel();
@ -3241,6 +3347,16 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
this.model.setFeatures(this.features);
}
public ModelBuildContext getBuildContext()
{
return buildContext;
}
public void setBuildContext(ModelBuildContext buildContext)
{
this.buildContext = buildContext;
}
public Model getModel()
{
return this.model;

View File

@ -61,4 +61,12 @@ public interface Dialect
* @return
*/
SqlBuilder toPagingSql(SqlBuilder queryView, SqlBuilder condition, Order[] orders, long startRow, int count);
/**
* 给定SQL类型的列是否是可排序的
*
* @param sqlType
* @return
*/
boolean isSortable(int sqlType);
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.persistence.features;
import org.datagear.dbinfo.SqlTypeInfo.SearchableType;
import org.datagear.model.PropertyFeature;
import org.datagear.model.ValueFeature;
import org.datagear.persistence.PersistenceFeature;
/**
* 可搜索类型
*
* @author datagear@163.com
*
*/
public class Searchable extends ValueFeature<SearchableType> implements PropertyFeature, PersistenceFeature
{
public Searchable()
{
super();
}
public Searchable(SearchableType value)
{
super(value);
}
}

View File

@ -4,9 +4,12 @@
package org.datagear.persistence.support;
import java.sql.Types;
import org.datagear.persistence.Dialect;
import org.datagear.persistence.Order;
import org.datagear.persistence.SqlBuilder;
import org.datagear.util.JDBCCompatiblity;
/**
* 抽象{@linkplain Dialect}
@ -47,6 +50,21 @@ public abstract class AbstractDialect implements Dialect
return this.identifierQuote + name + this.identifierQuote;
}
@Override
@JDBCCompatiblity("某些驱动程序对有些类型不支持排序比如Oracle对于BLOB类型")
public boolean isSortable(int sqlType)
{
if (Types.BIGINT == sqlType || Types.BIT == sqlType || Types.BOOLEAN == sqlType || Types.CHAR == sqlType
|| Types.DATE == sqlType || Types.DECIMAL == sqlType || Types.DOUBLE == sqlType
|| Types.FLOAT == sqlType || Types.INTEGER == sqlType || Types.NCHAR == sqlType
|| Types.NUMERIC == sqlType || Types.NVARCHAR == sqlType || Types.REAL == sqlType
|| Types.SMALLINT == sqlType || Types.TIME == sqlType || Types.TIMESTAMP == sqlType
|| Types.TINYINT == sqlType || Types.VARCHAR == sqlType)
return true;
return false;
}
/**
* 转换为排序SQL
*

View File

@ -37,12 +37,14 @@ import java.util.concurrent.LinkedBlockingDeque;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.datagear.dbinfo.SqlTypeInfo.SearchableType;
import org.datagear.model.Model;
import org.datagear.model.Property;
import org.datagear.model.features.NotReadable;
import org.datagear.model.features.Token;
import org.datagear.model.support.MU;
import org.datagear.model.support.PropertyPath;
import org.datagear.model.support.PropertyPathInfo;
import org.datagear.persistence.ColumnPropertyPath;
import org.datagear.persistence.Dialect;
import org.datagear.persistence.Order;
@ -301,9 +303,9 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
SqlBuilder queryView = buildQueryViewForModel(dialect, table, null, selectColumnPropertyPaths, model);
SqlBuilder condition = buildQueryCondition(pagingQuery, selectColumnPropertyPaths);
SqlBuilder condition = buildQueryCondition(dialect, model, pagingQuery, selectColumnPropertyPaths);
Order[] orders = buildQueryOrders(model, pagingQuery, selectColumnPropertyPaths);
Order[] orders = buildQueryOrders(dialect, model, pagingQuery, selectColumnPropertyPaths);
long total = queryCount(cn, queryView, condition);
@ -366,9 +368,9 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
if (propertyModelQueryPattern)
selectColumnPropertyPaths = toPropertyColumnPropertyPaths(selectColumnPropertyPaths, model, property);
SqlBuilder condition = buildQueryCondition(pagingQuery, selectColumnPropertyPaths);
SqlBuilder condition = buildQueryCondition(dialect, model, pagingQuery, selectColumnPropertyPaths);
Order[] orders = buildQueryOrders(MU.getModel(property), pagingQuery, selectColumnPropertyPaths);
Order[] orders = buildQueryOrders(dialect, model, pagingQuery, selectColumnPropertyPaths);
long total = queryCount(cn, queryView, condition);
@ -1626,11 +1628,14 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
/**
* {@linkplain Query}构建查询条件SQL
*
* @param dialect
* @param model
* @param query
* @param selectColumnPropertyPaths
* @return
*/
protected SqlBuilder buildQueryCondition(Query query, List<ColumnPropertyPath> selectColumnPropertyPaths)
protected SqlBuilder buildQueryCondition(Dialect dialect, Model model, Query query,
List<ColumnPropertyPath> selectColumnPropertyPaths)
{
if (!query.hasKeyword() && !query.hasCondition())
return null;
@ -1646,10 +1651,9 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
{
SqlBuilder keywordCondition = SqlBuilder.valueOf();
String keyword = wrapLikeKeyword(query.getKeyword());
String andSql = (query.isNotLike() ? " AND " : " OR ");
String likeSql = (query.isNotLike() ? " NOT LIKE " : " LIKE ");
String equalSql = (query.isNotLike() ? " != " : " = ");
if (hasCondition)
keywordCondition.sql("(");
@ -1660,16 +1664,57 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
if (!columnPropertyPath.isToken())
continue;
int sqlType = columnPropertyPath.getColumnSqlType();
if (!JdbcUtil.isSearchableSqlType(sqlType))
continue;
String myOperator = null;
Object myKeyword = null;
if (appendCount > 0)
keywordCondition.sql(andSql);
PropertyPath propertyPath = PropertyPath.valueOf(columnPropertyPath.getPropertyPath());
PropertyPathInfo propertyPathInfo = PropertyPathInfo.valueOf(model, propertyPath);
Property tailProperty = propertyPathInfo.getPropertyTail();
keywordCondition.sql(columnPropertyPath.getColumnNameQuote() + likeSql + "?", keyword);
@JDBCCompatiblity("很多驱动程序的值为SearchableType.ALL但实际并不支持LIKE语法比如PostgreSQL JDBC 42.2.5"
+ "这里为了兼容暂时采用else中的逻辑")
SearchableType searchableType = null;
// Searchable searchable =
// tailProperty.getFeature(Searchable.class);
// searchableType = (searchable == null ? null :
// searchable.getValue());
appendCount++;
if (SearchableType.NO.equals(searchableType))
;
else if (SearchableType.ONLY_LIKE.equals(searchableType) || SearchableType.ALL.equals(searchableType))
{
myOperator = likeSql;
myKeyword = wrapLikeKeyword(query.getKeyword());
}
else
{
// SearchableType.EXPCEPT_LIKE或者无SearchableType
int sqlType = ((JdbcType) tailProperty.getFeature(JdbcType.class)).getValue();
Number number = JdbcUtil.parseToNumber(query.getKeyword(), sqlType);
if (number != null)
{
myOperator = equalSql;
myKeyword = number;
}
else if (Types.CHAR == sqlType || Types.VARCHAR == sqlType || Types.NCHAR == sqlType
|| Types.NVARCHAR == sqlType)
{
myOperator = likeSql;
myKeyword = wrapLikeKeyword(query.getKeyword());
}
}
if (myOperator != null && myKeyword != null)
{
if (appendCount > 0)
keywordCondition.sql(andSql);
keywordCondition.sql(columnPropertyPath.getColumnNameQuote() + myOperator + "?", myKeyword);
appendCount++;
}
}
if (hasCondition)
@ -1701,12 +1746,14 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
/**
* {@linkplain Query}构建{@linkplain Order}数组
*
* @param dialect
* @param model
* @param query
* @param selectColumnPropertyPaths
* @return
*/
protected Order[] buildQueryOrders(Model model, Query query, List<ColumnPropertyPath> selectColumnPropertyPaths)
protected Order[] buildQueryOrders(Dialect dialect, Model model, Query query,
List<ColumnPropertyPath> selectColumnPropertyPaths)
{
if (!query.hasOrder())
return null;
@ -1735,7 +1782,7 @@ public class SelectPersistenceOperation extends AbstractModelPersistenceOperatio
{
int sqlType = columnPropertyPath.getColumnSqlType();
if (!JdbcUtil.isSortableSqlType(sqlType))
if (!dialect.isSortable(sqlType))
continue;
String propertyPath = columnPropertyPath.getPropertyPath();

View File

@ -553,6 +553,75 @@ public class JdbcUtil
return getJdbcType(obj.getClass());
}
/**
* 将字符串转换为指定SQL类型的数值
* <p>
* 如果{@code str}不合法或者{@code sqlType}不是数值类型将返回{@code null}
* </p>
*
* @param sqlType
* @return
*/
public static Number parseToNumber(String str, int sqlType)
{
BigDecimal number = null;
switch (sqlType)
{
case Types.NUMERIC:
case Types.DECIMAL:
{
return toBigDecimal(str);
}
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
{
number = toBigDecimal(str);
return (number == null ? null : number.intValue());
}
case Types.BIGINT:
{
number = toBigDecimal(str);
return (number == null ? null : number.longValue());
}
case Types.REAL:
case Types.FLOAT:
{
number = toBigDecimal(str);
return (number == null ? null : number.floatValue());
}
case Types.DOUBLE:
{
number = toBigDecimal(str);
return (number == null ? null : number.doubleValue());
}
default:
return null;
}
}
/**
* 转换为{@code BigDecimal}
* <p>
* 如果不合法将返回{@code null}
* </p>
*
* @param str
* @return
*/
protected static BigDecimal toBigDecimal(String str)
{
try
{
return new BigDecimal(str);
}
catch (Throwable t)
{
return null;
}
}
/**
* 获取指定类的JDBC类型
*
@ -1059,39 +1128,6 @@ public class JdbcUtil
return queryResultSet;
}
/**
* 指定SQL类型的列是否是可排序列
*
* @param sqlType
* @return
*/
@JDBCCompatiblity("某些驱动程序对有些类型不支持排序比如Oracle对于BLOB类型")
public static boolean isSortableSqlType(int sqlType)
{
if (Types.BIGINT == sqlType || Types.BIT == sqlType || Types.BOOLEAN == sqlType || Types.CHAR == sqlType
|| Types.CLOB == sqlType || Types.DATE == sqlType || Types.DECIMAL == sqlType || Types.DOUBLE == sqlType
|| Types.FLOAT == sqlType || Types.INTEGER == sqlType || Types.LONGNVARCHAR == sqlType
|| Types.LONGVARCHAR == sqlType || Types.NCHAR == sqlType || Types.NCLOB == sqlType
|| Types.NUMERIC == sqlType || Types.NVARCHAR == sqlType || Types.REAL == sqlType
|| Types.SMALLINT == sqlType || Types.SQLXML == sqlType || Types.TIME == sqlType
|| Types.TIMESTAMP == sqlType || Types.TINYINT == sqlType || Types.VARCHAR == sqlType)
return true;
return false;
}
/**
* 指定SQL类型的列是否是可搜索列
*
* @param sqlType
* @return
*/
@JDBCCompatiblity("某些驱动程序对有些类型不支持搜索比如Oracle对于BLOB类型")
public static boolean isSearchableSqlType(int sqlType)
{
return isSortableSqlType(sqlType);
}
/**
* SQL查询结果集
*