Kong Yijiに漢字の書き方を聞いてみると、4つあると言われます。
サブクエリステートメントのマッピングメソッドの数を尋ねたい場合は、3つまたは5つをリストします。
事業背景
たとえば、このようなメニューテーブルがあり、各メニューレコードには、その親メニューのIDを指すparentIdがあり、Entityオブジェクトは次のように定義されています。
@Table(name = "menu")
public class MenuEntity {
@Id
@GeneratedValue
private Integer id;
private Integer parentId;
private String menuName;
// getters and setters
}
复制代码
次に、このSQLを使用してすべての親メニューをクエリします。
SELECT * FROM menu WHERE id IN (SELECT parent_id FROM menu)
复制代码
では、このサブクエリはフィールドごとにどのようにマッピングされますか?
方法1:@QueryField
一般的なアノテーションを直接使用して@QueryField
、アノテーション内のクエリステートメントをそのまま定義します。
public class MenuQuery extends PageQuery {
@QueryField(and = "id IN (SELECT parent_id FROM menu)")
private boolean onlyParent;
}
复制代码
onlyParent
フィールドがtrue
として場合、対応するクエリステートメントは注釈から次のように取得できます。
id IN (SELECT parent_id FROM menu)
复制代码
SELECT
取得する文を連結します。
SELECT * FROM menu WHERE id IN (SELECT parent_id FROM menu)
复制代码
方法2:@SubQuery
次に、いくつかの最適化を行い、サブクエリステートメントのいくつかの変数を抽出し、新しいアノテーションを定義しましょう@SubQuery
。
@Target(FIELD)
@Retention(RUNTIME)
public @interface SubQuery {
String column() default "id";
String op() default "IN";
String select();
String from();
}
复制代码
次に、@SubQuery
注釈を使用してフィールドを再定義します。
public class MenuQuery extends PageQuery {
@SubQuery(select = "parent_id", from = "menu")
private boolean onlyParent;
}
复制代码
@SubQuery
注釈に対応するサブクエリステートメントの形式は次のとおりです。
#{column} #{op} (SELECT #{select} FROM #{from})
复制代码
id
注釈を定義するときに割り当てられた値を代入して、、、IN
をparent_id
取得しmenu
ます。
id IN (SELECT parent_id FROM menu)
复制代码
方法3:@ NestedQueries / @ NestedQuery
サブクエリには複数レベルのネストが必要なことが多く、@SubQuery
通常は1つでは不十分であり、Javaアノテーションは自己ネストをサポートしていないため、2つのアノテーション@NestedQueries
とを定義し@NestedQuery
、配列を使用してネストロジックを表現する必要があります。
@Target({})
public @interface NestedQuery {
String select();
String from();
/**
* Will use next @NestedQuery.select() as column if empty.
*
* @return custom column for next nested query.
*/
String where() default "";
String op() default "IN";
}
@Target(FIELD)
@Retention(RUNTIME)
public @interface NestedQueries {
String column() default "id";
String op() default "IN";
NestedQuery[] value();
boolean appendWhere() default true;
}
复制代码
@NestedQueries
注釈に対応するサブクエリステートメントの形式は次のとおりです。
#{column} #{op} (
SELECT #{select} FROM #{from} [#{where} #{op} (
SELECT #{select} FROM #{from} ...)]
)
复制代码
代わり@NestedQueries
にサブクエリを定義するために使用します
public class MenuQuery extends PageQuery {
@NestedQueries({
@NestedQuery(select = "parent_id", from = "menu")
})
private boolean onlyParent;
}
复制代码
同じことが得られます:
id IN (SELECT parent_id FROM menu)
复制代码
拡張1:最後のサブクエリのクエリ条件を追加します
親メニューをメニュー名でクエリしたい場合、SQLステートメントは次のようになります。
SELECT * FROM menu WHERE id IN (SELECT parent_id FROM menu WHERE menu_name = ?)
复制代码
boolean
typeonlyParent
フィールドをString
typeに変更する必要がありmenuName
ます。
public class MenuQuery extends PageQuery {
@NestedQueries({
@NestedQuery(select = "parent_id", from = "menu")
})
private String menuName;
}
复制代码
但是这里用于子查询定义的menuName
会和普通查询的menuName
字段冲突,于是有:
拓展二:将子查询的字段定义为Query对象
在MenuQuery
中将@NestedQueries
注解到新添加的MenuQuery
类型的menu
字段上用于子查询的映射,再添加一个String
类型的menuName
,用于子查询的查询条件。
public class MenuQuery extends PageQuery {
@NestedQueries({
@NestedQuery(select = "parent_id", from = "menu")
})
private MenuQuery menu;
private String menuName;
}
复制代码
再这样构造一个MenuQuery对象即可:
MenuQuery parentBySubMenu = MenuQuery.builder().menu(MenuQuery.builder().menuName("test").build()).build();
复制代码
解答
现在回到《基于查询对象字段的后缀推导》这篇文章最后提出的问题,这种复杂的嵌套查询如何映射呢?
SELECT * FROM t_perm WHERE id IN (
SELECT permId FROM t_role_and_perm WHERE roleId IN (
SELECT roleId FROM t_user_and_role WHERE userId IN (
SELECT id FROM t_user WHERE username = ?
)))
复制代码
根据上面所讲的注解和方法,对应的查询字段可以定义如下:
public class PermQuery extends PageQuery {
@NestedQueries({
@NestedQuery(select = "permId", from = "t_role_and_perm"),
@NestedQuery(select = "roleId", from = "t_user_and_role", where = "userId"),
@NestedQuery(select = "id", from = "t_user")
})
private String username;
}
复制代码
小结
本篇主要讲解了子查询映射的优化过程,目前的最佳方案为使用@NestedQueries
注解以实现子查询的映射,字段的类型可以选择boolean
类型,普通类型或者Query
类型。