サブクエリのいくつかのマッピング方法

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注釈を定義するときに割り当てられた値を代入して、、、INparent_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 = ?)
复制代码

booleantypeonlyParentフィールドをStringtypeに変更する必要があり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类型。

おすすめ

転載: juejin.im/post/7079638411891441677