MyBatis proporciona funciones de consulta de asociación avanzada, que pueden mapear fácilmente los conjuntos de resultados obtenidos por la base de datos a los frijoles Java definidos. El siguiente es un ejemplo para mostrar cómo mybatis trata con mapeos de complejos de relaciones comunes de uno a muchos a uno.
Diseñe un sistema de blog simple donde un usuario puede abrir múltiples blogs, publicar artículos en el blog, permitir comentarios y etiquetar artículos. El sistema de blog consiste principalmente en las siguientes tablas:
Tabla del autor: tabla de información del autor, registrar la información del autor, nombre de usuario y contraseña, dirección de correo electrónico, etc.
Table de blog: Blog Table, un autor puede abrir múltiples blogs, es decir, la relación entre el autor y el blog es de uno a muchos.
Tabla de publicación: tabla de registro del artículo, grabación del tiempo de publicación posterior, título, texto y otra información; Puede haber muchos artículos en un blog, y la relación entre el blog y la publicación es de uno a muchos.
Tabla de comentarios: tabla de comentarios del artículo, comentarios de artículo, un artículo puede tener muchos comentarios: la correspondencia entre publicaciones y comentarios es de uno a muchos.
Tabla de etiqueta: tabla de etiquetas, que representa la clasificación de etiqueta del artículo. Un artículo puede tener múltiples etiquetas, y una etiqueta se puede aplicar a diferentes artículos, por lo que la relación entre la etiqueta y la publicación es una relación de muchos a muchos; (La relación de muchos a muchos entre la etiqueta y la publicación se refleja a través de la tabla post_tag)
Tabla post_tag: registra la correspondencia entre artículos y etiquetas.
En términos generales, crearemos un Javabean (o POJO) correspondiente basado en la estructura de cada tabla para completar la operación básica de la tabla.
La definición de Javabean anterior de una sola tabla a veces no puede satisfacer las necesidades comerciales. En los negocios, un objeto de blog debe tener información sobre su autor y una lista de artículos, como se muestra en la figura a continuación:
Si desea obtener una instancia de dicha clase, necesita al menos algunos pasos:
1. Consulte la información del blog a través de la identificación del blog en la tabla de blogs y asigne la consulta BlogID y el título del objeto del blog;
2. Según el AuthorId de consulta en la información del blog, vaya a la tabla del autor para obtener la información del autor correspondiente, obtener el objeto del autor y luego asignarla al objeto del blog;
3. Según BlogID, consulte la lista de artículos de publicación correspondiente en la tabla de publicaciones y asigne el objeto de la lista <Post> al objeto Blog;
De esta manera, se llaman al menos tres declaraciones de consulta en la capa inferior. Consulte el siguiente código:
/** Obtenga el objeto BlogInfo a través de BlogID*/ public static BlogInfo OrganicQueryontest (String BlogId) {BigDecimal id = new BigDecimal (BlogID); Sqlsession session = sqlSessionFactory.opensession (); BlogInfo BlogInfo = new BlogInfo (); // 1. Consulte el objeto del blog de acuerdo con BlogID y establezca el valor en blogInfo Blog Blog = (Blog) Session.Selectone ("com.foo.bean.blogmapper.selectbyprimarykey", id); BlogInfo.setBlogid (Blog.getBlogID ()); BlogInfo.settitle (Blog.gettitle ()); // 2. Según el AuthorId en el blog, ingrese la base de datos para consultar la información del autor y establecer el resultado en el objeto BlogInfo Autor autor = (Autor) Session.Selectone ("com.foo.bean.authormapper.selectbyprimarykey", blog.getAuthorid ()); BlogInfo.setAuthor (autor); // 3. Consulte el objeto Publics y configúrelo en publicaciones de la lista BlogInfo = session.selectList ("com.foo.bean.postmapper.selectbyblogid", blog.getBlogID ()); BlogInfo.SetPosts (publicaciones); // Imprima el objeto en forma de una cadena JSON JSONObject Object = new JsonObject (BlogInfo); System.out.println (object.ToString ()); devolver blogInfo; }En el código anterior, podemos ver que es más problemático obtener un objeto BlogInfo. Debe llamar a la consulta de la base de datos tres veces en total, obtener la información requerida y luego ensamblar el objeto BlogInfo.
Consulta de declaración anidada
MyBatis proporciona un mecanismo llamado consulta de declaración anidada, que puede simplificar enormemente las operaciones anteriores y agregar configuración y código de la siguiente manera:
<resultmap type = "com.foo.bean.bloginfo" id = "bloginfo"> <id columna = "blog_id" propiedad = "blogid" /> <resultado columna = "title" propiedad = "title" /> <asociación propiedad = "autor" columna = "blog_author_id" javatype = "com.foo.bean.author" select = "com.foo.bean.authormapper.selectbyprimarykey"> </sociation> <colección de propiedades = "posts" column = "blog_id" oftype = "com.foo.bean.post" select = "com.foo.bean.postmapper.selectbyblogid"> </colección> </resultadoMap> <ectelect Id = "querybloginfinfinfinfinfinfin resultMap = "BlogInfo" Parametertype = "java.math.bigDecimal"> Seleccione B.Blog_id, B.Title, B.Author_id como Blog_author_id de Louluan.blog B Where B.Blog_id = #{BlogID, JDBCType = Decimal} </select> /** Obtener objeto BlogInfo a través de BlogID*/ public static BlogInfo NestedQueryontest (String BlogID) {BigDecimal id = new BigDecimal (BlogID); Sqlsession session = sqlSessionFactory.opensession (); BlogInfo BlogInfo = new BlogInfo (); BlogInfo = (BlogInfo) Session.Selectone ("com.foo.bean.blogmapper.QueryBlogInfobyid", id); JSONObject Object = new JsonObject (BlogInfo); System.out.println (object.ToString ()); devolver blogInfo; }La consulta anterior se puede realizar plenamente a través del código anterior. Aquí solo necesitamos blogInfo = (BlogInfo) Session.Selectone ("com.foo.bean.blogmapper.queryBloginfobyid", id); En el código, podemos obtener un objeto BlogInfo complejo.
El principio de consulta de declaración anidada
En el código anterior, MyBatis realizará el siguiente proceso:
1. Primero ejecute la declaración correspondiente de QueryBlogInfobyid para obtener el resultado del conjunto de resultados establecido de la tabla de blogs;
2. Saque el siguiente registro válido de ResultSet y luego cree el objeto BlogInfo correspondiente basado en las especificaciones de mapeo definidas por el resultado de resultados.
3. Cuando desee asignar el atributo del autor en BlogInfo, encuentra que hay una consulta asociada. En este momento, MyBatis ejecutará por primera vez la instrucción SELECT de consulta para obtener el resultado devuelto y establecer el resultado en el atributo de autor de BlogInfo;
4. Al asignar las publicaciones de BlogInfo, también están presentes procesos similares.
5. Repita 2 pasos hasta que el resultado sea de resultado. next () == falso;
El siguiente es un diagrama esquemático del proceso de asignación de construcción de objetos BlogInfo:
Una muy buena función de este tipo de consulta anidada asociada es que puede reutilizar la declaración selecta y construir objetos complejos a través de la combinación entre declaraciones selectas simples. Las dos declaraciones seleccionadas anidadas arriba com.foo.bean.authormapper.selectbyprimarykey y com.foo.bean.postmapper.selectbyblogid se pueden usar de forma independiente.
N+1 Problema
Sus desventajas también son bastante obvias: el llamado problema N+1. La consulta anidada asociada muestra un conjunto de resultados, y luego la consulta asociada se realiza en función de cada registro de este conjunto de resultados.
Ahora suponga que solo hay una consulta anidada (es decir, hay una etiqueta de asociación dentro del resultado de resultados), y el número de entradas devueltas por la consulta es n, entonces la instrucción de consulta asociada se ejecutará n veces, además de la consulta en sí devuelve el conjunto de resultados en la consulta una vez, y se requiere un total de N+1 veces para acceder a la base de datos. Si N es relativamente grande, ¡este consumo de acceso a la base de datos es muy grande! Por lo tanto, los usuarios que usan este tipo de consulta de declaración anidada deben considerar cuidadosamente para garantizar que el valor de N no sea muy grande.
Como ejemplo, la instrucción SELECT devolverá un conjunto de resultados con com.foo.bean.blogmapper.queryBloginfobyid con 1. Dado que tiene dos consultas de declaración relacionadas, debe acceder a la base de datos 1* (1+1) = 3 veces en total.
Consulta de resultados anidada
La consulta de declaraciones anidadas puede causar tiempos de acceso de base de datos inciertos, lo que puede afectar el rendimiento. MyBatis también admite una especie de consulta de resultados anidada: es decir, para la consulta de uno a muchos, de muchos a muchos, y muchas a uno, MyBatis busca los resultados de la base de datos a la vez a través de la consulta conjunta, y luego convierte los resultados basados en su objetos individuales, muchos a uno, muchos a muchos a su relación y configuración en el resultado de resultados, y crea los objetos requeridos.
Redefine el mapa de resultados de BlogInfo
<resultmap type = "com.foo.bean.bloginfo" id = "bloginfo"> <id columna = "blog_id" propiedad = "blogid"/> <resultado columna = "title" Property = "title"/> <asociación propiedad = "autor" column = "blog_author_id" javatype = "com.foo.bean.author"> <d columna = "autory" autory "autory" autory "autor" Autor "Authorid"/"Authorid" Autor "Authorid" Authorid "Authorid"/"Authorid" Autor "Authorid". columna = "user_name" Propiedad = "UserName"/> <resultado columna = "contraseña" propiedad = "contraseña"/> <resultado columna = "correo electrónico" propiedad = "correo electrónico"/> <resultado columna = "biografía" propiedad = "biografía"/> </asociación> <colección propiedad = "publica" columna = "blog_post_id" oftype = "com.foo.bean.pan.post"> <d columna = "columna =" blog_post_id "oftype =" com.foo.bean.post. <resultado columna = "Blog_id" Property = "Blogid"/> <resultado columna = "create_time" Property = "CreateTime"/> <resultado columna = "sujeto" Propiedad = "Sujeto"/> <Result Column = "Body" Property = "Body"/> <Result Column = "Property" Property = "Draft"/> </ Collection> </sultMap>
Las declaraciones SQL correspondientes son las siguientes:
<select id = "QUIERYALLBLOGINFO" resultMap = "BlogInfo"> SELECT B.Blog_id, B.Title, B.Author_id como Blog_author_id, A.Author_id, a.user_name, a.password, A.EMAIL, A.Biography, p.post_id, P.Blog_id como Blog_Post_id, P.Create_Time, P.Mody, p.post_id, p.body. B Autor de unión externa izquierda A en b.author_id = a.author_id Izquierda Outer Join Post P en p.blog_id = b.blog_id </select>
/** Obtenga toda la información sobre todos los blogs*/ public static BlogInfo NestedResultonTest () {sqlsession session = sqlsessionFactory.opensession (); BlogInfo BlogInfo = new BlogInfo (); BlogInfo = (BlogInfo) Session.Selectone ("com.foo.bean.blogmapper.QueryallBloginfo"); JSONObject Object = new JsonObject (BlogInfo); System.out.println (object.ToString ()); devolver blogInfo; } Pasos de ejecución para la consulta de resultados anidados:
1. Realice las operaciones de unión basadas en la relación correspondiente de la tabla para obtener el conjunto de resultados;
2. Según la información del conjunto de resultados y la información de definición de resultados de resultados de BlogInfo, ensamble y asigne el resultado devuelto establecido en la memoria para construir BlogInfo;
3. Devuelve la lista de resultados construida <Crementainfo> Resultado.
Para la consulta de resultados asociada, si se trata de una relación de muchos a uno, está configurada por la <sociación de asociación = "Autor" columna = "Blog_author_id" javatype = "com.foo.bean.author">. MyBatis obtendrá datos de la memoria a través del valor Author_ID correspondiente a la propiedad de la columna y los encapsulará en un objeto de autor;
Si se trata de una relación de uno a muchos, al igual que la relación entre el blog y la publicación, está configurada por <Propiedad de colección = "Publicar" columna = "Blog_post_id" oftype = "com.foo.bean.post">, MyBatis usa Blog_id para obtener objetos de publicación en la memoria y encapsularlos en la lista <Post>;
Para consultar los resultados asociados, solo necesita consultar la base de datos una vez, y luego la integración y el ensamblaje de los resultados se colocan en la memoria.
Lo anterior es demostrar el procesamiento de objetos de uno a muchos y muchos a uno al consultar toda la información del blog.
PS: Ejemplo de mapeo de autoasociación:
Clase de entidad
Módulo de clase pública {private int id; clave de cadena privada; nombre de cadena privada; Módulo privado ParentModule; Lista privada <module> ChildrenModules; URL de cadena privada; Private int sort; espectáculo de cuerdas privadas; cadena privada del; public int getId () {return id; } public void setid (int id) {this.id = id; } public String getKey () {Key de retorno; } public void setKey (tecla de cadena) {this.key = key; } public String getName () {nombre de retorno; } public void setName (nombre de cadena) {this.name = name; } módulo público getParentModule () {return ParentModule; } public void setParentModule (módulo parentModule) {this.parentModule = parentModule; } public String getUrl () {return url; } public void seturl (url de cadena) {this.url = url; } public int getSort () {return sort; } public void setSort (int sort) {this.sort = sort; } public String getShow () {return show; } public void setShow (string show) {this.show = show; } public String getDel () {return del; } public void setDel (String del) {this.del = del; } Lista pública <module> getChildrenModules () {return childrenModules; } public void setChildrenModules (List <Module> ChildrenModules) {this.ChildrenModules = ChildrenModules; }} Código XML: <mappper namespace = "com.sagAware.caraccess.mapper.moduleMapper"> <resultmap type = "module" id = "moduleResultMap"> <id propiedad = "id" columna = "module_id"/> <resulte propiedad = "key" columna = "module_key"/> <resulte propiedad = "name" columna = "module_name"/>/> "Results =" url "" url "url" url "url" url "url" url "módica" columna = "Module_url"/> <resultado propiedad = "sort" columna = "Module_sort"/> <resultado Property = "show" column = "module_show"/> <resultado propiedad = "del" columna = "module_del"/> <!-consulta el module principal-> <asociación propiedad = "parentmodule" column = "module_parent_id" select = "getmodulesbyid"/-quyy-quymodul Submodule-> <Collection Property = "ChildrenModules" columna = "Module_id" select = "getChildrenModules"/> </ resultadoMap> <select id = "getModules" parametertype = "string" resultmap = "ModuleSeSeSultMap"> Seleccionar * de TB_Module donde Module_id = 2 </select> <select> Id = "getModulesbyid" INTERTYTETiTiTiTiTiMe "INTENT =" INTERTITE "INTERTITE" INTERTITE "INTERTITE" INTERTITE "INTERTITE" INTERTITOTOTiTiTiTiME " resultMap = "moduleResultMap"> select * de tb_module donde module_id = #{module_id} </select> <select id = "getChildrenModes" parametertype = "int" result "moduleResultMap"> select * from tb_module where module_parent_id = #{module_id} </select> </select>