表 Student
字段 | 注释 |
---|---|
SNO | 学号 |
SNAME | 学生名字 |
SSEX | 性别 |
SBIRITHDAY | 生日 |
CLASS | 班级 |
<!--建表语句:--> create table TEST.STUDENT ( SNO varchar(3) not null, SNAME varchar(4) not null, SSEX varchar(2) not null, SBIRTHDAY datetime null, CLASS varchar(5) null ) 复制代码
<!--Bean 文件--> public class Student { private String SNO; private String SNAME; private String SSEX; private Date SBIRTHDAY; private String CLASS; <!--get 和 set 方法--> ... } 复制代码
按照返回数据类型大致分为基础数据类型,JavaBean 和 Map。其中虽然返回的结果行数有单条也有多条,对应的接口返回类型是集合或者单个对象,但是在 xml 映射文件中,resultType 的值是相同的。
接口类:
<!--单条结果集--> String querySingleStudent(); <!--多条结果集--> List<String> queryAllStudent(); 复制代码
Mapper 文件:
<!--单条结果集--> <select id="querySingleStudent" resultType="string"> SELECT SNAME FROM TEST.STUDENT LIMIT 1 </select> <!--多条结果集--> <select id="queryAllStudent" resultType="string"> SELECT SNAME FROM TEST.STUDENT </select> 复制代码
接口类:
<!--单条结果集--> Map<String, Object> queryStudentMap(); <!--多条结果集--> List<Map<String, Object>> queryAllStudentMap(); 复制代码
Mapper 文件:
<!--单条结果集--> <select id="queryStudentMap" resultType="hashmap"> SELECT SNAME FROM TEST.STUDENT LIMIT 1 </select> <!--多条结果集--> <select id="queryAllStudentMap" resultType="hashmap"> SELECT SNAME FROM TEST.STUDENT </select> 复制代码
其中:
<setting name="callSettersOnNulls" value="true"/>
接口类:
<!--单条结果集--> Student querySingleStudentBean(); <!--多条结果集--> List<Student> queryAllStudentBean(); 复制代码
Mapper 文件:
<!--单条结果集--> <select id="queryStudentMap" resultType="student"> SELECT SNAME FROM TEST.STUDENT LIMIT 1 </select> <!--多条结果集--> <select id="queryAllStudentMap" resultType="student"> SELECT SNAME FROM TEST.STUDENT </select> 复制代码
<typeAliases> <typeAlias type="com.bean.Student" alias="student"/> ... </typeAliases> 复制代码
但是如果 JavaBean 文件很多,不想一个个指定,也可以使用 package 标签 设置mybatis自动扫描,别名即为类名的小写。
<typeAliases> <package name="包名"/> </typeAliases> 复制代码
对于一般的查询语句,resultType 足够了。对于多表查询等情况,就要请出 resultMap 了。
数据库字段类型 jdbcType 和 java 数据类型 并不是一一对应的关系,而且不同数据库类型也不尽相同。而 mybatis 将 TypeHandler 作为两者之间的映射关系。大部分情况下都是没有问题的,但是并非能覆盖所有的情况,特殊情况下可以使用 resultMap 自定义这种映射关系。
举个例子,数据库 LongVarchar 字段类型对应 java 中的 String 类型。但是在 DB2 数据库中,查询的 LongVarchar 类型的字段,在 mybatis 中被识别成 jdbcType 为 BLOB。有两种解决方法,第一种是在 SQL 中对该字段使用 CAST 转换为 VARCHAR(长度)类型。另一种是使用 resultMap:
<resultMap id="resultMapDemo" type="" autoMapping="true"> <result property="" column="" jdbcType="VARCHAR" /> </resultMap> <select id="demoID" resultMap="resultMapDemo"> ... <select> 复制代码
<select>
标签中使用 resultMap 指定返回集合。注意 resultMap 和 resultType 不能同时使用 <resultMap>
标签 <result>
标签 字段的映射关系的标签即有,也有,在 mybatis 文档中指出不使用id,会造成性能下降,因此将主键字段使用 id 标签是推荐的做法。但是如果不存在主键呢,当你在 ResultMap 只提供了部分字段而不是全部字段,即使使用了 autoMapping 属性,那么 mybatis 会按照你提供的字段名进行去重。那么在使用 resultMap 的时候,最优选择是:
在 resultType 的例子中都只涉及到一张表,如果涉及多张表关联查询呢。我们可以简单的将所有列映射到 hashmap 的键值上。
但是 HashMap 不是一个很好的领域模型。 你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)作为领域模型。
因此这里均采用 JavaBean 作为领域模型。增加一个成绩表 Score
字段 | 注释 |
---|---|
SNO | 学号 |
CNO | 课程编号 |
DEGREE | 成绩 |
<!--建表语句--> create table SCORE ( SNO varchar(3) not null, CNO varchar(5) not null, DEGREE decimal(10, 1) not null ) 复制代码
<!--Bean 文件--> public class Score { private String SNO; private String CNO; private Double DEGREE; <!--get 和 set 方法--> ... } 复制代码
这里的一对多关系是两个表字段一一对应,一个学生的某门课的成绩是唯一确定的。 在一一对应的情况下要在 resultMap 中使用 标签。
在 Student.java 中增加字段 Score
<!--Student.java--> private Score score; public Score getScore() { return score; } public void setScore(Score score) { this.score = score; } 复制代码
有两种使用情况,第一种为嵌套查询,即前一个 SQL 查询结果集中的字段作为参数传递给下一个 SQL。第二种情况为嵌套结果集,即两个表做关联查询,将结果集映射到多个 JavaBean 文件。
<resultMap id="allstudentResultMap" type="student"> <!--指定第二个 SELECT 语句,和传递的字段--> <association property="score" column="sno" select="queryScore" /> </resultMap> <!--第一个 SQL --> <select id="queryAllStudent" resultMap="allstudentResultMap"> select SNO,SNAME from test.STUDENT </select> <!--第二个 SQL--> <select id="queryScore" resultType="score"> select degree from test.SCORE where sno = #{sno} </select> 复制代码
在标签中
resultSet.getString(columnName)
,没有进行参数匹配,因此第二个 SQL 中 #{} 中写任何字符都可以;如果需要传递多个字段,使用 column = " {prop1=col1,prop2=col2} "
,这种情况下会以参数对象的形式来传递。
另外需要注意的是这种嵌套查询对于大型结果集和列名并友好,存在 N+1
的问题,因为下一条 SQL 会执行 N 次去循环查询,使用关联查询更合适。再者也可以开启 mybatis 的 懒查询
功能,嵌套的 SQL 不是一口气顺序执行完,而是在使用的时候才会执行下一条 SQL。例如执行 student.getScore().getSNO()
才会执行 queryScore
的 SQL。默认情况下没有开启,需要在配置文件中设置
设置参数 | 描述 | 默认值 |
---|---|---|
lazyLoadingEnabled | 延迟加载的全局开关,特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 | false |
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载 | false (true in ≤3.4.1) |
<!--mybatis-config.xml--> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> 复制代码
也可以在标签中设置 fetchType = “lazy” 开启懒加载,会覆盖全局的参数设置。
对于多表关联查询,一般在 SQL 中使用别名来避免字段名的重复。mybatis 要做的是将别名正确的映射到 JavaBean 属性上。
<!--嵌套结果--> <resultMap id="associationDemoMap" type="student" autoMapping="true"> <association property="score" javaType="score"> <result property="SNO" column="SC_SNO"/> </association> </resultMap> <select id="queryStudentScore" resultMap="associationDemoMap"> SELECT SNAME, SSEX, CLASS, ST.SNO, SC.SNO AS SC_SNO FROM test.student st INNER JOIN test.score sc ON st.sno = sc.sno where CNO = '3-105'; </select> 复制代码
通过设置标签指定了表列名和属性之间的映射关系。但这样如果字段很多,会需要一一指定,标签提供了 columnPrefix
属性,指定别名的前缀,这样可以重用resultMap
<resultMap id="associationDemoMap" type="student" autoMapping="true"> <!--columnPrefix 指定别名的前缀--> <association property="score" resultMap="anotherMap" columnPrefix="SC_" /> </resultMap> <!--方便重用--> <resultMap id="anotherMap" type="score" autoMapping="true"> </resultMap> 复制代码
除了一对一的关系,还有一对多的关系,比如这里一个学生Student 对应多门课的成绩。 一对多对应的情况下要在 resultMap 中使用 标签。首先需要调整 JavaBean 文件中两个表之间的关系。
<!--Student.java--> private List<Score> score; public List<Score> getScore() { return score; } public void setScore(List<Score> score) { this.score = score; } 复制代码
以嵌套结果集为例
<resultMap id="collectionDemoMap" type="student" autoMapping="true"> <!--多出了 ofType 属性--> <collection property="score" ofType="score"> <result property="SNO" column="SC_SNO"/> </collection> </resultMap> <select id="queryStudentScore" resultMap="collectionDemoMap"> SELECT SNAME,SSEX,CLASS,ST.SNO,SC.SNO AS SC_SNO FROM test.student st INNER JOIN test.score sc ON st.sno = sc.sno </select> 复制代码
注意到相比 association 多了一个属性ofType,是用来表示 List 集合中的类型的。其他属性的用法同 association 是一样的。