SearchCondition
SearchCondition 表示以下形式的 SQL WHERE 子句表达式:<左侧运算对象> <运算符> <右侧运算对象>
以下为示例:
MyTable.Column1 = 5
MyTable.Column2 LIKE "E%"
MyTable.Column3 = JoinTable.Column1
运算对象也可以更复杂,例如 SQL 函数或子选择。SearchCondition 可以使用任意的 RelationalExpression 运算对象。可以使用 SearchCondition 构造函数或 setter 方法指定运算对象。以下是具体的 ColumnExpression 实现:
ClassAttribute
|
此类表示可在 SQL 语句中使用的类属性。自省信息用于确定关联的表格和列。
|
SQLFunction
|
此类表示 SQL 语句中的 SQL 函数。
|
SubSelectExpression
|
此类表示可在 SQL 语句中使用的子选择。子选择是通过 StatementSpec 属性指定的。
|
ConstantExpression
|
此类表示 SQL 语句中的常量。
|
KeywordExpression
|
此类表示一个表达式,该表达式的结果为可以在 SQL 语句中使用的 SQL 关键字。
|
RangeExpression
|
此类表示 SQL WHERE 子句中的一个范围。
|
DateExpression
|
此类表示 SQL 语句中的日期常量。ConstantExpression 的此子类对于提供日期值的特殊处理是必需的。
|
ArrayExpression
|
此类表示 SQL IN 子句中的常量数组。
|
TableColumn
|
此类表示可在 SQL 语句中使用的表列。指定的精确表格和列名称将直接用于 SQL 语句中。
|
|
|
以下示例构建一个复杂的查询,以确定在指定的日期截止后使用最旧的修改时间戳的 WTPartMaster 对象。以下是此查询的 SQL:
SELECT A0.*
FROM WTPartMaster A0
WHERE (A0.modifyStampA2 IN (SELECT MIN(B0.modifyStampA2)
FROM WTPartMaster B0
WHERE B0.modifyStampA2 > ’cutoff’) )
以下代码将构造查询规范:
Class targetClass = wt.part.WTPartMaster.class;
QuerySpec subSelect = new QuerySpec();
subSelect.getFromClause().setAliasPrefix("B");
int subIndex = subSelect.appendClassList(targetClass, false);
int[] fromIndicies = { subIndex };
ClassAttribute subModifyStamp =
new ClassAttribute(targetClass,WTAttributeNameIfc.MODIFY_STAMP_NAME);
SQLFunction minFunction = SQLFunction.new SQLFunction(SQLFunction.
MINIMUM, subModifyStamp);
subSelect.appendSelect(minFunction, fromIndicies, false);
subSelect.appendWhere(new SearchCondition(subModifyStamp,
SearchCondition.GREATER_THAN, DateExpression.newExpression(cutoff)),
fromIndicies);
QuerySpec select = new QuerySpec();
int index = select.appendClassList(targetClass, true);
select.appendWhere(new SearchCondition(modifyStamp,SearchCondition.IN,
new SubSelectExpression(subSelect)), new int[] { index });
复合查询
复合查询是一种 SQL 语句,可通过 set 运算符将多个组件查询组合到单个 SQL 语句中。集合运算符包括 UNION、UNION ALL、INTERSECT 和 MINUS。通过指定 set 运算符并添加组件查询来组成复合查询。组件查询是 StatementSpec 对象,因此也支持复合查询的嵌套。
以下示例构建一个复合查询,以返回特定的 PartMaster 编号及其所有替换项的编号。请注意,仅选择编号,而不选择完整的对象。这是必需的,因为如果考虑了所有子类,则复合查询语句必须包括所有子类表。这些子类表可能包含其他列,这些列会使每个语句的选择列表与其他组件语句不兼容。SQL 要求复合语句中的每个组件查询必须在选择列表中具有相同的编号和相应的类型。以下是此查询的 SQL:
SELECT A0.number
FROM WTPartMaster A0
WHERE (A0.name = ‘ENGINE')
UNION
SELECT A2.number
FROM WTPartMaster A0,WTPartAlternateLink A1,WTPartMaster A2
WHERE (A0.name = ‘ENGINE') AND
(A0.idA2A2 = A1.idA3A5) AND (A2.idA2A2 = A1.idA3B5)
以下代码将构造查询规范。构造的第一个选择是针对 PartMasters 的,名称为 ENGINE。
QuerySpec partSelect = new QuerySpec();
int partIndex = partSelect.appendClassList(wt.part.WTPartMaster.class, false);
partSelect.appendWhere(new SearchCondition(wt.part.WTPartMaster.class,
WTPartMaster.NAME, SearchCondition.EQUAL, "ENGINE"), new int[]
{ partIndex });
构造下一个选择以返回 PartMaster 替换项。替换项由 WTPartAlternateLink 类表示,该类是 PartMasters 之间的多对多关联。必须在整个关联中指定从原始部件到其替换部件的联接。
QuerySpec altSelect = new QuerySpec();
partIndex = altSelect.appendClassList(wt.part.WTPartMaster.class, false);
int altIndex = altSelect.appendClassList(W wt.part.WTPartAlternateLink.class,
false);
int altPartIndex = altSelect.appendClassList(wt.part.WTPartMaster.class,
false);
altSelect.appendSelect(new ClassAttribute(
wt.part.WTPartMaster.class, wt.part.WTPartMaster.NUMBER),
new int[] { altPartIndex }, false);
altSelect.appendWhere(new
SearchCondition(wt.part.WTPartMaster.class,
WTPartMaster.NAME, SearchCondition.EQUAL, "ENGINE"), new int[]
{ partIndex });
altSelect.appendJoin(altIndex, wt.part.WTPartAlternateLink.ALTERNATES_ROLE,
partIndex);
altSelect.appendJoin(altIndex, wt.part.WTPartAlternateLink.ALTERNATE_FOR_ROLE,
altPartIndex);
最后,使用前两个查询和 UNION 集合运算符构造复合语句。
CompoundQuerySpec compound = new CompoundQuerySpec();
compound.setSetOperator(SetOperator.UNION);
compound.addComponent(partSelect);
compound.addComponent(altSelect);
访问控制注意事项
某些高级 SQL API 的使用可略过访问控制。系统会检测到这些情况,并会抛出 AdvancedQueryAccessException。以下高级查询使用将导致此异常:
• 子选择 (wt.query.SubSelectExpression)
• MINUS 或 INTERSECT 复合语句 (wt.query.CompoundQuerySpec)
• 外部表格 (wt.query.ExternalTableExpression)
• 聚合函数 (wt.query.SQLFunction)
AVERAGE
MAXIMUM
MINIMUM
SUM
COUNT
• ROWNUM 关键字 (wt.query.KeywordExpression)
这样做是为了确保不会在不知情的情况下略过访问控制。在某些情况下,使用略过访问控制的这些高级 SQL 功能是合法的。在这些情况下,可以在运行时禁用高级检查。查询规范类支持只能从服务器端代码设置的 "advancedQueryEnabled" 属性。如果适用,应在传递给 PersistenceManager 查询/查找 API 的查询实例上将该属性设置为 true,以允许执行这些查询而不会抛出异常。
// Use advanced APIs to build query.
// Disable checking of advance features statement.setAdvancedQueryEnabled(true);
// Execute query with access control
PersistenceHelper.manager.find(statement);
find() 方法使用访问控制执行该语句。因此,可将访问控制相关列隐式添加到选择中。对于某些高级功能 (例如聚合函数、INTERSECT 和 MINUS),添加这些列可能会影响预期的结果或导致 SQL 异常。在这些情况下,要成功在服务器端执行查询,应使用非访问控制的 query() 方法。
PersistenceServerHelper.manager.query(statement);
排序
查询可用于在数据库级别对结果数据进行排序。不过,通常情况下,数据库排序应仅应用于分页查询和仅涉及 ColumnExpressions 的查询。其他类型的查询可作为几个单独的 SQL 语句来实现,因此排序仅应用于单个语句,而不应用于完整的查询。任何 ColumnExpression 都可以用作排序列。OrderBy 项用于将 ColumnExpression 传递到 StatementSpec。OrderBy 还指示排序顺序 (升序或降序) 以及可选的区域设置。如果指定了区域设置,则将使用数据库语言支持针对该区域设置对任何基于字符的属性进行排序。对于 Oracle,这是本国语言支持 (NLS) (有关详细信息,请参阅 Oracle 文档)。Java 区域设置值通过 dbservice.properties 条目映射到 Oracle NLS 语言排序名称。
通过 QuerySpec 和 CompoundQuerySpec 方法的标准查询和复合查询均支持排序。
QuerySpec.appendOrderBy(OrderBy a_orderBy, int[] a_fromIndicies)
CompoundQuerySpec.appendOrderBy(OrderBy a_orderBy)
QuerySpec 方法根据 QuerySpec 的 FROM 子句验证 OrderBy 中包含的 ColumnExpression。CompoundQuerySpec 方法不会验证。请注意,这两种方法都不会将 ColumnExpression 附加到语句的 SELECT 子句。这仍然必须通过 appendSelect() 方法完成。在这两种情况下,建议为 OrderBy 中包含的每个 ColumnExpression 设置一个列别名。根据查询的类型和所涉及子类的数量,如果不使用列别名,则实际的 SQL 语句可能无效。请注意,列别名不得为 SQL 保留字 (例如,"number")。
以下示例使用排序来构建复合查询。部件和文档的名称按名称排序返回。以下是此查询的 SQL:
SELECT A0.bname sortName
FROM WTPart A0
UNION
SELECT A0.bname sortName
FROM WTDocument A0
ORDER BY sortName DESC
以下代码将构造查询规范。第一个组件查询是针对部件的。请注意列别名的设置。
String sortName = "sortName";
QuerySpec partQuery = new QuerySpec();
int classIndex = partQuery.appendClassList(wt.part.WTPart.class, false);
ClassAttribute partName = new ClassAttribute(wt.part.WTPart.class,
wt.part.WTPart.NAME);
partName.setColumnAlias(sortName);
partQuery.appendSelect(partName, new int[] { classIndex }, false);
下一部分将构造查询的文档部分。使用相同的列别名。
QuerySpec docQuery = new QuerySpec();
classIndex = docQuery.appendClassList(wt.doc.WTDocument.class, false);
ClassAttribute docName =
new ClassAttribute(wt.doc.WTDocument.class, wt.doc.WTDocument.NAME);
docName.setColumnAlias(sortName);
docQuery.appendSelect(docName, new int[] { classIndex }, false);
最后,使用这两个组件查询构造复合查询。OrderBy 会附加到整个查询中。默认区域设置用于根据用户语言对名称进行排序。
CompoundQuerySpec query = new CompoundQuerySpec();
query.setSetOperator(SetOperator.UNION);
query.addComponent(partQuery);
query.addComponent(docQuery);
query.appendOrderBy(new OrderBy(partName, true
));
要启用检查高级功能语句并成功在服务器端执行查询,必须使用非访问控制的 query() 方法。
query.setAdvancedQueryEnabled(true);
QueryResult queryResult = PersistenceHelper.manager.query (statement);
|
QuerySpec 必须在远程方法服务器存根中执行。有关示例的详细信息,请参阅访问控制注意事项。
|
联接支持
查询联接用于关联单独表中包含的数据。可以通过使用 PersistenceManager 导航方法或通过专用 WhereExpressions 完成联接。QuerySpec 类还提供了显式支持,以使用 Rose 模型中定义的链接类和角色将联接附加到查询。这提供了 QuerySpec 的灵活性以及使用模型信息指定查询联接的简便性。可以使用以下 QuerySpec 方法。
appendJoin(int a_linkIndex, String a_role, Persistable a_source)
appendJoin(int a_linkIndex, String a_role, int a_targetIndex)
以下示例构建了一个查询,该查询通过 FolderMembership 链接将 SubFolder 和 Part 类联接在一起。该查询返回所有文件夹以及文件夹中包含的所有关联部件。以下代码将构造查询规范。第一部分添加了应该返回的类和属性。最后两行代码使用 FolderMembership 链接类的建模角色将这些类联接在一起。
QuerySpec query = new QuerySpec();
int folderIndex = query.appendClassList(wt.folder.SubFolder.class,
false);
int linkIndex = query.appendClassList(wt.folder.FolderMembership.class,
false);
int partIndex = query.appendClassList(wt.part.WTPart.class, false);
query.appendSelect(new ClassAttribute(wt.folder.SubFolder.class,
wt.folder.SubF
older.NAME),
new int[] { folderIndex } , false);
query.appendSelect(new ClassAttribute(wt.part.WTPart.class,
wt.part.WTPart.NAME),
new int[] { partIndex }, false);
query.appendJoin(linkIndex, wt.folder.FolderMembership.FOLDER_ROLE,
folderIndex);
query.appendJoin(linkIndex, wt.folder.FolderMembership.MEMBER_ROLE,
partIndex);