|
1 | 1 | <?xml version="1.0" encoding="UTF-8"?>
|
2 | 2 | <!--
|
3 |
| - Copyright 2010-2012 the original author or authors. |
| 3 | + Copyright 2010-2013 the original author or authors. |
4 | 4 |
|
5 | 5 | Licensed under the Apache License, Version 2.0 (the "License");
|
6 | 6 | you may not use this file except in compliance with the License.
|
|
27 | 27 | </properties>
|
28 | 28 |
|
29 | 29 | <body>
|
30 |
| - <section name="Statement Builders"> |
31 |
| - <subsection name="SelectBuilder"> |
32 |
| - <p>Una de las cosas más tediosas que un programador Java puede llegar a tener que hacer es incluir código SQL en código Java. Normalmente esto se hace cuando es necesario generar dinámicamente el SQL – de otra forma podrías externalizar el código en un fichero o un procedimiento almacenado. Como ya has visto, MyBatis tiene una respuesta potente a la generación dinámica de SQL mediante las capacidades del mapeo XML. Sin embargo, en ocasiones se hace necesario construir una sentencia SQL dentro del código Java. En este caso, MyBatis tiene una funcionalidad más para ayudarte en ello, antes de que comiences con el típico lío de signos de suma, comillas, líneas nuevas, problemas de formato y condicionales anidados para tratar con las comas extra y las conjunciones AND… Realmente, generar código dinámico en java, puede ser una verdadera pesadilla.</p> |
33 |
| - <p>MyBatis 3 introduce un concepto un tanto distinto para tratar con el problema. Podríamos haber creado simplemente una clase que te permitiera invocar a métodos para crear una sentencia SQL paso a paso. En su lugar hemos intentado proporcionar algo distinto. El resultado está más próximo a un Domain Specific Languaje de lo que java jamás estará en su estado actual…</p> |
34 |
| - <p><strong>Los secretos de SelectBuilder</strong></p> |
35 |
| - <p>La clase SelectBuilder no es mágica, y tampoco pasa nada si no sabes cómo funciona internamente. Así que veamos sin más preámbulo qué es lo que hace. SelectBuilder usa una combinación de imports estáticos y una variable ThreadLocal para permitir una sintaxis más limpia que permite entrelazar condicionales y se encarga del formateo de SQL por ti. Te permite crear métodos como este:</p> |
36 |
| -<source><![CDATA[public String selectBlogsSql() { |
37 |
| - BEGIN(); // Clears ThreadLocal variable |
38 |
| - SELECT("*"); |
39 |
| - FROM("BLOG"); |
40 |
| - return SQL(); |
41 |
| -}]]></source> |
42 |
| - <p>Este es un ejemplo bastante sencillo que posiblemente habrías preferido construir en estático. Aquí hay uno más complicado:</p> |
43 |
| -<source><![CDATA[private String selectPersonSql() { |
44 |
| - BEGIN(); // Clears ThreadLocal variable |
45 |
| - SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME"); |
46 |
| - SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON"); |
47 |
| - FROM("PERSON P"); |
48 |
| - FROM("ACCOUNT A"); |
49 |
| - INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID"); |
50 |
| - INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID"); |
51 |
| - WHERE("P.ID = A.ID"); |
52 |
| - WHERE("P.FIRST_NAME like ?"); |
53 |
| - OR(); |
54 |
| - WHERE("P.LAST_NAME like ?"); |
55 |
| - GROUP_BY("P.ID"); |
56 |
| - HAVING("P.LAST_NAME like ?"); |
57 |
| - OR(); |
58 |
| - HAVING("P.FIRST_NAME like ?"); |
59 |
| - ORDER_BY("P.ID"); |
60 |
| - ORDER_BY("P.FULL_NAME"); |
61 |
| - return SQL(); |
62 |
| -}]]></source> |
63 |
| - <p>Construir el SQL de arriba concatenando Strings sería complicado. Por ejemplo:</p> |
64 |
| -<source><![CDATA["SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, " |
| 30 | + <section name="The SQL Builder Class"> |
| 31 | + <subsection name="El Problema"> |
| 32 | + <p>Una de las cosas más tediosas que un programador Java puede llegar a tener que hacer es incluir código SQL en código Java. Normalmente esto se hace cuando es necesario generar dinámicamente el SQL – de otra forma podrías externalizar el código en un fichero o un procedimiento almacenado. Como ya has visto, MyBatis tiene una respuesta potente a la generación dinámica de SQL mediante las capacidades del mapeo XML. Sin embargo, en ocasiones se hace necesario construir una sentencia SQL dentro del código Java. En este caso, MyBatis tiene una funcionalidad más para ayudarte en ello, antes de que comiences con el típico lío de signos de suma, comillas, líneas nuevas, problemas de formato y condicionales anidados para tratar con las comas extra y las conjunciones AND… Realmente, generar código dinámico en java, puede ser una verdadera pesadilla. Por ejemplo:</p> |
| 33 | +<source><![CDATA[ |
| 34 | +String sql = "SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, " |
65 | 35 | "P.LAST_NAME,P.CREATED_ON, P.UPDATED_ON " +
|
66 | 36 | "FROM PERSON P, ACCOUNT A " +
|
67 | 37 | "INNER JOIN DEPARTMENT D on D.ID = P.DEPARTMENT_ID " +
|
|
71 | 41 | "GROUP BY P.ID " +
|
72 | 42 | "HAVING (P.LAST_NAME like ?) " +
|
73 | 43 | "OR (P.FIRST_NAME like ?) " +
|
74 |
| -"ORDER BY P.ID, P.FULL_NAME";]]></source> |
75 |
| - <p>Si prefieres esa sintaxis, no hay problema en que la uses. Sin embargo, es bastante propensa a errores. Fíjate la cuidadosa adición de un espacio en blanco al final de cada línea. Aun así, incluso si prefieres esta sintaxis, el siguiente ejemplo es decididamente más sencillo que la concatenación de Strings:</p> |
76 |
| -<source><![CDATA[private String selectPersonLike(Person p){ |
77 |
| - BEGIN(); // Clears ThreadLocal variable |
78 |
| - SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME"); |
79 |
| - FROM("PERSON P"); |
80 |
| - if (p.id != null) { |
81 |
| - WHERE("P.ID like #{id}"); |
82 |
| - } |
83 |
| - if (p.firstName != null) { |
84 |
| - WHERE("P.FIRST_NAME like #{firstName}"); |
85 |
| - } |
86 |
| - if (p.lastName != null) { |
87 |
| - WHERE("P.LAST_NAME like #{lastName}"); |
88 |
| - } |
89 |
| - ORDER_BY("P.LAST_NAME"); |
90 |
| - return SQL(); |
91 |
| -}]]></source> |
92 |
| - <p>¿Qué hay de especial en este ejemplo? Bien, si lo miras detenidamente, verás que no hay que preocuparse de duplicar “AND”s, o elegir entre “WHERE” o “AND”, o ninguno de ambos! La sentencia de arriba crea una “query by example” (query a partir de un ejemplo) para todos los registros de PERSON, algunos con un like por el parámetro ID, otros por el parámetro firstName o por lastName - o cualquier combinación de ellos. La SelectBuilder se encarga de saber cuándo es necesario añadir un “WHERE”, cuando debes añadirse un “AND” y también de la concatenación de Strings. Y lo mejor de todo es que lo hace casi de forma independiente de cómo hayas llamado los métodos (la única excepción es el método OR()).</p> |
93 |
| - <p>Los dos métodos que puede que te hayan llamado la atención son: BEGIN() y SQL(). Simplificando, siempre debe comenzarse a usar el SelectBuilder llamado a BEGIN() y finalizar llamando a SQL(). Por supuesto puedes incluir métodos entre medio como lo requiera tu lógica pero el ámbito de la generación siempre comienza con un BEGIN() y acaba con un SQL(). El método BEGIN() limpia la variable ThreadLocal, para asegurar que no arrastras información de un estado anterior, y el método SQL() ensambla la sentencia SQL basándose en las llamadas que has ido haciendo desde la última llamada a BEGIN(). Debes saber que BEGIN() tiene un sinónimo llamado RESET() que hace exactamente lo mismo pero se lee mejor en ciertos contextos.</p> |
94 |
| - <p>Para usar el SelectBuilder como en los ejemplos de arriba simplemente tienes que importarlo estáticamente de la siguiente forma:</p> |
95 |
| -<source>import static org.apache.ibatis.jdbc.SelectBuilder.*;</source> |
96 |
| - <p>Una vez que se ha importado, la clase en la que estés trabajando tendrá accesibles todos los métodos del SelectBuilder. La lista completa de métodos es la siguiente:</p> |
97 |
| - <table> |
98 |
| - <thead> |
99 |
| - <tr><th><p>Method</p></th><th><p>Description</p></th></tr></thead> |
100 |
| - <tbody> |
101 |
| - <tr> |
102 |
| - <td> |
103 |
| - <code>BEGIN()</code> / <code>RESET()</code> |
104 |
| - </td> |
105 |
| - <td>Estos métodos limpian estado guardad en el ThreadLocal de la clase SelectBuilder, y la preparan para construir una nueva sentencia. BEGIN() se lee mejor cuando se está creando una sentencia. RESET() se lee mejor cuando se está borrando lo hecho anteriormente en medio de una ejecución (quizá porque la lógica necesita una sentencia completamente distinta según las condiciones).</td> |
106 |
| - </tr> |
| 44 | +"ORDER BY P.ID, P.FULL_NAME"; |
| 45 | +]]></source> |
| 46 | + </subsection> |
| 47 | + |
| 48 | + <subsection name="La Solución"> |
| 49 | + <p>MyBatis 3 introduce un concepto un tanto distinto para tratar con el problema. |
| 50 | + Con la clase SQL, puecdes crear una sentencia SQL en un sólo paso invocando a sus métodos. |
| 51 | + El ejemplo anterior tendría este aspecto si se rescribe con la clase SQL: |
| 52 | + </p> |
| 53 | + |
| 54 | + <source><![CDATA[ |
| 55 | +private String selectPersonSql() { |
| 56 | + return new SQL() {{ |
| 57 | + SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME"); |
| 58 | + SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON"); |
| 59 | + FROM("PERSON P"); |
| 60 | + FROM("ACCOUNT A"); |
| 61 | + INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID"); |
| 62 | + INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID"); |
| 63 | + WHERE("P.ID = A.ID"); |
| 64 | + WHERE("P.FIRST_NAME like ?"); |
| 65 | + OR(); |
| 66 | + WHERE("P.LAST_NAME like ?"); |
| 67 | + GROUP_BY("P.ID"); |
| 68 | + HAVING("P.LAST_NAME like ?"); |
| 69 | + OR(); |
| 70 | + HAVING("P.FIRST_NAME like ?"); |
| 71 | + ORDER_BY("P.ID"); |
| 72 | + ORDER_BY("P.FULL_NAME"); |
| 73 | + }}.toString(); |
| 74 | +} |
| 75 | +]]></source> |
| 76 | + <p>¿Qué hay de especial en este ejemplo? |
| 77 | + Bien, si lo miras detenidamente, verás que no hay que preocuparse de duplicar “AND”s, o elegir entre “WHERE” o “AND”, o ninguno de ambos! |
| 78 | + La clase SQL se ocupa de colocar el "WHERE" donde debe de ir, si debe usarse "AND" o no y de realizar todas las concatenaciones de Strings. |
| 79 | + </p> |
| 80 | + </subsection> |
| 81 | + <subsection name="La clase SQL"> |
| 82 | + |
| 83 | + <p>Aqui van algunos ejemplos:</p> |
| 84 | + |
| 85 | + <source><![CDATA[ |
| 86 | +// Anonymous inner class |
| 87 | +public String deletePersonSql() { |
| 88 | + return new SQL() {{ |
| 89 | + DELETE_FROM("PERSON"); |
| 90 | + WHERE("ID = ${id}"); |
| 91 | + }}.toString(); |
| 92 | +} |
| 93 | +
|
| 94 | +// Builder / Fluent style |
| 95 | +public String insertPersonSql() { |
| 96 | + String sql = new SQL() |
| 97 | + .INSERT_INTO("PERSON"); |
| 98 | + .VALUES("ID, FIRST_NAME", "${id}, ${firstName}"); |
| 99 | + .VALUES("LAST_NAME", "${lastName}") |
| 100 | + .toString(); |
| 101 | + return sql; |
| 102 | +} |
| 103 | +
|
| 104 | +// With conditionals (note the final parameters, required for the anonymous inner class to access them) |
| 105 | +public String selectPersonLike(final String id, final String firstName, final String lastName) { |
| 106 | + return new SQL() {{ |
| 107 | + SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME"); |
| 108 | + FROM("PERSON P"); |
| 109 | + if (id != null) { |
| 110 | + WHERE("P.ID like ${id}"); |
| 111 | + } |
| 112 | + if (firstName != null) { |
| 113 | + WHERE("P.FIRST_NAME like ${firstName}"); |
| 114 | + } |
| 115 | + if (lastName != null) { |
| 116 | + WHERE("P.LAST_NAME like ${lastName}"); |
| 117 | + } |
| 118 | + ORDER_BY("P.LAST_NAME"); |
| 119 | + }}.toString(); |
| 120 | +} |
| 121 | +
|
| 122 | +public String deletePersonSql() { |
| 123 | + return new SQL() {{ |
| 124 | + DELETE_FROM("PERSON"); |
| 125 | + WHERE("ID = ${id}"); |
| 126 | + }}.toString(); |
| 127 | +} |
| 128 | +
|
| 129 | +public String insertPersonSql() { |
| 130 | + return new SQL() {{ |
| 131 | + INSERT_INTO("PERSON"); |
| 132 | + VALUES("ID, FIRST_NAME", "${id}, ${firstName}"); |
| 133 | + VALUES("LAST_NAME", "${lastName}"); |
| 134 | + }}.toString(); |
| 135 | +} |
| 136 | +
|
| 137 | +public String updatePersonSql() { |
| 138 | + return new SQL() {{ |
| 139 | + UPDATE("PERSON"); |
| 140 | + SET("FIRST_NAME = ${firstName}"); |
| 141 | + WHERE("ID = ${id}"); |
| 142 | + }}.toString(); |
| 143 | +} |
| 144 | +]]></source> |
| 145 | + |
| 146 | + <table> |
| 147 | + <thead> |
| 148 | + <tr> |
| 149 | + <th>Metodo</th> |
| 150 | + <th>Descripción</th> |
| 151 | + </tr> |
| 152 | + </thead> |
| 153 | + <tbody> |
107 | 154 | <tr>
|
108 | 155 | <td>
|
109 | 156 | <code>SELECT(String)</code>
|
|
171 | 218 | </tr>
|
172 | 219 | <tr>
|
173 | 220 | <td>
|
174 |
| - <code>SQL()</code> |
| 221 | + <code>DELETE_FROM(String)</code> |
| 222 | + </td> |
| 223 | + <td>Comienza una sentencia delete y especifica la tabla donde borrar. Generalmente suele ir seguida de una clausula WHERE! |
175 | 224 | </td>
|
176 |
| - <td>Devuelve la SQL generada y restablece el estado del SelectBuilder (como si se hubiera llamado a un BEGIN() o a un RESET()). Por tanto este método solo se puede llamar una vez!</td> |
177 | 225 | </tr>
|
178 |
| - </tbody></table> |
179 |
| - </subsection> |
180 |
| - |
181 |
| - <subsection name="SqlBuilder"> |
182 |
| - <p>De forma similar a la SelectBuilder, MyBatis también proporciona una SqlBuilder generalizada. Incluye los métodos de SelectBuilder y también métodos para crear inserts, updates y deletes. Esta clase puede ser de utilidad para usarla en la creación de SQLs en DeleteProvider, InsertProvider o UpdateProvider (y también la SelectProvider)</p> |
183 |
| - <p>Para usar el SqlBuilder como en los ejemplos de arriba simplemente tienes que importarlo estáticamente de la siguiente forma:</p> |
184 |
| - <source>import static org.apache.ibatis.jdbc.SqlBuilder.*;</source> |
185 |
| - <p>SqlBuilder contiene todos los métodos de SelectBuilder, y estos métodos adicionales:</p> |
186 |
| - <table> |
187 |
| - <thead> |
188 |
| - <tr><th>Metodo</th><th>Descripción</th></tr> |
189 |
| - </thead> |
190 |
| - <tbody> |
191 |
| - <tr> |
192 |
| - <td> |
193 |
| - <code>DELETE_FROM(String)</code> |
194 |
| - </td> |
195 |
| - <td>Comienza una sentencia delete y especifica la tabla donde borrar. Generalmente suele ir seguida de una clausula WHERE! |
196 |
| - </td> |
197 |
| -</tr> |
198 | 226 | <tr>
|
199 | 227 | <td>
|
200 | 228 | <code>INSERT_INTO(String)</code>
|
|
220 | 248 | </td>
|
221 | 249 | <td>Añade a una sentencia insert. El primer parámetro es el nombre de columna y el Segundo el valor(es).</td>
|
222 | 250 | </tr>
|
223 |
| - </tbody> |
224 |
| - </table> |
| 251 | + </tbody> |
| 252 | + </table> |
| 253 | + |
| 254 | + </subsection> |
| 255 | + |
| 256 | + <subsection name="SqlBuilder y SelectBuilder (DEPRECADAS)"> |
| 257 | + <p> |
| 258 | + En versiones anteriores a la 3.2 optamos por una solución distinta, usando una variable ThreadLocal para |
| 259 | + resolver algunas limitaciones de las que hacen los DSLs Java algo incomodos. Sin embargo, esta solución está ahora |
| 260 | + desprecada porque los frameworks actuales están mas orientados a usar patrones builder-type y clases anónimas |
| 261 | + interas para este tipo de cosas. Por lo tanto las clases SelectBuilder y SqlBuilder están ahora deprecadas. |
| 262 | + </p> |
| 263 | + <p> |
| 264 | + Los siguientes métodos aplican solo a las clases deprecadas SqlBuilder y SelectBuilder. |
| 265 | + </p> |
| 266 | + <table> |
| 267 | + <thead> |
| 268 | + <tr> |
| 269 | + <th>Método</th> |
| 270 | + <th>Descripción</th> |
| 271 | + </tr> |
| 272 | + </thead> |
| 273 | + <tbody> |
| 274 | + <tr> |
| 275 | + <td> |
| 276 | + <code>BEGIN()</code> / <code>RESET()</code> |
| 277 | + </td> |
| 278 | + <td>Estos métodos limpian estado guardad en el ThreadLocal de la clase SelectBuilder, y la preparan para construir una nueva sentencia. BEGIN() se lee mejor cuando se está creando una sentencia. RESET() se lee mejor cuando se está borrando lo hecho anteriormente en medio de una ejecución (quizá porque la lógica necesita una sentencia completamente distinta según las condiciones).</td> |
| 279 | + </tr> |
| 280 | + <tr> |
| 281 | + <td> |
| 282 | + <code>SQL()</code> |
| 283 | + </td> |
| 284 | + <td>Devuelve la SQL generada y restablece el estado del SelectBuilder (como si se hubiera llamado a un BEGIN() o a un RESET()). Por tanto este método solo se puede llamar una vez!</td> |
| 285 | + </tr> |
| 286 | + </tbody> |
| 287 | + </table> |
| 288 | + |
| 289 | + <p>La clase SelectBuilder no es mágica, pero es importante que conozcas cómo funcionan. |
| 290 | + SelectBuilder y SqlBuilder usan una combinación de imports estáticos y una variable ThreadLocal para permitir una sintaxis más limpia más fácilmente usabe con condicionales. |
| 291 | + Para usarlas debes importar estáticamente métodos de las siguientes clases (uno u otro, no ambos):</p> |
225 | 292 |
|
226 |
| - <p>Aquí hay algunos ejemplos:</p> |
| 293 | + <source>import static org.apache.ibatis.jdbc.SelectBuilder.*;</source> |
| 294 | + <source>import static org.apache.ibatis.jdbc.SqlBuilder.*;</source> |
227 | 295 |
|
228 |
| -<source><![CDATA[public String deletePersonSql() { |
| 296 | + <p>De esta forma podrás crear métodos como estos:</p> |
| 297 | + |
| 298 | + <source><![CDATA[ |
| 299 | +/* DEPRECATED */ |
| 300 | +public String selectBlogsSql() { |
229 | 301 | BEGIN(); // Clears ThreadLocal variable
|
230 |
| - DELETE_FROM("PERSON"); |
231 |
| - WHERE("ID = ${id}"); |
| 302 | + SELECT("*"); |
| 303 | + FROM("BLOG"); |
232 | 304 | return SQL();
|
233 | 305 | }
|
| 306 | + ]]></source> |
234 | 307 |
|
235 |
| -public String insertPersonSql() { |
| 308 | + <source><![CDATA[ |
| 309 | +/* DEPRECATED */ |
| 310 | +private String selectPersonSql() { |
236 | 311 | BEGIN(); // Clears ThreadLocal variable
|
237 |
| - INSERT_INTO("PERSON"); |
238 |
| - VALUES("ID, FIRST_NAME", "${id}, ${firstName}"); |
239 |
| - VALUES("LAST_NAME", "${lastName}"); |
| 312 | + SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME"); |
| 313 | + SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON"); |
| 314 | + FROM("PERSON P"); |
| 315 | + FROM("ACCOUNT A"); |
| 316 | + INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID"); |
| 317 | + INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID"); |
| 318 | + WHERE("P.ID = A.ID"); |
| 319 | + WHERE("P.FIRST_NAME like ?"); |
| 320 | + OR(); |
| 321 | + WHERE("P.LAST_NAME like ?"); |
| 322 | + GROUP_BY("P.ID"); |
| 323 | + HAVING("P.LAST_NAME like ?"); |
| 324 | + OR(); |
| 325 | + HAVING("P.FIRST_NAME like ?"); |
| 326 | + ORDER_BY("P.ID"); |
| 327 | + ORDER_BY("P.FULL_NAME"); |
240 | 328 | return SQL();
|
241 | 329 | }
|
| 330 | + ]]></source> |
242 | 331 |
|
243 |
| -public String updatePersonSql() { |
244 |
| - BEGIN(); // Clears ThreadLocal variable |
245 |
| - UPDATE("PERSON"); |
246 |
| - SET("FIRST_NAME = ${firstName}"); |
247 |
| - WHERE("ID = ${id}"); |
248 |
| - return SQL(); |
249 |
| -}]]></source> |
250 |
| - </subsection> |
| 332 | + </subsection> |
251 | 333 | </section>
|
252 | 334 | </body>
|
253 | 335 |
|
|
0 commit comments