Mybatis 數據源和數據庫鏈接池源碼解析(DataSource)mybatis
Mybatis Mapper 接口源碼解析(binding包)app
在上篇文章 Mybatis 解析 SQL 源碼分析一 介紹了 Maper.xml 配置文件的解析,可是沒有解析 resultMap 節點,由於該解析比較複雜,也比較難理解,全部單獨拿出來進行解析。post
在使用 Mybatis 的時候,都會使用resultMap節點來綁定列與bean屬性的對應關係,可是通常就只會使用其簡單的屬性,他還有一些比較複雜的屬性能夠實現一些高級的功能,在沒查看源碼以前,我也只會簡單的使用,不少高級的用法都沒有使用過,經過此次學習,但願能在工做使用,可以寫出簡潔高效的SQL。學習
先來看看 resultMap 節點的官方定義:測試
簡單的使用:fetch
<resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap>
會把列名和屬性名進行綁定,該節點一共有 4 個屬性:ui
1. id :表示該 resultMap,共其餘的語句調用
2. type:表示其對於的pojo類型,可使用別名,也可使用全限定類名
3. autoMapping:若是設置這個屬性,MyBatis將會爲這個ResultMap開啓或者關閉自動映射。這個屬性會覆蓋全局的屬性 autoMappingBehavior。默認值爲:unset。
4. extends:繼承,一個 resultMap 能夠繼承另外一個 resultMap,這個屬性是否是沒有用過 ? ^^
接下來看下它能夠有哪些子節點:
在查詢數據庫獲得數據後,會把對應列的值賦值給javabean對象對應的屬性,默認狀況下mybatis會調用實體類的無參構造方法建立一個實體類,而後再給各個屬性賦值,若是沒有構造方法的時候,可使用 constructor 節點進行綁定,如現有以下的構造方法:
public Person(int id, String name, String job, int age) { this.id = id; this.name = name; this.job = job; this.age = age; }
則,可使用 constructor 節點進行綁定:
<resultMap id="queryPersonMap" type="mybatis.pojo.Person" > <constructor> <idArg column="id" javaType="int"/> <arg column="name" javaType="string" /> <arg column="job" javaType="string" /> <arg column="age" javaType="int" /> </constructor> </resultMap>
關聯查詢,在級聯中有一對1、一對多、多對多等關係,association主要是用來解決一對一關係的,association 能夠有多種使用方式:
好比如今有一個 Person 類,它有一個 Address 屬性,關聯 Address 對象:
public class Person implements Serializable { private int id; private String name; private String job; private int age; private Address address; } public class Address { private int id; private String name; private long number; }
關聯查詢方式一:
<resultMap id="queryPersonMap" type="mybatis.pojo.Person" > <id column="id" property="id"/> <result column="name" property="name" /> <result column="job" property="job" /> <result column="age" property="age"/> <association property="address" column="address_id" javaType="mybatis.pojo.Address" select="queryAddress" /> </resultMap> <select id="queryAddress" resultType="mybatis.pojo.Address"> select * from address where id = #{id} </select>
關聯查詢方式二:
<resultMap id="queryPersonMap" type="mybatis.pojo.Person" > <id column="id" property="id"/> <result column="name" property="name" /> <result column="job" property="job" /> <result column="age" property="age"/> <association property="address" column="address_id" javaType="mybatis.pojo.Address" resultMap="addressMap"/> </resultMap> <resultMap id="addressMap" type="mybatis.pojo.Address"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="number" property="number"/> </resultMap>
關聯查詢方式三:
<resultMap id="queryPersonMap" type="mybatis.pojo.Person" > <id column="id" property="id"/> <result column="name" property="name" /> <result column="job" property="job" /> <result column="age" property="age"/> <association property="address" javaType="mybatis.pojo.Address"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="number" property="number"/> </association> </resultMap>
collection 集合,若是pojo對象有一個屬性是集合類型的,可使用collection 來進行查詢:
public class Person implements Serializable { private int id; private String name; private String job; private int age; private List<Address> addressList; }
<resultMap id="queryPersonMap" type="mybatis.pojo.Person" > <id column="id" property="id"/> <result column="name" property="name" /> <result column="job" property="job" /> <result column="age" property="age"/> <collection property="addressList" javaType="ArrayList" ofType="mybatis.pojo.Address"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="number" property="number"/> </collection> </resultMap>
固然還有其餘的方法,具體能夠參考官網。
鑑別器,mybatis可使用discriminator判斷某列的值,而後根據某列的值改變封裝行爲,有點像 Java的 switch 語句,鑑別器指定了 column 和 javaType 屬性。 列是 MyBatis 查找比較值的地方。 JavaType 是須要被用來保證等價測試的合適類型,
好比某列的值等於多少,則返回1,等於多少返回2等等。
<resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultMap="carResult"/> <case value="2" resultMap="truckResult"/> <case value="3" resultMap="vanResult"/> <case value="4" resultMap="suvResult"/> </discriminator> </resultMap>
以上就是 resultMap 節點的所有使用方法,下面是一個比較複雜的例子,源碼解析會按照其來解析,例子來自於官方文檔。
<resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> <arg column="name" javaType="string" /> </constructor> <id column="id" property="id" /> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </resultMap>
首先須要說明的是,一個 resultMap 節點會解析成一個 ResultMap 對象,而每一個子節點(除了discriminator節點)會被解析成 ResultMapping 對象,即一個 ResultMap 包含的是 ResultMapping 對象的集合。
先來看看 ResultMapping 的一個聲明:
public class ResultMapping { // configuration 對象 private Configuration configuration; private String property; private String column; private Class<?> javaType; private JdbcType jdbcType; private TypeHandler<?> typeHandler; // 對應的是 resultMap 屬性,經過id來引用其餘的resultMap private String nestedResultMapId; // 對應的是 select 屬性,經過id來引用其餘的select節點的定義 private String nestedQueryId; private Set<String> notNullColumns; private String columnPrefix; // 處理後的標誌,標誌有兩個 id和constructor private List<ResultFlag> flags; // 對應節點的column屬性拆分後生成的結果,composites.size()>0會使column爲null private List<ResultMapping> composites; private String resultSet; private String foreignColumn; private boolean lazy; |
ResultMap 的聲明以下:
public class ResultMap { // ID,表示一個resultMap private String id; // 該resultMap對應的Javabean類型 private Class<?> type; // 對應的是除了discriminator節點外的其餘節點 private List<ResultMapping> resultMappings; // id 節點的映射集合 private List<ResultMapping> idResultMappings; // 構造節點的集合 private List<ResultMapping> constructorResultMappings; // 記錄了映射關係中 不帶有contructot節點的的映射關係 private List<ResultMapping> propertyResultMappings; // column集合 private Set<String> mappedColumns; // discriminator 節點 private Discriminator discriminator; private boolean hasNestedResultMaps; private boolean hasNestedQueries; private Boolean autoMapping; }
解析:
resultMapElements(context.evalNodes("/mapper/resultMap")); private void resultMapElements(List<XNode> list) throws Exception { for (XNode resultMapNode : list) { try { // 解析每一個 resultMap 節點 resultMapElement(resultMapNode); } catch (IncompleteElementException e) { // ignore, it will be retried } } } private ResultMap resultMapElement(XNode resultMapNode) throws Exception { // 注意這裏傳入的是一個空的集合 return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList()); }
主要的解析方法:
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); // ID 屬性 String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); // type屬性 String type = resultMapNode.getStringAttribute("type",resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType",resultMapNode.getStringAttribute("javaType")))); // extends 屬性 String extend = resultMapNode.getStringAttribute("extends"); // autoMapping 屬性 Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 從註冊的類型管理器裏面查找對應的類型 Class<?> typeClass = resolveClass(type); // discriminator 節點 Discriminator discriminator = null; List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); resultMappings.addAll(additionalResultMappings); // 處理子節點 List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { if ("constructor".equals(resultChild.getName())) { // 處理 constructor 節點 processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { // 處理discriminator節點 discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { List<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } // 處理其餘節點,建立 resultMapping 對象並添加到集合中 resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { // 建立表明該 resultMap 節點的 ResultMap 對象並添加到 ResultMap 集合中。 return resultMapResolver.resolve(); } catch (IncompleteElementException e) { // 解析失敗,添加到集合,從新解析 configuration.addIncompleteResultMap(resultMapResolver); throw e; } }
處理 constructor 節點:
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { List<XNode> argChildren = resultChild.getChildren(); for (XNode argChild : argChildren) { List<ResultFlag> flags = new ArrayList<ResultFlag>(); // 向集合中添加 contrucator 標誌 flags.add(ResultFlag.CONSTRUCTOR); if ("idArg".equals(argChild.getName())) { // 添加id標誌 flags.add(ResultFlag.ID); } // 建立 ResultMapping 對象並添加到集合中 resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags)); } }
建立 ResultMapping 對象:
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception { // 解析節點的屬性 String property = context.getStringAttribute("property"); String column = context.getStringAttribute("column"); String javaType = context.getStringAttribute("javaType"); String jdbcType = context.getStringAttribute("jdbcType"); String nestedSelect = context.getStringAttribute("select"); String nestedResultMap = context.getStringAttribute("resultMap", processNestedResultMappings(context, Collections.<ResultMapping> emptyList())); String notNullColumn = context.getStringAttribute("notNullColumn"); String columnPrefix = context.getStringAttribute("columnPrefix"); String typeHandler = context.getStringAttribute("typeHandler"); String resultSet = context.getStringAttribute("resultSet"); String foreignColumn = context.getStringAttribute("foreignColumn"); boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager")); Class<?> javaTypeClass = resolveClass(javaType); @SuppressWarnings("unchecked") // 對應的 typeHandler 類型 Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); // 建立 ResultMapping 對象 return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy); }
以後是建立 ResultMapped 對象並添加到集合中:
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); // 調用的使用 builderAssistant 的 addResultMap 方法 return resultMapResolver.resolve();
public ResultMap addResultMap(String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) { // 爲 id 加上 namespace即 namespace.id id = applyCurrentNamespace(id, false); extend = applyCurrentNamespace(extend, true); if (extend != null) { if (!configuration.hasResultMap(extend)) { throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'"); } // 獲取父級的resultMap ResultMap resultMap = configuration.getResultMap(extend); List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings()); // 由於上面添加過一次,如今要刪除重複的 extendedResultMappings.removeAll(resultMappings); // Remove parent constructor if this resultMap declares a constructor. boolean declaresConstructor = false; for (ResultMapping resultMapping : resultMappings) { if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) { declaresConstructor = true; break; } } if (declaresConstructor) { Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator(); while (extendedResultMappingsIter.hasNext()) { if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) { extendedResultMappingsIter.remove(); } } } resultMappings.addAll(extendedResultMappings); } // 建立 resultMap ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping) .discriminator(discriminator) .build(); // 添加到集合 configuration.addResultMap(resultMap); return resultMap; }
到這裏,就把 resultMap 節點解析完畢了,以後在解析 Mapper.xml 文件的其餘節點,參考 Mybatis 解析 SQL 源碼分析一