天天看點

MyBatis之關聯映射解析

ORM架構一個重要的技術點是處理對象間的關聯映射,比如一對一,一對多的關系,和Hibernate不同的是,Mybatis的使用需要開發人員直接和SQL語句進行打交道,是以在處理關聯映射的時候不論是檔案配置還是實作原理都是大有不同的,本文緻力于使用一個例子講清楚不同關聯映射關系的配置和使用方法,以及作者在使用過程中對不同的參數的作用的深入了解。

對象關系

我們有這樣的一組對象關系,簡單來說,一個雇員(Employee)有一個工卡(WorkCard),有多個員工任務(EmployeeTask),員工任務僅包含一個任務(Task),對于員工來還有體檢表(HealthForm),但是由于有體檢的内容男女有别,是以我們從Employee繼承出來FemaleEmployee和MaleEmpolyee,分别包含FemaleHealthForm和MaleHealthForm。

MyBatis之關聯映射解析

從表的結構來看,其關鍵的外鍵關系如下

MyBatis之關聯映射解析

Mybatis配置

我們從簡單的EmployeeTask和Task之間的一對一關系來說,無論是從Java Bean的角度來看還是從表的關系來看,都是EmployeeTask在維護關聯關系。我們所說的關聯關系的使用更多的是在查詢的時候,也就是在查詢的時候能夠以級聯的查詢出關聯的對象。對于插入的時候,因為Mybatis的實作原理是開發人員自定義SQL語句,是以不能實作級聯的插入,我們隻需要提供一個關聯對象相關聯的外鍵,就像在插入EmployeeTask的時候如何處理Task對象這個屬性這樣(點選這裡)

MyBatis之關聯映射解析

一對一查詢

是以我們接下來更加着重考慮在查詢操作(select)的時候的級聯映射的配置,也就是對select後的resultMap如何配置(resultMap是描述從資料庫中查詢出的資料列和pojo的屬性之間如何映射,如果列名和屬性名一緻的可以不用配置,會進行自動映射)。 拿EmployeeTask來說,對于其查詢語句我們有

<select id="getEmployeeTaskByEmpId" parameterType="long" resultMap="empTaskResultMap">
        select * from t_employee_task where emp_id=#{empId}
    </select>
           

其中empTaskResultMap的配置如下

<resultMap id="empTaskResultMap" type="multiAssociation.pojo.EmployeeTask">
        <id column="id" property="id"></id>
        <result column="emp_id" property="empId"/>
        <result column="task_name" property="taskName"/>
        <association column="task_id" property="task" select="multiAssociation.pojo.TaskMapper.getTaskById"/>
        <!-- select語句對應的是對應mapper xml中方法的id-->
    </resultMap>
           

這裡面我們對列名和屬性名不一緻的情況進行了配置,并且使用到了association标簽,這個标簽是用來關聯一個對象的,解讀一下這個語句的意思就是:将列task_id的值左為參數傳入multiAssociation.pojo.TaskMapper.getTaskById作為參數查詢後作為task屬性的值,其中multiAssociation.pojo.TaskMapper.getTaskById是TaskMapper配置檔案中指定的查詢語句,這種方式稱為嵌套查詢,是以TaskMapper中必須有對應的方法才可以,也就是下面的配置

<select id="getTaskById" parameterType="long" resultType="multiAssociation.pojo.Task">
        select * from t_task where id=#{id}
    </select>
           

當然我們也可以通過另一種方式,就是在getEmployeeTaskByEmpId的select語句中使用連接配接查詢,然後将對應的task列對應的值映射到對應的屬性上也是可以的

一對多映射

一對多映射使用collection标簽,它的使用方法和association是一樣的,我們使用Employee來做為示例,它有一個一對一關系和一個一對多的關系,它的resultMap配置如下

<resultMap id="employeeResultMap" type="multiAssociation.pojo.Employee">
        <id column="id" property="id"></id>
        <result column="real_name" property="realName"></result>
        <result column="sex" property="sex" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
        <result column="POSITION" property="position"/>

        <association property="workCard" column="id" select="multiAssociation.pojo.WorkCardMapper.getWorkCardByEmpId"/>

        <collection property="employeeTaskList" column="id" select="multiAssociation.pojo.EmployeeTaskMapper.getEmployeeTaskByEmpId"/>
        <discriminator javaType="long" column="sex">
            <!--最後傳回的resultMap會取代原來的resultMap,是以後面的的resultMap要extends-->
            <case value="0" resultMap="femaleEmployeeResultMap"></case>
            <case value="1" resultMap="maleEmployeeFormResultMap"/>
        </discriminator>
    </resultMap>
           

可以看到association标簽和collection标簽的使用幾乎一模一樣,那麼它們對于select标簽使用的嵌套方法有沒有什麼特殊的要求,答案是沒有的,這裡嵌套的select依賴于對應的Mapper的xml檔案配置的,而不依賴于具有明确傳回類型(單個對象或者對象清單)Mapper接口方法,從這個角度來說,xml配置問題不是強依賴于接口的。直覺的來說multiAssociation.pojo.EmployeeTaskMapper.getEmployeeTaskByEmpId雖然是配置到collection的嵌套查詢中,但是在對應的Mapper接口中其傳回值可能僅僅是一個對象。

另外大家可能注意到了discriminator标簽,這段配置的意思根據查詢結果的sex列的值,如果sex為0,則傳回femaleEmployeeResultMap作為最終結果,如果是1,則傳回maleEmployeeFormResultMap作為結果,當然,我們也需要femaleEmployeeResultMap和maleEmployeeFormResultMap繼承上面employeeResultMap的基礎屬性,這個使用extends來實作,進而實作了有判别的傳回類對象。

<resultMap id="femaleEmployeeResultMap" type="multiAssociation.pojo.FemaleEmployee" extends="employeeResultMap">
        <association property="femaleHealthForm" column="id" select="multiAssociation.pojo.FemaleHealthFormMapper.getFemaleHealthFormWithEmpId"/>
    </resultMap>

    <resultMap id="maleEmployeeFormResultMap" type="multiAssociation.pojo.MaleEmployee" extends="employeeResultMap">
        <association property="maleHealthForm" column="id" select="multiAssociation.pojo.MaleHealthFormMapper.getMaleHealthFormWithEmpId"/>
    </resultMap>
           

關于源碼

你可以在這裡找到本文的所有源碼,本文發表于我的部落格,歡迎關注!

參考文檔

Mapper-XML檔案 Result_Maps

繼續閱讀