forked from mybatis/mybatis-3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsqlmap-xml.xml
1939 lines (1694 loc) · 76.4 KB
/
sqlmap-xml.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2009-2017 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
<properties>
<title>MyBatis 3 | Mapper XML 文件</title>
<author email="[email protected]">Clinton Begin</author>
<author email="[email protected]">Nan Lei</author>
<author email="[email protected]">Dongxu Wang</author>
</properties>
<body>
<section name="Mapper XML 文件">
<p>MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。</p>
<p>SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):</p>
<ul>
<li>
<code>cache</code>
– 给定命名空间的缓存配置。
</li>
<li>
<code>cache-ref</code>
– 其他命名空间缓存配置的引用。
</li>
<li>
<code>resultMap</code>
– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
</li>
<li>
<strike>
<code>parameterMap</code>
– 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
</strike>
</li>
<li>
<code>sql</code>
– 可被其他语句引用的可重用语句块。
</li>
<li>
<code>insert</code>
– 映射插入语句
</li>
<li>
<code>update</code>
– 映射更新语句
</li>
<li>
<code>delete</code>
– 映射删除语句
</li>
<li>
<code>select</code>
– 映射查询语句
</li>
</ul>
<p>下一部分将从语句本身开始来描述每个元素的细节。</p>
<subsection name="select" id="select">
<p>查询语句是 MyBatis 中最常用的元素之一,光能把数据存到数据库中价值并不大,如果还能重新取出来才有用,多数应用也都是查询比修改要频繁。对每个插入、更新或删除操作,通常对应多个查询操作。这是 MyBatis 的基本原则之一,也是将焦点和努力放到查询和结果映射的原因。简单查询的 select 元素是非常简单的。比如:
</p>
<source><![CDATA[<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>]]></source>
<p>这个语句被称作 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
</p>
<p>注意参数符号:</p>
<source><![CDATA[#{id}]]></source>
<p>这就告诉 MyBatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:
</p>
<source><![CDATA[// Similar JDBC code, NOT MyBatis…
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);]]></source>
<p>当然,这需要很多单独的 JDBC 的代码来提取结果并将它们映射到对象实例中,这就是 MyBatis 节省你时间的地方。我们需要深入了解参数和结果映射,细节部分我们下面来了解。
</p>
<p>select 元素有很多属性允许你配置,来决定每条语句的作用细节。
</p>
<source><![CDATA[<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">]]></source>
<table>
<caption>Select Attributes</caption>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>
在命名空间中唯一的标识符,可以被用来引用这条语句。
</td>
</tr>
<tr>
<td><code>parameterType</code></td>
<td>
将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
</td>
</tr>
<tr>
<td>
<strike>parameterMap</strike>
</td>
<td>
<strike>这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
</strike>
</td>
</tr>
<tr>
<td><code>resultType</code></td>
<td>从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
</td>
</tr>
<tr>
<td><code>resultMap</code></td>
<td>外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
</td>
</tr>
<tr>
<td><code>flushCache</code></td>
<td>将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
</td>
</tr>
<tr>
<td><code>useCache</code></td>
<td>将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
</td>
</tr>
<tr>
<td><code>timeout</code></td>
<td>这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
</td>
</tr>
<tr>
<td><code>fetchSize</code></td>
<td>这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
</td>
</tr>
<tr>
<td><code>statementType</code></td>
<td>STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
</td>
</tr>
<tr>
<td><code>resultSetType</code></td>
<td>FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
</td>
</tr>
<tr>
<td><code>databaseId</code></td>
<td>如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
</td>
</tr>
<tr>
<td><code>resultOrdered</code></td>
<td>这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:<code>false</code>。
</td>
</tr>
<tr>
<td><code>resultSets</code></td>
<td>这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。
</td>
</tr>
</tbody>
</table>
</subsection>
<subsection name="insert, update 和 delete" id="insert_update_and_delete">
<p>
数据变更语句 insert,update 和 delete 的实现非常接近:
</p>
<source><![CDATA[<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">]]></source>
<table>
<caption>Insert, Update, Delete 's Attributes</caption>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>命名空间中的唯一标识符,可被用来代表这条语句。</td>
</tr>
<tr>
<td><code>parameterType</code></td>
<td>将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
</td>
</tr>
<tr>
<td>
<strike><code>parameterMap</code></strike>
</td>
<td>
<strike>这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
</strike>
</td>
</tr>
<tr>
<td><code>flushCache</code></td>
<td>将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
</td>
</tr>
<tr>
<td><code>timeout</code></td>
<td>这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
</td>
</tr>
<tr>
<td><code>statementType</code></td>
<td>STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
</td>
</tr>
<tr>
<td><code>useGeneratedKeys</code></td>
<td>(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
</td>
</tr>
<tr>
<td><code>keyProperty</code></td>
<td>(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:<code>unset</code>。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
</td>
</tr>
<tr>
<td><code>keyColumn</code></td>
<td>(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
</td>
</tr>
<tr>
<td><code>databaseId</code></td>
<td>如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
</td>
</tr>
</tbody>
</table>
<p>下面就是 insert,update 和 delete 语句的示例:</p>
<source><![CDATA[<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>]]></source>
<p>如前所述,插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式。</p>
<p>首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了。例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:</p>
<source><![CDATA[<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>]]></source>
<p>
如果你的数据库还支持多行插入, 你也可以传入一个<code>Author</code>s数组或集合,并返回自动生成的主键。
</p>
<source><![CDATA[<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>]]></source>
<p>对于不支持自动生成类型的数据库或可能不支持自动生成主键 JDBC 驱动来说,MyBatis 有另外一种方法来生成主键。
</p>
<p>这里有一个简单(甚至很傻)的示例,它可以生成一个随机 ID(你最好不要这么做,但这里展示了 MyBatis 处理问题的灵活性及其所关心的广度):
</p>
<source><![CDATA[<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>]]></source>
<p>在上面的示例中,selectKey 元素将会首先运行,Author 的 id 会被设置,然后插入语句会被调用。这给你了一个和数据库中来处理自动生成的主键类似的行为,避免了使 Java 代码变得复杂。
</p>
<p>selectKey 元素描述如下:
</p>
<source><![CDATA[<selectKey
keyProperty="id"
resultType="int"
order="BEFORE"
statementType="PREPARED">]]></source>
<table>
<caption>selectKey Attributes</caption>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>keyProperty</code></td>
<td>selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
</td>
</tr>
<tr>
<td><code>keyColumn</code></td>
<td>匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
</td>
</tr>
<tr>
<td><code>resultType</code></td>
<td>结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。
</td>
</tr>
<tr>
<td><code>order</code></td>
<td>这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。
</td>
</tr>
<tr>
<td><code>statementType</code></td>
<td>与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。
</td>
</tr>
</tbody>
</table>
</subsection>
<subsection name="sql">
<p>这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:
</p>
<source><![CDATA[<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>]]></source>
<p>这个 SQL 片段可以被包含在其他语句中,例如:
</p>
<source><![CDATA[<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>]]></source>
<p>
属性值可以用于包含的refid属性或者包含的字句里面的属性值,例如:
</p>
<source><![CDATA[<sql id="sometable">
${prefix}Table
</sql>
<sql id="someinclude">
from
<include refid="${include_target}"/>
</sql>
<select id="select" resultType="map">
select
field1, field2, field3
<include refid="someinclude">
<property name="prefix" value="Some"/>
<property name="include_target" value="sometable"/>
</include>
</select>]]></source>
</subsection>
<subsection name="参数(Parameters)" id="Parameters">
<p>前面的所有语句中你所见到的都是简单参数的例子,实际上参数是 MyBatis 非常强大的元素,对于简单的做法,大概 90% 的情况参数都很少,比如:
</p>
<source><![CDATA[<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>]]></source>
<p>上面的这个示例说明了一个非常简单的命名参数映射。参数类型被设置为 <code>int</code>,这样这个参数就可以被设置成任何内容。原生的类型或简单数据类型(比如整型和字符串)因为没有相关属性,它会完全用参数值来替代。然而,如果传入一个复杂的对象,行为就会有一点不同了。比如:
</p>
<source><![CDATA[<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>]]></source>
<p>如果 User 类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找,然后将它们的值传入预处理语句的参数中。
</p>
<p>这点对于向语句中传参是比较好的而且又简单,不过参数映射的功能远不止于此。
</p>
<p>首先,像 MyBatis 的其他部分一样,参数也可以指定一个特殊的数据类型。
</p>
<source><![CDATA[#{property,javaType=int,jdbcType=NUMERIC}]]></source>
<p>像 MyBatis 的剩余部分一样,javaType 通常可以从参数对象中来去确定,前提是只要对象不是一个 HashMap。那么 javaType 应该被确定来保证使用正确类型处理器。
</p>
<p><span class="label important">NOTE</span> 如果 null 被当作值来传递,对于所有可能为空的列,JDBC Type 是需要的。你可以自己通过阅读预处理语句的 setNull() 方法的 JavaDocs 文档来研究这种情况。
</p>
<p>为了以后定制类型处理方式,你也可以指定一个特殊的类型处理器类(或别名),比如:
</p>
<source><![CDATA[#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}]]></source>
<p>尽管看起来配置变得越来越繁琐,但实际上是很少去设置它们。
</p>
<p>对于数值类型,还有一个小数保留位数的设置,来确定小数点后保留的位数。
</p>
<source><![CDATA[#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}]]></source>
<p>最后,mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像你在获取输出参数时所期望的那样。如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 来映射结果集到参数类型。要注意这里的 javaType 属性是可选的,如果左边的空白是 jdbcType 的 CURSOR 类型,它会自动地被设置为结果集。
</p>
<source><![CDATA[#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}]]></source>
<p>MyBatis 也支持很多高级的数据类型,比如结构体,但是当注册 out 参数时你必须告诉它语句类型名称。比如(再次提示,在实际中要像这样不能换行):
</p>
<source><![CDATA[#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}]]></source>
<p>尽管所有这些强大的选项很多时候你只简单指定属性名,其他的事情 MyBatis 会自己去推断,最多你需要为可能为空的列名指定 <code>jdbcType</code>。
</p>
<source><![CDATA[#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}]]></source>
<h4>
字符串替换
</h4>
<p>默认情况下,使用#{}格式的语法会导致 MyBatis 创建预处理语句属性并安全地设置值(比如?)。这样做更安全,更迅速,通常也是首选做法,不过有时你只是想直接在 SQL 语句中插入一个不改变的字符串。比如,像 ORDER BY,你可以这样来使用:
</p>
<source><![CDATA[ORDER BY ${columnName}]]></source>
<p>这里 MyBatis 不会修改或转义字符串。
</p>
<p>
<span class="label important">NOTE</span> 以这种方式接受从用户输出的内容并提供给语句中不变的字符串是不安全的,会导致潜在的 SQL 注入攻击,因此要么不允许用户输入这些字段,要么自行转义并检验。
</p>
</subsection>
<subsection name="Result Maps" id="Result_Maps">
<p>
resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果
集中取出数据的 JDBC 代码的那个东西,
而且在一些情形下允许你做一些 JDBC 不支持的事
情。
事实上,
编写相似于对复杂语句联合映射这些等同的代码,
也许可以跨过上千行的代码。
ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们
的关系。
</p>
<p>
你已经看到简单映射语句的示例了,但没有明确的 resultMap。比如:
</p>
<source><![CDATA[<select id="selectUsers" resultType="map">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>]]></source>
<p>
这样一个语句简单作用于所有列被自动映射到 HashMap 的键上,这由 resultType 属性
指定。这在很多情况下是有用的,但是 HashMap 不能很好描述一个领域模型。那样你的应
用程序将会使用 JavaBeans 或 POJOs(Plain Old Java Objects,普通 Java 对象)来作为领域
模型。MyBatis 对两者都支持。看看下面这个 JavaBean:
</p>
<source><![CDATA[package com.someapp.model;
public class User {
private int id;
private String username;
private String hashedPassword;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getHashedPassword() {
return hashedPassword;
}
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
}]]></source>
<p>
基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些
在 select 语句中会精确匹配到列名。
</p>
<p>
这样的一个 JavaBean 可以被映射到结果集,就像映射到 HashMap 一样简单。
</p>
<source><![CDATA[<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>]]></source>
<p>
要记住类型别名是你的伙伴。使用它们你可以不用输入类的全路径。比如:
</p>
<source><![CDATA[<!-- In mybatis-config.xml file -->
<typeAlias type="com.someapp.model.User" alias="User"/>
<!-- In SQL Mapping XML file -->
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>]]></source>
<p>
这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,基于属性名来映射列到
JavaBean 的属性上。如果列名没有精确匹配,你可以在列名上使用 select 字句的别名(一个
基本的 SQL 特性)来匹配标签。比如:
</p>
<source><![CDATA[<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>]]></source>
<p>
ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正的看到一个。这些简
单的示例不需要比你看到的更多东西。
只是出于示例的原因,
让我们来看看最后一个示例中
外部的 resultMap 是什么样子的,这也是解决列名不匹配的另外一种方式。
</p>
<source><![CDATA[<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>]]></source>
<p>
引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
</p>
<source><![CDATA[<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>]]></source>
<p>
如果世界总是这么简单就好了。
</p>
<h4>高级结果映射</h4>
<p>
MyBatis 创建的一个想法:数据库不用永远是你想要的或需要它们是什么样的。而我们
最喜欢的数据库最好是第三范式或 BCNF 模式,但它们有时不是。如果可能有一个单独的
数据库映射,所有应用程序都可以使用它,这是非常好的,但有时也不是。结果映射就是
MyBatis 提供处理这个问题的答案。
</p>
<p>
比如,我们如何映射下面这个语句?
</p>
<source><![CDATA[<!-- Very Complex Statement -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>]]></source>
<p>
你可能想把它映射到一个智能的对象模型,包含一个作者写的博客,有很多的博文,每
篇博文有零条或多条的评论和标签。
下面是一个完整的复杂结果映射例子
(假设作者,
博客,
博文,
评论和标签都是类型的别名) 我们来看看,
。
但是不用紧张,
我们会一步一步来说明。
当天最初它看起来令人生畏,但实际上非常简单。
</p>
<source><![CDATA[<!-- Very Complex Result Map -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>]]></source>
<p>
resultMap 元素有很多子元素和一个值得讨论的结构。
下面是 resultMap 元素的概念视图
</p>
<h4>resultMap</h4>
<ul>
<li>
<code>constructor</code> - 类在实例化时,用来注入结果到构造方法中
<ul>
<li><code>idArg</code> - ID 参数;标记结果作为 ID 可以帮助提高整体效能</li>
<li><code>arg</code> - 注入到构造方法的一个普通结果</li>
</ul>
</li>
<li><code>id</code> – 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能</li>
<li><code>result</code> – 注入到字段或 JavaBean 属性的普通结果</li>
<li>
<code>association</code> – 一个复杂的类型关联;许多结果将包成这种类型
<ul>
<li>嵌入结果映射 – 结果映射自身的关联,或者参考一个
</li>
</ul>
</li>
<li>
<code>collection</code> – 复杂类型的集
<ul>
<li>嵌入结果映射 – 结果映射自身的集,或者参考一个</li>
</ul>
</li>
<li>
<code>discriminator</code> – 使用结果值来决定使用哪个结果映射
<ul>
<li>
<code>case</code> – 基于某些值的结果映射
<ul>
<li>嵌入结果映射 – 这种情形结果也映射它本身,因此可以包含很多相
同的元素,或者它可以参照一个外部的结果映射。
</li>
</ul>
</li>
</ul>
</li>
</ul>
<table>
<caption>ResultMap Attributes</caption>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>当前命名空间中的一个唯一标识,用于标识一个result map.</td>
</tr>
<tr>
<td><code>type</code></td>
<td>类的全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格).
</td>
</tr>
<tr>
<td><code>autoMapping</code></td>
<td>如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性autoMappingBehavior。默认值为:unset。
</td>
</tr>
</tbody>
</table>
<p>
<span class="label important">最佳实践</span> 通常逐步建立结果映射。单元测试的真正帮助在这里。如果你尝试创建
一次创建一个向上面示例那样的巨大的结果映射,
那么可能会有错误而且很难去控制它
来工作。开始简单一些,一步一步的发展。而且要进行单元测试!使用该框架的缺点是
它们有时是黑盒(是否可见源代码)
。你确定你实现想要的行为的最好选择是编写单元
测试。它也可以你帮助得到提交时的错误。
</p>
<p>
下面一部分将详细说明每个元素。
</p>
<h4>id & result</h4>
<source><![CDATA[<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>]]></source>
<p>
这些是结果映射最基本内容。id 和 result 都映射一个单独列的值到简单数据类型(字符
串,整型,双精度浮点数,日期等)的单独属性或字段。
</p>
<p>
这两者之间的唯一不同是 id 表示的结果将是当比较对象实例时用到的标识属性。这帮
助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)
。 </p>
<p>
每个都有一些属性:
</p>
<table>
<caption>Id and Result Attributes</caption>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>property</code></td>
<td>
映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同
的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称
property
的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你
可以这样映射一些东西:
“username”
,或者映射到一些复杂的东西:
“address.street.number”
。
</td>
</tr>
<tr>
<td><code>column</code></td>
<td>
从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会
传递给 resultSet.getString(columnName)方法参数中相同的字符串。
</td>
</tr>
<tr>
<td><code>javaType</code></td>
<td>
一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名
的列表)
。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。
然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType
来保证所需的行为。
</td>
</tr>
<tr>
<td><code>jdbcType</code></td>
<td>
在这个表格之后的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅
仅需要对插入,更新和删除操作可能为空的列进行处理。这是 JDBC
jdbcType
的需要,而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定
这个类型-但仅仅对可能为空的值。
</td>
</tr>
<tr>
<td><code>typeHandler</code></td>
<td>
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默
认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理
器的实现,或者是类型别名。
</td>
</tr>
</tbody>
</table>
<h4>支持的 JDBC 类型</h4>
<p>
为了未来的参考,MyBatis 通过包含的 jdbcType 枚举型,支持下面的 JDBC 类型。
</p>
<table>
<tr>
<td><code>BIT</code></td>
<td><code>FLOAT</code></td>
<td><code>CHAR</code></td>
<td><code>TIMESTAMP</code></td>
<td><code>OTHER</code></td>
<td><code>UNDEFINED</code></td>
</tr>
<tr>
<td><code>TINYINT</code></td>
<td><code>REAL</code></td>
<td><code>VARCHAR</code></td>
<td><code>BINARY</code></td>
<td><code>BLOB</code></td>
<td><code>NVARCHAR</code></td>
</tr>
<tr>
<td><code>SMALLINT</code></td>
<td><code>DOUBLE</code></td>
<td><code>LONGVARCHAR</code></td>
<td><code>VARBINARY</code></td>
<td><code>CLOB</code></td>
<td><code>NCHAR</code></td>
</tr>
<tr>
<td><code>INTEGER</code></td>
<td><code>NUMERIC</code></td>
<td><code>DATE</code></td>
<td><code>LONGVARBINARY</code></td>
<td><code>BOOLEAN</code></td>
<td><code>NCLOB</code></td>
</tr>
<tr>
<td><code>BIGINT</code></td>
<td><code>DECIMAL</code></td>
<td><code>TIME</code></td>
<td><code>NULL</code></td>
<td><code>CURSOR</code></td>
<td><code>ARRAY</code></td>
</tr>
</table>
<h4>构造方法</h4>
<p>
对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像
你绝大多数的领域模型,
指令也许是你想使不可变类的地方。
通常包含引用或查询数
据的表很少或基本不变的话对不可变类来说是合适的。
构造方法注入允许你在初始化时
为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属
性来达到这个目的,但是一些人更青睐构造方法注入。构造方法元素支持这个。
</p>
<p>
看看下面这个构造方法:
</p>
<source><![CDATA[public class User {
//...
public User(Integer id, String username, int age) {
//...
}
//...
}]]></source>
<p>
为了将结果注入构造方法,MyBatis需要通过某种方式定位相应的构造方法。
在下面的例子中,MyBatis搜索一个声明了三个形参的的构造方法,以 <code>java.lang.Integer</code>, <code>java.lang.String</code> and <code>int</code> 的顺序排列。
</p>
<source><![CDATA[<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="age" javaType="_int"/>
</constructor>]]></source>
<p>
当你在处理一个带有多个形参的构造方法时,对arg元素顺序的维持是很容易出错的。为了能利用构造方法形参的name来对形参进行引用,你可以添加 <code>@Param</code> 注解或者使用'-parameters'编译选项和启用 <code>useActualParamName</code> (此选项默认开启)来编译工程。
下面的例子对于同一个构造方法依然是有效的,尽管第二和第三个形参顺序与构造方法中声明的顺序不匹配。
</p>
<source><![CDATA[<constructor>