MyBatis之關聯對映解析
ORM框架一個重要的技術點是處理物件間的關聯對映,比如一對一,一對多的關係,和Hibernate不同的是,Mybatis的使用需要開發人員直接和SQL語句進行打交道,所以在處理關聯對映的時候不論是檔案配置還是實現原理都是大有不同的,本文致力於使用一個例子講清楚不同關聯對映關係的配置和使用方法,以及作者在使用過程中對不同的引數的作用的深入理解。
物件關係
我們有這樣的一組物件關係,簡單來說,一個僱員(Employee)有一個工卡(WorkCard),有多個員工任務(EmployeeTask),員工任務僅包含一個任務(Task),對於員工來還有體檢表(HealthForm),但是由於有體檢的內容男女有別,所以我們從Employee繼承出來FemaleEmployee和MaleEmpolyee,分別包含FemaleHealthForm和MaleHealthForm。
從表的結構來看,其關鍵的外來鍵關係如下
Mybatis配置
我們從簡單的EmployeeTask和Task之間的一對一關係來說,無論是從Java Bean的角度來看還是從表的關係來看,都是EmployeeTask在維護關聯關係。我們所說的關聯關係的使用更多的是在查詢的時候,也就是在查詢的時候能夠以級聯的查詢出關聯的物件。對於插入的時候,因為Mybatis的實現原理是開發人員自定義SQL語句,所以不能實現級聯的插入,我們只需要提供一個關聯物件相關聯的外來鍵,就像在插入EmployeeTask的時候如何處理Task物件這個屬性這樣(點選這裡)
一對一查詢
所以我們接下來更加著重考慮在查詢操作(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>
關於原始碼
你可以在這裡找到本文的所有原始碼,本文發表於我的部落格,歡迎關注!