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 @QueryField
para 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 onlyParent
campo se asigna true
como , la declaración de consulta correspondiente se puede obtener de la anotación como
id IN (SELECT parent_id FROM menu)
复制代码
Concatenar SELECT
las 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 @SubQuery
anotación para redefinir el campo:
public class MenuQuery extends PageQuery {
@SubQuery(select = "parent_id", from = "menu")
private boolean onlyParent;
}
复制代码
@SubQuery
El 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 @SubQuery
generalmente no es suficiente y las anotaciones de Java no admiten el anidamiento automático, por lo que debemos definir dos anotaciones @NestedQueries
y @NestedQuery
usar 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;
}
复制代码
@NestedQueries
El 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 @NestedQueries
para 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 boolean
tipo al tipo .onlyParent
String
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
类型。