摘要: 深入淺出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