在上篇MyBatis基礎篇中咱們獨立使用MyBatis構建了一個簡單的數據庫訪問程序,能夠實現單表的基本增刪改查等操做,經過該實例咱們能夠初步瞭解MyBatis操做數據庫須要的一些組成部分(配置文件、實體類、SQL映射文件、Mapper接口等等)和重要對象(SqlSession、Mapper實例等等)。有了總體認知後,咱們就能夠進一步深刻學習MyBatis的使用,resultMap本文主要圍繞resultMap展開。html
resultMap做爲MyBatis的Sql映射文件中重要的元素之一,主要用來實現複雜的結果映射,其子元素結構以下:java
constructor是構造器的意思,對反射有基本瞭解的都應該不會陌生。藉此咱們先回顧一下前面定義的一個簡單resulMap映射內容mysql
<resultMap type="person" id="personResultMap" > <id column="id" property="id" /> <result column="name" property="name" /> <result column="gender" property="gender" /> </resultMap>
上面是一個典型的在resultMap中定義數據表與實體類的映射關係,type這裏用的別名指向Person類,id爲該映射的惟一標識,用於在後面咱們的定義語句中引用,內部的id和result分別對應主鍵和普通字段定義,column指數據表中字段名,property指實體類中屬性名。在以前的示例中,經過這個一個映射關係,咱們查詢出來的結果就轉化爲了一個Person類對象。web
這個Person類對象並不是由咱們建立出來,而是由mybatis調用了Person類的默認無參構造函數建立對象,再調用set方法爲對象賦值,這樣才返回了咱們想要的結果。下面結合以前的示例,作一些改動以便觀察。sql
package com.mmm.pojo; /** * Person實體類 * */ public class Person { private String id; private String name; private String gender; public String getId() { return id; } public void setId(String id) { this.id = id; System.out.println("爲主鍵屬性id賦值"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("爲屬性name賦值"); } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; System.out.println("爲屬性gender賦值"); } public Person() { System.out.println("調用無參構造建立對象"); } }
<select id="selectById"resultMap="personResultMap"> select * from `person` where id = #{id} </select>
調用該查找方法後會看到控制檯輸出以下信息:數據庫
說明確實是mybatis調用了這些方法幫咱們建立了對象,基於構造器。提及構造器,在咱們定義的pojo實體類中,簡單的私有屬性加上set/get方法聲明即完成,在java類中,會默認提供一個無參的構造方法,例如這裏的Person類apache
public Person(){}
咱們使用語句 Person p = new Person();這裏即調用的該無參構造。方法均可以重載,構造方法也不例外。可是很重要的一點,咱們在重載構造方法後,本來默認提供的無參構造就沒法使用了,須要顯示聲明,這點下面會給出示例。resultMap中constructor就是基於重載的帶參構造方法建立對象,以下:session
<resultMap type="person" id="personResultMap" > <constructor> <idArg column="id" javaType="string" /> <arg column="name" javaType="string" /> <arg column="gender" javaType="string" /> </constructor> </resultMap>
在實體類中添加劇載的帶參構造方法mybatis
public Person(String id, String name, String gender) { this.id = id; this.name = name; this.gender = gender; System.out.println("直接調用帶參構造函數建立對象"); }
再次調用查找方法後會看到控制檯輸出以下信息:app
這樣一來,使用constructor一樣拿到了數據。
這個時候若是咱們把實體類中的默認無參構造方法的顯示聲明刪除,即只有一個帶參的構造方法,這樣咱們再把Sql映射文件中<resultMap>內容還原成文中一開始定義的典型<id><result>等,再調用方法,爲了更明顯,在地址欄中經過controller層層調用到該方法,報錯乍一看,一大串異常,其實標題末尾一句話就出來了
不存在該方法,該方法指的就是Person實體類中的無參構造方法,在以前咱們沒有顯示聲明沒有問題,那是由於在類中若是沒有任何的構造方法聲明,會默認提供一個無參構造;可是咱們使用construtor的時候在實體類中重載了一個帶參的構造方法,使得本來默認的無參構造沒法使用了,這裏若是還要須要使用,能夠在實體類中顯示聲明。
前面的單表典型基本映射就是由這兩個元素組成,因此應該比較好理解。id和result都用於將表中字段和實體類中屬性創建起映射關係,區別是id是用於主鍵字段的映射,result用於普通字段映射。兩個元素中都有5個屬性供定義,分別是column、property、javaType、jdbcType、typeHandler。
column:這裏用於單表查詢的話能夠簡單的理解爲數據表中字段,前面也是相似處理,可是實際上並非這麼簡單,例如咱們在寫SQL語句時常常會使用到別名,別名的好處就不復述了,特別是進行多表查詢時,這裏咱們在SQL映射文件的<select>語句中也可使用別名,這個別名與這裏的column就對應上了,不注意這點的話可能出問題,後面介紹關聯查詢會有示例。
property:實體類中定義的與表字段對應的屬性
javaType:類型這裏可使用全名,也可使用別名,例如上面的string就是一個別名。咱們在定義基本的實體類映射時,mybatis通常能夠自動肯定類型。
jdbcType:這裏是指針對數據表字段方的jdbc類型,mybatis中支持下面的 JDBC 類型。
typeHandler:在使用數據庫建立表,定義字段名,選擇字段類型時,例如MySql中字符串類型varchar,而後咱們使用mybatis映射到實體類中變成Java字符串類型String,這個轉換過程是怎麼實現的?其中mybatis就已經預約義了一些類型處理器,也就是這個typeHandler,咱們也能夠根據須要自定義一些類型處理器。
association意思爲聯繫、關聯,針對單個(一個)對象,典型的一對1、多對一關聯映射關係均可以經過該元素實現,下面是基本定義內容。
<association property="關聯的對象在本對象實體類中的屬性名" column="關聯表的主鍵在本表中的字段名" javaType="關聯對象的實體類類型(可用別名)"> <id column="ID" property="id" /> <result column="數據表字段名" property="實體類屬性名" />
<result column="數據表字段名" property="實體類屬性名" />
... </association>
經過上面的關聯定義,而後在<select>元素中定義多表關聯查詢語句便可實現關聯查詢效果。這個<association>內部的<id><result>等元素,咱們不必定要這樣定義,也能夠經過額外定義一個resultMap填入這些元素,而後<association>有一個resultMap屬性,值就填額外的reslutMap的id,這樣也能夠實現關聯映射。除此以外,也能夠經過一個關聯的嵌套查詢語句實現,即額外定義一個針對關聯表的<select>元素,而後<association>有一個select屬性,值就填<select>的id,這樣一樣能實現。
collection意爲集合,這裏主要用於關聯多個對象的映射關係定義,例如典型的一對多,下面是基本定義內容。
<collection property="關聯的對象在本對象實體類中的屬性名" column="本表的主鍵在關聯表中的字段名" ofType="關聯集合中對象的實體類類型(可用別名)"> <id column="ID" property="id" /> <result column="數據表字段名" property="實體類屬性名" /> <result column="數據表字段名" property="實體類屬性名" />
... </collection>
相似上面的association,也能夠額外定義<select>元素,而後引入,從而實現關聯查詢。
關聯映射應用得較多,關聯屬性也相對複雜一些,光憑文字和片斷描述很難快速理解,下面以一個一對多和多對一結合的示例來總體應用一次。
這裏以部門做爲查詢的實體對象,從圖中不難理解,部門與公司是多對一關係,部門與員工是一對多關係,咱們查詢一個部門對象時,要把這個部門所屬的公司信息查出來,還要把部門下面的全部員工信息查出來。
這裏對象分公司、部門、員工一共三類,因此首先準備三張數據表,結構以下
這裏要理解一點,基於主鍵關聯,不管是一對多,仍是多對一,在「多「」的一方表中,都有一個與「一」的一方表主鍵對應的字段,例如這裏的部門表中COMPANY_ID和員工表中的DEPT_ID,用來關聯對應表。
新建maven工程,這裏不須要建web項目,普通的java項目就行。pom依賴能夠參照SSM框架開發web項目系列(一) 環境搭建篇。總體結構以下所示
而後新建三個對象實體類,以下所示
package com.mmm.pojo; //公司 public class Company { private Integer id; //主鍵 private String companyName; //公司名稱 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } @Override public String toString() { return "Company [id=" + id + ", companyName=" + companyName + "]"; } }
package com.mmm.pojo; import java.util.List; //部門 public class Dept { private Integer id; //主鍵 private String deptName; //部門名稱 private Company company; //部門所屬公司---------多對一 private List<Emp> empList; //部門下面員工---------一對多 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } public Company getCompany() { return company; } public void setCompany(Company company) { this.company = company; } public List<Emp> getEmpList() { return empList; } public void setEmpList(List<Emp> empList) { this.empList = empList; } @Override public String toString() { return "Dept [id=" + id + ", deptName=" + deptName + ", company=" + company + ", empList=" + empList + "]"; } }
package com.mmm.pojo; //員工實體類 public class Emp { private Integer id; //主鍵 private String name; //員工姓名 private String gender; //員工性別 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Emp [id=" + id + ", name=" + name + ", gender=" + gender + "]"; } }
<?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> <!-- 這裏能夠定義類的別名,在mapper.xml文件中應用會方便不少 --> <typeAliases> <typeAlias alias="emp" type="com.mmm.pojo.Emp" /> <typeAlias alias="dept" type="com.mmm.pojo.Dept" /> <typeAlias alias="company" type="com.mmm.pojo.Company" /> </typeAliases> <!-- 環境配置 --> <environments default="envir"> <environment id="envir"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssm?characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/mmm/mapper/deptMapper.xml"/> </mappers> </configuration>
package com.mmm.mapper; import com.mmm.pojo.Dept; public interface DeptMapper { public Dept selectById(Integer id); }
<?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="com.mmm.mapper.DeptMapper"> <resultMap type="dept" id="deptResultMap"> <!-- 下面的column就是前面說的要注意的,若是簡單的認爲是字段名會出問題 --> <id column="d_id" property="id" /> <result column="d_name" property="deptName" /> <!-- 一個公司下面多個部門,部門與公司,多對一關係 --> <association property="company" column="COMPANY_ID" javaType="company" resultMap="companyResultMap"> <!-- <id column="ID" property="id" /> <result column="COMPANY_NAME" property="companyName" /> --> </association> <!-- 一個部門下有多個員工,部門與員工,一對多關係 --> <collection property="empList" column="DEPT_ID" ofType="emp"> <id column="ID" property="id" /> <result column="EMP_NAME" property="name" /> <result column="EMP_GENDER" property="gender" /> </collection> </resultMap> <!-- 也能夠經過相似下面的定義而後引入到association中,association的resultMap屬性值對應下面resultMap的id --> <!-- <resultMap type="company" id="companyResultMap"> <id column="ID" property="id" /> <result column="COMPANY_NAME" property="companyName" /> </resultMap> --> <!-- 根據主鍵id查找記錄 --> <select id="selectById" resultMap="deptResultMap"> select e.*,d.ID AS d_id,d.DEPT_NAME AS d_name,c.* from `TBL_EMP` e, `TBL_DEPT` d, `TBL_COMPANY` c where d.ID = #{id} and d.COMPANY_ID = c.id and d.ID = e.DEPT_ID; </select> </mapper>
最後,測試一下是否能實現關聯查詢
package com.mmm.test; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import com.mmm.mapper.DeptMapper; import com.mmm.pojo.Dept; public class TestMyBatis { @Test public void testCore() throws IOException { //直接實例SqlSessionFactoryBuilder對象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //MyBatis配置文件路徑 String path = "mybatis-config.xml"; //經過路徑獲取輸入流 Reader reader = Resources.getResourceAsReader(path); //經過reader構建sessionFactory SqlSessionFactory sessionFactory = builder.build(reader); //獲取SqlSession對象 SqlSession sqlSession = sessionFactory.openSession(); //獲取Mapper實例 DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); //根據id查找制定的部門,並關聯查詢出其公司和員工信息 Dept dept = mapper.selectById(10001); System.out.println(dept); } }
控制檯輸出信息過長,複製下來粘貼以下內容:
Dept [id=10001, deptName=資產部, company=Company [id=10003, companyName=公司一], empList=[Emp [id=10001, name=員工一, gender=男], Emp [id=10002, name=員工二, gender=男], Emp [id=10003, name=員工三, gender=男]]]
能夠看到,至此基本實現了,查詢部門時,也關聯查詢出了所屬公司信息、下面全部部門員工信息。
咱們在瀏覽網站,點擊連接跳轉,每一個頁面的刷新,大可能是不斷在請求查詢數據庫中的數據,固然也不全是查詢,例如會有日誌記錄,也是插入操做等等。查詢是一個比較重要的內容,怎樣關聯查詢,怎麼優化查詢語句,減小查詢次數,提升查詢速度等等都是須要去思考和學習的地方。