forked from p81075629/datagear
解决数据管理关键字查询对于某些数据库报错的BUG
This commit is contained in:
parent
587bb51522
commit
5f78c26024
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}。
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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。
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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查询结果集。
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue