Varios métodos de asignación de subconsultas

Pregúntale a Kong Yiji cuántas formas de escribir el carácter chino y te dirá que hay cuatro.

Si quiere preguntarme cuántos métodos de mapeo para declaraciones de subconsultas, le enumeraré tres o cinco.

conocimiento de los negocios

Por ejemplo, tenemos una tabla de menú de este tipo, cada registro de menú tiene un parentId que apunta al id de su menú principal, y el objeto Entity se define de la siguiente manera:

@Table(name = "menu")
public class MenuEntity {
    @Id
    @GeneratedValue
    private Integer id;
    private Integer parentId;
    private String menuName;
    // getters and setters
}
复制代码

Ahora queremos consultar todos los menús principales usando este SQL:

SELECT * FROM menu WHERE id IN (SELECT parent_id FROM menu)
复制代码

Entonces, ¿cómo se mapea esta subconsulta por campos?

Método 1: @QueryField

Use directamente la anotación general @QueryFieldpara definir la declaración de consulta en la anotación tal como es.

public class MenuQuery extends PageQuery {
    @QueryField(and = "id IN (SELECT parent_id FROM menu)")
    private boolean onlyParent;
}
复制代码

Cuando el onlyParentcampo se asigna truecomo , la declaración de consulta correspondiente se puede obtener de la anotación como

id IN (SELECT parent_id FROM menu)
复制代码

Concatenar SELECTlas oraciones para obtener:

SELECT * FROM menu WHERE id IN (SELECT parent_id FROM menu)
复制代码

Método 2: @SubQuery

Hagamos algo de optimización a continuación, extraigamos algunas variables de la declaración de la subconsulta y definamos una nueva anotación @SubQuery:

@Target(FIELD)
@Retention(RUNTIME)
public @interface SubQuery {
    String column() default "id";
    String op() default "IN";
    String select();
    String from();
}
复制代码

Luego use la @SubQueryanotación para redefinir el campo:

public class MenuQuery extends PageQuery {
    @SubQuery(select = "parent_id", from = "menu")
    private boolean onlyParent;
}
复制代码

@SubQueryEl formato de la sentencia de subconsulta correspondiente a la anotación es:

#{column} #{op} (SELECT #{select} FROM #{from})
复制代码

Sustituya el valor asignado al definir la anotación id, IN, parent_id, para obtener menu:

id IN (SELECT parent_id FROM menu)
复制代码

Método 3: @NestedQueries/@NestedQuery

Las subconsultas a menudo requieren varios niveles de anidamiento, uno @SubQuerygeneralmente no es suficiente y las anotaciones de Java no admiten el anidamiento automático, por lo que debemos definir dos anotaciones @NestedQueriesy @NestedQueryusar matrices para expresar la lógica de anidamiento.


@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;
}
复制代码

@NestedQueriesEl formato de la sentencia de subconsulta correspondiente a la anotación es:

#{column} #{op} (
  SELECT #{select} FROM #{from} [#{where} #{op} (
     SELECT #{select} FROM #{from} ...)]
)
复制代码

Úselo en su lugar @NestedQueriespara definir subconsultas

public class MenuQuery extends PageQuery {
    @NestedQueries({
            @NestedQuery(select = "parent_id", from = "menu")
    })
    private boolean onlyParent;
}
复制代码

Lo mismo se puede obtener:

id IN (SELECT parent_id FROM menu)
复制代码

Expansión 1: agregar condiciones de consulta para la última subconsulta

Supongamos que quiero consultar su menú principal por nombre de menú, la instrucción SQL puede ser así:

SELECT * FROM menu WHERE id IN (SELECT parent_id FROM menu WHERE menu_name = ?)
复制代码

Solo tenemos que cambiar el campo de booleantipo al tipo .onlyParentStringmenuName

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类型。

Supongo que te gusta

Origin juejin.im/post/7079638411891441677
Recomendado
Clasificación