深入淺出Mybatis系列(四)---配置詳解之typeAliases別名(mybatis原始碼篇)

weixin_34377065發表於2016-11-19

摘要: 深入淺出Mybatis系列(四)---配置詳解之typeAliases別名(mybatis原始碼篇)   上篇文章《深入淺出Mybatis系列(三)---配置詳解之properties與environments(mybatis原始碼篇)》 介紹了properties與environments, 本篇...

  上篇文章《深入淺出Mybatis系列(三)---配置詳解之properties與environments(mybatis原始碼篇)》 介紹了properties與environments, 本篇繼續講剩下的配置節點之一:typeAliases。 typeAliases節點主要用來設定別名,其實這是挺好用的一個功能, 通過配置別名,我們不用再指定完整的包名,並且還能取別名。

  例如: 我們在使用 com.demo.entity. UserEntity 的時候,我們可以直接配置一個別名user, 這樣以後在配置檔案中要使用到com.demo.entity. UserEntity的時候,直接使用User即可。

  就以上例為例,我們來實現一下,看看typeAliases的配置方法:

<configuration>
    <typeAliases>
      <!--
      通過package, 可以直接指定package的名字, mybatis會自動掃描你指定包下面的javabean,
      並且預設設定一個別名,預設的名字為: javabean 的首字母小寫的非限定類名來作為它的別名。
      也可在javabean 加上註解@Alias 來自定義別名, 例如: @Alias(user) 
      <package name="com.dy.entity"/>
       -->
      <typeAlias alias="UserEntity" type="com.dy.entity.User"/>
  </typeAliases>
  
  ......
  
</configuration>

 

再寫一段測試程式碼,看看有沒生效:(我只寫一段虛擬碼)

 

Configuration con = sqlSessionFactory.getConfiguration();
Map<String, Class<?>> typeMap = con.getTypeAliasRegistry().getTypeAliases();
for(Entry<String, Class<?>> entry: typeMap.entrySet()) {
    System.out.println(entry.getKey() + " ================> " + entry.getValue().getSimpleName());
}

 

 

 

 

=======================================我是原始碼分割線==============================================

上面給大家簡單介紹了typeAliases的用法, 接下來就看看Mybatis中的原始碼了:

老規矩,先從對xml的解析講起:

/**
  * 解析typeAliases節點
  */
 private void typeAliasesElement(XNode parent) {
     if (parent != null) {
       for (XNode child : parent.getChildren()) {
         //如果子節點是package, 那麼就獲取package節點的name屬性, mybatis會掃描指定的package
         if ("package".equals(child.getName())) {
           String typeAliasPackage = child.getStringAttribute("name");
           //TypeAliasRegistry 負責管理別名, 這兒就是通過TypeAliasRegistry 進行別名註冊, 下面就會看看TypeAliasRegistry原始碼
           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
         } else {
           //如果子節點是typeAlias節點,那麼就獲取alias屬性和type的屬性值
           String alias = child.getStringAttribute("alias");
           String type = child.getStringAttribute("type");
           try {
             Class<?> clazz = Resources.classForName(type);
             if (alias == null) {
               typeAliasRegistry.registerAlias(clazz);
             } else {
               typeAliasRegistry.registerAlias(alias, clazz);
             }
           } catch (ClassNotFoundException e) {
             throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
           }
         }
       }
     }
   }

 

重要的原始碼在這兒:

TypeAliasRegistry:

public class TypeAliasRegistry {
  
  //這就是核心所在啊, 原來別名就僅僅通過一個HashMap來實現, key為別名, value就是別名對應的型別(class物件)
  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

  /**
   * 以下就是mybatis預設為我們註冊的別名
   */
  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

  
  /**
   * 處理別名, 直接從儲存有別名的hashMap中取出即可
   */
  @SuppressWarnings("unchecked")
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) return null;
      String key = string.toLowerCase(Locale.ENGLISH); // issue #748
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }
  
  /**
   * 配置檔案中配置為package的時候, 會呼叫此方法,根據配置的報名去掃描javabean ,然後自動註冊別名
   * 預設會使用 Bean 的首字母小寫的非限定類名來作為它的別名
   * 也可在javabean 加上註解@Alias 來自定義別名, 例如: @Alias(user)
   */
  public void registerAliases(String packageName){
    registerAliases(packageName, Object.class);
  }

  public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }

  public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }

  //這就是註冊別名的本質方法, 其實就是向儲存別名的hashMap新增值而已, 呵呵, 別名的實現太簡單了,對吧
  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) throw new TypeException("The parameter alias cannot be null");
    String key = alias.toLowerCase(Locale.ENGLISH); // issue #748
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

  public void registerAlias(String alias, String value) {
    try {
      registerAlias(alias, Resources.classForName(value));
    } catch (ClassNotFoundException e) {
      throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
    }
  }
  
  /**
   * 獲取儲存別名的HashMap, Configuration物件持有對TypeAliasRegistry的引用,因此,如果需要,我們可以通過Configuration物件獲取
   */
  public Map<String, Class<?>> getTypeAliases() {
    return Collections.unmodifiableMap(TYPE_ALIASES);
  }

}

 

