resultMap簡介
MyBatis是基於“數據庫結構不可控”的思想建立的,也就是我們希望數據庫遵循第三範式或BCNF,但實際事與願違,那麼結果集映射就是MyBatis爲我們提供這種理想與現實間轉換的手段了,而resultMap就是結果集映射的配置標籤了。
1.從SQL查詢結果到領域模型實體
在深入ResultMap標籤前,我們需要了解從SQL查詢結果集到JavaBean或POJO實體的過程。
1. 通過JDBC查詢得到ResultSet對象
2. 遍歷ResultSet對象並將每行數據暫存到HashMap實例中,以結果集的字段名或字段別名爲鍵,以字段值爲值
3. 根據ResultMap標籤的type屬性通過反射實例化領域模型
4. 根據ResultMap標籤的type屬性和id、result等標籤信息將HashMap中的鍵值對,填充到領域模型實例中並返回
2.使用場景
在項目的實際開發中,有可能會遇到這樣兩種情況。
1. 實體類中的屬性名與列名不相同,不能改但。導致不能自動裝配值
2. 多表查詢的返回值中可能需要其他對象,或者數組(一對一和一對多)
二、resultMap標籤解釋
標籤及屬性介紹
<resultMap > 標籤:
id屬性 ,resultMap標籤的標識。
type屬性 ,返回值的全限定類名,或類型別名。
autoMapping屬性 ,值範圍true(默認值)|false, 設置是否啓動自動映射功能,自動映射功能就是自動查找與字段名小寫同名的屬性名,並調用setter方法。而設置爲false後,則需要在`resultMap`內明確註明映射關係纔會調用對應的setter方法。
<resultMap>可以設置的子標籤映射:
1).id標籤 :ID 結果,將結果集標記爲ID,以方便全局調用(適用於指定主鍵)
column 數據庫的列名
Property需要裝配的屬性名
2).result標籤:將查詢到的列的值,反射到指定的JavaBean的 屬性上
column 數據庫的列名
Property 需要裝配的屬性名
3).association標籤:複雜類型 , 多表查詢(一對一)時,將根據外鍵或某列信息查詢出的對象,直接裝配給某個resultMap指定的屬性。
column 數據庫的列名
Property 需要裝配的屬性名
select 指定用來多表查詢的sqlmapper
4).Collection標籤:複雜類型,多表查詢(一對多),將查詢出的結果集合直接裝配給某個對應的集合
column 數據庫的列名
Property 需要裝配的屬性名
javaType 指定用什麼類型接受返回的值(必要)
select 指定用來多表查詢的sqlmapper
5).constructor– 用來將結果反射給一個實例化好的類的構造器
a) idArg –ID 參數;將結果集標記爲ID,以方便全局調用
b) arg –反射到構造器的通常結果
6).discriminator – 使用一個結果值以決定使用哪個resultMap
a) case – 基本一些值的結果映射的case 情形
i. nestedresult mappings –一個case 情形本身就是一個結果映射,因此也可以包括一些相同的元素,也可以引用一個外部resultMap。
<resultMap type="" id="">
<id column="" property=""/>
<result column="" property="" />
<association property="" column="" select=""></association>
<collection property="" column="" javaType="" select=""></collection>
<constructor></constructor>
</resultMap>
三、各標籤使用
1.id、result
id、result是最簡單的映射,id爲主鍵映射;result其他基本數據庫表字段到實體類屬性的映射。
實體字段 表的列名
sid stuid
sname stuname
gid gid
grade grade
<resultMap type="student" id="studentMap" autoMapping="true">
<!-- 主鍵建議用id標籤 -->
<id column="stuid" property="sid"/>
<!-- 其他列可以用result標籤 -->
<result column="stuname" property="sname"/>
</resultMap>
id、result語句屬性配置細節:
屬性
描述
property
需要映射到JavaBean 的屬性名稱。
column
數據表的列名或者標籤別名。
javaType
一個完整的類名,或者是一個類型別名。如果你匹配的是一個JavaBean,那MyBatis 通常會自行檢測到。然後,如果你是要映射到一個HashMap,那你需要指定javaType 要達到的目的。
jdbcType
數據表支持的類型列表。這個屬性只在insert,update 或delete 的時候針對允許空的列有用。JDBC 需要這項,但MyBatis 不需要。如果你是直接針對JDBC 編碼,且有允許空的列,而你要指定這項。
typeHandler
使用這個屬性可以覆寫類型處理器。這項值可以是一個完整的類名,也可以是一個類型別名。
支持的JDBC類型
爲了將來的引用,MyBatis支持下列JDBC 類型,通過JdbcType 枚舉:
BIT,FLOAT,CHAR,TIMESTAMP,OTHER,UNDEFINED,TINYINT,REAL,VARCHAR,BINARY,BLOB,NVARCHAR,SMALLINT,DOUBLE,LONGVARCHAR,VARBINARY,CLOB,NCHAR,INTEGER,NUMERIC,DATE,LONGVARBINARY,BOOLEAN,NCLOB,BIGINT,DECIMAL,TIME,NULL,CURSOR
2.association聯合
聯合元素用來處理“一對一”的關係。
需要指定映射的Java實體類的屬性,屬性的javaType(通常MyBatis 自己會識別)。對應的數據庫表的列名稱。如果想覆寫的話返回結果的值,需要指定typeHandler。
不同情況需要告訴MyBatis 如何加載一個聯合。MyBatis 可以用兩種方式加載:
1. select: 執行一個其它映射的SQL 語句返回一個Java實體類型。較靈活;
2. resultsMap: 使用一個嵌套的結果映射來處理通過join查詢結果集,映射成Java實體類型。
association 標籤
property=多表查詢裝配的屬性名
column=通過那一列關聯
select=指定查詢語句,如果查詢語句在其他的namespace中,則需要寫全namespace.方法id
例如:在獲取某個學生的信息的同時又想獲取所屬班級的所有信息
學生實體中的字段
package cn.et.fuqiang.resultMap.entity;
public class Student {
private Integer sid;//學生id
private String sname; //學生姓名
private Integer gid; //班級id
private Grade grade; //所屬班級的所有信息
}
班級實體的字段
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade {
private Integer id;//班級id
private String gname;//班級名稱
public Grade() {
super();
// TODO Auto-generated constructor stub
}
}
在Student的字段中,Grade班級實體,需要查詢班級表才能獲取,學生表中沒有該列(所以需要使用多表查詢中的 association標籤)
想要使用多表查詢先要定義一個resultMap,簡單來說其實相當於一個工廠,可以指定某個實體中那些屬性可以裝哪一列的值,並且還要定義類似於子查詢的方法。
<!-- 使用resultMap 多表查詢 -->
<resultMap type="student" id="gradeInStudent">
<!-- 主鍵建議用id標籤 -->
<id column="stuid" property="sid"/>
<!-- 其他列可以用result標籤 -->
<result column="stuname" property="sname"/>
<!-- 多對一 多表查詢
返回一個班級實體-->
<association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>
</resultMap>
在namespace="cn.et.fuqiang.resultMap.xml.GradeXmlInterface下的方法
<select id="queryGradeById" resultMap="gradeMap">
select * from grade where gid=#{0}
</select>
在這個resultMap中
type:是上面定義的學生類(使用了別名,如果沒有別名可以寫全類名)
id:是這個resultMap的標識
因爲表中的字段與實體中的不同,所以使用了下面兩個子標籤,將表的列與實體的屬性做了映射。這樣才能將表中的值獲取到實體的屬性中。
<!-- 主鍵建議用id標籤 -->
<id column="stuid" property="sid"/>
<!-- 其他列可以用result標籤 -->
<result column="stuname" property="sname"/>
因爲實體中還要一個班級實體,但表中沒有該字段,所以就要用多表查詢
<!-- 多對一 多表查詢返回一個班級實體-->
<association property="grade"column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>
(這裏的property是實體中的字段,而column=gid就相當於關聯的條件了
而select就是該關聯條件對應的查詢方法)
主sql
在這裏我們使用resultMap定義返回的類型,這樣就會自動將所有內容,裝到對應的屬性上
然後直接使用該主sql,查詢,獲取的值就會自動對應到其中。
<select id="quserAllStudent" resultMap="gradeInStudent">
select * from student
</select>
以上代碼寫爲標準的sql語句
select s.*,g.*from student s inner join grade g ons.gid=g.gid
3. collection聚集
聚集元素用來處理“一對多”的關係。需要指定映射的Java實體類的屬性,屬性的javaType(一般爲ArrayList);列表中對象的類型ofType(Java實體類);對應的數據庫表的列名稱;
不同情況需要告訴MyBatis 如何加載一個聚集。MyBatis 可以用兩種方式加載:
1. select:執行一個其它映射的SQL 語句返回一個Java實體類型。較靈活;
2. resultsMap: 使用一個嵌套的結果映射來處理通過join查詢結果集,映射成Java實體類型。
Collection標籤
property=多表查詢裝配的屬性名
column=通過那一列關聯
select=指定查詢語句,如果查詢語句在其他的namespace中,則需要寫全namespace.方法id
javaType=java實體中接受的集合類型
例如,一個班級有多個學生。
學生實體中的字段
package cn.et.fuqiang.resultMap.entity;
public class Student {
private Integer sid;//學生id
private String sname; //學生姓名
private Integer gid; //班級id
}
班級實體的字段
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade {
private Integer id;//班級id
private String gname;//班級名稱
private List<Student> students;//該班級下所有的學生
public Grade() {
super();
// TODO Auto-generated constructor stub
}
}
在Grade實體中有個List<Student> students屬性,需要查詢學生表才能獲取,班級表中沒有該信息(所以要使用collection聚集查詢對應的結果集)
<!-- 使用resultMap 多表查詢 -->
<resultMap type="grade" id="studentsInGrade">
<!-- 主鍵適合用 id 標籤 -->
<id column="gid" property="id" />
<!-- 一對多 多表查詢 collection 可獲取一個集合
property=多表查詢裝配的屬性名
column=通過那一列關聯
select=查詢語句
javaType=java實體中接受的集合類型
-->
<collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>
</resultMap>
在namespace="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"下的查詢語句和類型
<resultMap type="student" id="studentMap" autoMapping="true">
<!-- 主鍵建議用id標籤 -->
<id column="stuid" property="sid"/>
<!-- 其他列可以用result標籤 -->
<result column="stuname" property="sname"/>
</resultMap>
<!-- 使用多表查詢班級
注意: resultMap使用時應注意,如果該resultMap中配置了多表查詢,如果是互相查詢可能造成死循環
-->
<select id="quserUserByGid" resultMap="studentMap">
select * from student where gid=#{0}
</select>
注意:因爲實例中學生的字段名與表中的不同所以,又加了一個學生查詢專屬的resultMap
在這個resultMap中
type:是上面定義的學生類(使用了別名,如果沒有別名可以寫全類名)
id:是這個resultMap的標識
因爲表中的字段與實體中的不同,所以使用了下面的子標籤,將表的列與實體的屬性做了映射。這樣才能將表中的值獲取到實體的屬性中。
<!-- 主鍵適合用 id 標籤 -->
<id column="gid" property="id"/>
使用多表查詢獲取某個班級下的所有學生
<collection property="students" column="gid"javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>
</resultMap>
(這裏的property是實體中的字段,而column=gid就相當於關聯的條件了
而select就是該關聯條件對應的查詢方法)
主sql
<select id="queryAllGrade" resultMap="studentsInGrade">
select * from grade
</select>
執行該映射時就會自動根據resultMap自動調用其中內嵌的sql語句查詢所有學生
所有代碼實例
Mybatis配置
mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 指定jdbc的配置文件位置 -->
<properties resource="cn/et/fuqiang/resultMap/jdbc.properties"></properties>
<!-- 配置類別名 -->
<typeAliases>
<!-- 使用該標籤將該包下的所有類統一起別名 默認爲類名首字母小寫 -->
<package name="cn.et.fuqiang.resultMap.entity"/>
</typeAliases>
<!-- 配置jdbc環境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!-- 配置數據庫連接信息 -->
<dataSource type="POOLED">
<property name="driver" value="${driverClass}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<!-- 使用接口映射 配置查詢語句 -->
<mappers>
<!-- 註冊接口 如果使用的是註解則不需要mappers文件-->
<!-- 有兩種方式配置mapper
第一種 如果有mapper.xml文件直接配置該文件
<mapper resource="mapper 文件的類路徑">
第二種 配置接口的全類名
<mapper class="接口的全類名">
-->
<mapper class="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"/>
<mapper class="cn.et.fuqiang.resultMap.xml.GradeXmlInterface"/>
</mappers>
</configuration>
Jdbc連接四要素配置
jdbc.properties
url=jdbc:oracle:thin:@localhost:1521:orcl
user=mybatis
password=mybatis
driverClass=oracle.jdbc.OracleDriver
兩個實體
student實體
package cn.et.fuqiang.resultMap.entity;
public class Student {
private Integer sid;//學生id
private String sname;//學生姓名
private Integer gid;//班級id
private Grade grade;//所屬班級的所有信息
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(Integer sid, String sname, Integer gid, Grade grade) {
super();
this.sid = sid;
this.sname = sname;
this.gid = gid;
this.grade = grade;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getGid() {
return gid;
}
public void setGid(Integer gid) {
this.gid = gid;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
}
Grade實體
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade {
private Integer id;//班級
private String gname;//班級名稱
private List<Student> students;//該班級下所有的學生
public Grade() {
super();
// TODO Auto-generated constructor stub
}
public Grade(Integer id, String gname) {
super();
this.id = id;
this.gname = gname;
}
public String getGname() {
return gname;
}
public void setGname(String gname) {
this.gname = gname;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
兩個接口映射的接口
GradeXmlInterface.java
package cn.et.fuqiang.resultMap.xml;
import java.util.List;
import cn.et.fuqiang.resultMap.entity.Grade;
public interface GradeXmlInterface {
public Grade queryGradeById(Integer id);
public List<Grade> queryAllGrade();
}
StudentXmlInterface.java
package cn.et.fuqiang.resultMap.xml;
import java.util.List;
import cn.et.fuqiang.resultMap.entity.Student;
public interface StudentXmlInterface {
public Student queryStudentById(Integer sid);
public List<Student> quserAllStudent();
public List<Student> quserUserByGid();
}
兩個映射的mapper.xml
GradeXmlInterface.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.et.fuqiang.resultMap.xml.GradeXmlInterface">
<!-- 當實體的屬性名與表名不一致時可使用 resultMap標籤指定對應的列值給對應的屬性
autoMapping是設置是否自動裝配屬性名與表的列名對應的變量
-->
<resultMap type="grade" id="gradeMap" autoMapping="false">
<!-- 主鍵適合用 id 標籤 -->
<id column="gid" property="id"/>
<!-- 其他的用result標籤 -->
<result column="gname" property="gname"/>
</resultMap>
<select id="queryGradeById" resultMap="gradeMap">
select * from grade where gid=#{0}
</select>
<!-- 使用resultMap 多表查詢 -->
<resultMap type="grade" id="studentsInGrade">
<!-- 主鍵適合用 id 標籤 -->
<id column="gid" property="id" />
<!-- 一對多 多表查詢 collection 可獲取一個集合
property=多表查詢裝配的屬性名
column=通過那一列關聯
select=查詢語句
javaType=java實體中接受的集合類型
-->
<collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>
</resultMap>
<!-- 使用多表查詢在查詢班級的同時根據上面的resultMap查出對應的所有學生
注意: resultMap使用時應注意,如果該resultMap中配置了多表查詢,如果是互相查詢可能造成死循環
-->
<select id="queryAllGrade" resultMap="studentsInGrade">
select * from grade
</select>
</mapper>
StudentXmlInterface.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.et.fuqiang.resultMap.xml.StudentXmlInterface">
<!-- 當實體的屬性名與表名不一致時可使用 resultMap標籤指定對應的列值給對應的屬性
autoMapping是設置是否自動裝配屬性名與表的列名對應的變量(不寫默認是true)
-->
<resultMap type="student" id="studentMap" autoMapping="true">
<!-- 主鍵建議用id標籤 -->
<id column="stuid" property="sid"/>
<!-- 其他列可以用result標籤 -->
<result column="stuname" property="sname"/>
</resultMap>
<select id="queryStudentById" resultMap="studentMap">
select * from student where stuid=#{0}
</select>
<!-- 使用resultMap 多表查詢 -->
<resultMap type="student" id="gradeInStudent">
<!-- 主鍵建議用id標籤 -->
<id column="stuid" property="sid"/>
<!-- 其他列可以用result標籤 -->
<result column="stuname" property="sname"/>
<!-- 多對一 多表查詢 association
property=多表查詢裝配的屬性名
column=通過那一列關聯
select=查詢語句
-->
<association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>
</resultMap>
<select id="quserAllStudent" resultMap="gradeInStudent">
select * from student
</select>
<!-- 使用多表查詢班級
注意: resultMap使用時應注意,如果該resultMap中配置了多表查詢,如果是互相查詢可能造成死循環
-->
<select id="quserUserByGid" resultMap="studentMap">
select * from student where gid=#{0}
</select>
</mapper>
測試類
package cn.et.fuqiang.resultMap.xml;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import cn.et.fuqiang.resultMap.entity.Grade;
import cn.et.fuqiang.resultMap.entity.Student;
public class ResultMapXmlTest {
private static SqlSession session;
private static GradeXmlInterface gradeInter=null;
private static StudentXmlInterface studentInter=null;
static{
//mybatis的配置文件
String resource = "mybatis.xml";
//使用類加載器加載mybatis的配置文件(它也加載關聯的映射文件)
InputStream is =GradeXmlInterface.class.getResourceAsStream(resource);
//構建sqlSession的工廠
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
//使用MyBatis提供的Resources類加載mybatis的配置文件(它也加載關聯的映射文件)
//Readerreader = Resources.getResourceAsReader(resource);
//構建sqlSession的工廠
//SqlSessionFactorysessionFactory = new SqlSessionFactoryBuilder().build(reader);
//創建能執行映射文件中sql的sqlSession
session = sessionFactory.openSession();
gradeInter=session.getMapper(GradeXmlInterface.class);
studentInter=session.getMapper(StudentXmlInterface.class);
}
//@Test
public void queryGradeTest(){
Grade grade=gradeInter.queryGradeById(1);
System.out.println(grade.getId()+"------"+grade.getGname());
}
//@Test
public void queryStudentTest(){
Student student=studentInter.queryStudentById(1);
System.out.println(student.getSid()+"------"+student.getSname());
}
//@Test
public void queryGradeInStudent(){
List<Student> students=studentInter.quserAllStudent();
for (Student stu : students) {
System.out.println(stu.getSid()+"------"+stu.getSname()+"-------"+stu.getGrade().getGname());
}
}
@Test
public void queryStudentInGrade(){
List<Grade> students=gradeInter.queryAllGrade();
for (Grade grade : students) {
System.out.println(grade.getGname()+"班"+grade.getStudents().size()+"人");
}
}
}
MyBatis框架核心之Mapper文件使用resultMap及多表查詢
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.