由原始碼可見,設定別名的原理就這麼簡單,Mybatis預設給我們設定了不少別名,在上面程式碼中都可以見到。

好啦,本篇內容就是這麼簡單,到此為止。 下篇將繼續講解還沒講完的配置節點。

https://yq.aliyun.com/articles/52669

MyBatis一對多雙向關聯——MyBatis學習筆記之七

處理has-one關係需要用到association元素,
而處理has many關係則需要用到collection元素。
例如本例中,假設一名教師可同時指導多名學生,下面就來介紹如何使用collection元素來實現這種對映,
具體的任務是查詢出教師及其指導的多個學生的資訊(本示例原始碼下載頁面:http://down.51cto.com/data/490947)。

一、為Teacher實體增加相關屬性

      為教師實體增加指導學生集合的屬性如下:

private List<Student> supStudents;//指導學生

 併為其增加setter和getter方法,這裡略過

二、TeacherMapper介面

 為實現教師實體對映,應先建立對映器介面如下:

package com.abc.mapper;
import com.abc.domain.Teacher;
public interface TeacherMapper {
public Teacher getById(int id);
}

三、對映檔案

為教師實體建立的對映檔案如下:

<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--與以前一樣,namespace的值是對應的對映器介面的完整名稱-->
<mapper namespace="com.abc.mapper.TeacherMapper">
<!--TeacherMapper介面中getById方法對應的SQL語句。
查詢教師及其指導的學生的資訊。
由於教師、學生都有id、name、gender等屬性,因此給教師的欄位都起了別名-->
<select id="getById" parameterType="int" resultMap="supervisorResultMap">
select t.id t_id, t.name t_name, t.gender t_gender,
t.research_area t_research_area, t.title t_title,
s.id,s.name, s.gender,s.major,s.grade
from teacher t,student s where t.id=#{id}
and s.supervisor_id = t.id
</select>
<!--教師實體對映-->
<resultMap id="supervisorResultMap" type="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
<result property="gender" column="t_gender"/>
<result property="researchArea" column="t_research_area"/>
<result property="title" column="t_title"/>
<!--collection元素對映教師的指導學生集合的屬性。
resultMap以名稱空間名.resultMap的id的形式,引用studentResultMap。
需要注意的是,上面的select語句中學生的欄位名/別名應與studentResultMap中的column屬性一致-->
<collection property="supStudents"   resultMap="com.abc.mapper.StudentMapper.studentResultMap"/>
</resultMap>
</mapper>

 相應地,學生實體的對映檔案如下:

<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.abc.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<id property="id" column="id"/>
<result property="name"   column="name"/>
<result property="gender" column="gender"/>
<result property="major"  column="major"/>
<result property="grade"  column="grade"/>
<!--相應地,在此引用supervisorResultMap,亦採用
名稱空間名.resultMap的id的形式。-->
<association property="supervisor"
resultMap="com.abc.mapper.TeacherMapper.supervisorResultMap"/>
</resultMap>
</mapper>

在工程的src\resources目錄下新建子目錄mappers,用來統一存放對映檔案。為了能讓MyBatis找到這些對映檔案,修改其核心配置檔案configuration.xml中的mappers元素如下:

<!--在classpath中以相對路徑的形式引用對映檔案-->
<mappers>
<mapper resource="resources/mappers/StudentMapper.xml"/>
<mapper resource="resources/mappers/TeacherMapper.xml"/>
</mappers>

注意:resources目錄在工程編譯前會被複制到classes目錄下(詳見工程生成檔案build.xml中的copy-resources和compile這兩個target),而classes目錄會被ant新增到classpath中。

四、執行類

執行類為CollectionDemo,其內容如下:

package com.demo;
import org.springframework.context.ApplicationContext;
import com.abc.mapper.StudentMapper;
import com.abc.mapper.TeacherMapper;
import com.abc.domain.Teacher;
import com.abc.domain.Student;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class CollectionDemo
{
private static ApplicationContext ctx;
static
{
//在類路徑下尋找resources/beans.xml檔案
ctx = new ClassPathXmlApplicationContext("resources/beans.xml");
}
public static void main(String[] args)
{
//從Spring容器中請求對映器
TeacherMapper mapper =
(TeacherMapper)ctx.getBean("teacherMapper");
//查詢id為1的教師
Teacher teacher = mapper.getById(1);
if(teacher == null)
{
System.out.println("未找到相關教師資訊。");
}
else
{
//教師資訊
System.out.println("**********************************************");
System.out.println("教師姓名:" + "  " + teacher.getName());
System.out.println("教師職稱:" + "  " + teacher.getTitle());
System.out.println("**********************************************");
System.out.println("指導學生資訊:");
//遍歷指導的學生
for(Student s : teacher.getSupStudents())
{
System.out.println("**********************************************");
System.out.println( s.getName() + "  " + s.getGender() + "  " + s.getGrade()
+ "  " + s.getMajor());
//從學生端訪問教師
System.out.println("指導教師研究方向:" + s.getSupervisor().getResearchArea());
}
System.out.println("**********************************************");
}
}
}

http://legend2011.blog.51cto.com/3018495/973579

 

相關文章