天天看點

Mybatis表對象繼承實作        Mybatis表對象繼承

       我們一般用表來表現對象之間的繼承關系時通常有三種方式。第一種是把所有對象包含的屬性都存放在一張表中,然後用一個字段來區分目前記錄對應的對象類型;第二種是每個子類型一張表,每張表都存該對象所有的屬性;第三種是基類作為一張表進行存儲,每個子類特性的屬性都建立一張表進行儲存,然後在基類對應的表裡面通過一個字段來區分對象的類型。第二種跟普通的處理方式沒有什麼區;第一種和第三種大同小異,本文将主要圍繞第三種方式的實作來描述。

       舉個例子,就拿我們的組織關系來說吧,組織上有機構、部門、崗位、人員,它們都屬于組織,從對象的角度來講,可以把它們共性的東西,如ID、上級ID、名稱等抽取出來作為一個基類,其它特性的東西在子類中表現。本文隻是為了表明這種繼承的意思,會簡化很多。為此,我們可以定義一個基類OrganizationBase,其定義如下。

/**

 * 組織基類

 *

 * @author Elim 2016年12月17日

 */

public abstract class OrganizationBase {

   /**

    * 主鍵

    */

   private Long id;

    * 名稱

   private String name;

    * 類型,1:機構,2:部門,3:崗位,4:個人

   private Integer type;

    * 父級組織的ID

   private Integer parentId;

   protected OrganizationBase(Integer type) {

      this.type = type;

   }

    * @return the id

   public Long getId() {

      return id;

    * @param id

    *            the id to set

   public void setId(Long id) {

      this.id = id;

    * @return the name

   public String getName() {

      return name;

    * @param name

    *            the name to set

   public void setName(String name) {

      this.name = name;

    * @return the type

   public Integer getType() {

      return type;

    * @param type

    *            the type to set

   public void setType(Integer type) {

    * @return the parentId

   public Integer getParentId() {

      return parentId;

    * @param parentId

    *            the parentId to set

   public void setParentId(Integer parentId) {

      this.parentId = parentId;

}

       在基類中的type屬性是用來區分組織類型的,機構、部門、崗位都沒有自己的特殊屬性,都完全從父類繼承,但是從程式的角度來講,它們的類定義還是需要的,為此我們定義了Organization、Department、Post類,它們的類定義的結構類似,其中Organization類的定義如下。

 * 機構

 * @author Elim

 * 2016年12月17日

public class Organization extends OrganizationBase {

   public Organization() {

      super(OrgType.ORG);

       我們給員工定義了一個Person類,其簡單的擴充了mobile屬性和email屬性,具體定義如下。

 * 員工

public class Person extends OrganizationBase {

    * 員工的手機号碼

   private String mobile;

    * 員工的郵箱位址

   private String email;

   public Person() {

      super(OrgType.PERSON);

    * @return the mobile

   public String getMobile() {

      return mobile;

    * @param mobile the mobile to set

   public void setMobile(String mobile) {

      this.mobile = mobile;

    * @return the email

   public String getEmail() {

      return email;

    * @param email the email to set

   public void setEmail(String email) {

      this.email = email;

       組織基類型對應的表和員工對應的表的MySQL建表語句如下。

create table t_org(id int primary key auto_increment, name varchar(100), org_type int, parent_id int);

create table t_person(id int,mobile varchar(20), email varchar(100));

       我們的增删改查都應該是基于主表和擴充表來的,沒有擴充表的除外。我們先來說增、删、改,查放到最後來說。針對OrganizationBase,我們建立對應的OrganizationBaseMapper.xml檔案,其增、删、改定義如下。

   <insert id="insert" parameterType="com.elim.learn.mybatis.model.OrganizationBase" useGeneratedKeys="true" keyProperty="id" keyColumn="id">

      insert into t_org(id,name,org_type,parent_id) values(#{id},#{name},#{type},#{parentId})

   </insert>

   <insert id="delete" parameterType="java.lang.Long">

      delete t_org where id=#{id}

   <update id="update" parameterType="com.elim.learn.mybatis.model.OrganizationBase">

      update t_org

      <set>

        <if test="name != null">

           name = #{name},

        </if>

        <if test="type != null">

           org_type = #{type},

        <if test="parentId != null">

           parent_id = #{parentId}

      </set>

      where id = #{id}

   </update>

       對于沒有擴充屬性的組織來講,其增、删、改操作直接通過基表對應的增、删、改操作即可,而對于有擴充屬性的而言,其增、删、改操作應當還要包括對應的擴充表的操作,如我們的員工。針對員工資訊,建立對應的PersonMapper.xml檔案,其中的增、删、改定義如下。

   <insert id="insert" parameterType="com.elim.learn.mybatis.model.Person">

      insert into t_person(id,email,mobile) values(#{id},#{email},#{mobile})

      delete t_person where id=#{id}

   <update id="update" parameterType="com.elim.learn.mybatis.model.Person">

      update t_person

        <if test="email != null">

           email = #{email},

        <if test="mobile != null">

           mobile = #{mobile},

       我們在新增沒有擴充表的記錄時,隻需要操作OrganizationBaseMapper中的對應操作即可,如下面示例的testInsert();而如果我們是操作有擴充表的,則需要同時操作多個Mapper,如下面示例的testInsertPerson()。

   @Test

   public void testInsert() {

      Organization orgBase = new Organization();

      orgBase.setName("TEST_ORG");

      this.orgBaseMapper.insert(orgBase);

   public void testInsertPerson() {

      Person person = new Person();

      person.setName("ZhangSan");

      person.setEmail("[email protected]");

      person.setMobile("15889898989");

      person.setParentId(1);

      this.orgBaseMapper.insert(person);

      this.personMapper.insert(person);

       删與改操作是類似的,這裡就不再給出對應的測試示例了,有興趣的朋友可以自己試一下。至于查而言,那麼我們又有兩種方式了,一種是基表的查詢隻查詢通用資訊,其也可以通過區分字段來區分不同的類型,有需要的時候再去查子表,而子表在查詢的時候将所有的資訊都查詢出來。另一種是直接在基表中通過左連接配接查詢出所有的資訊。這裡以方式二為例,假設我們有一個根據ID查詢組織的需求,我們在OrganizationBaseMapper.xml中定義了一個findById的查詢,語句如下。

   <select id="findById" resultMap="baseResultMap" parameterType="java.lang.Long">

      select a.id,a.name,a.org_type,a.parent_id,b.mobile,b.email from t_org a left join t_person b on a.id=b.id where a.id=#{id}

   </select>

       接着來看一下我們的baseResultMap的定義。

   <resultMap type="com.elim.learn.mybatis.model.OrganizationBase" id="baseResultMap">

      <id column="id" property="id" />

      <result column="name" property="name" />

      <result column="parent_id" property="parentId" />

      <discriminator javaType="java.lang.Integer" column="org_type">

        <case value="1" resultType="com.elim.learn.mybatis.model.Organization" />

        <case value="2" resultType="com.elim.learn.mybatis.model.Department" />

        <case value="3" resultType="com.elim.learn.mybatis.model.Post" />

        <case value="4" resultMap="PersonResultMap" />

      </discriminator>

   </resultMap>

   <resultMap type="com.elim.learn.mybatis.model.Person" id="PersonResultMap">

      <result column="email" property="email" />

      <result column="mobile" property="mobile" />

       在ResultMap定義中,我們通過discriminator元素來指定類型區分列,并在其下通過case元素指定各種區分列的值對應的傳回結果類型或ResultMap。我們可以看到對于沒有其它擴充屬性的,我們可以直接通過case元素指定傳回類型,如case等于1的情況;而有擴充屬性的,則可以通過case元素指定擴充屬性對應的resultMap,然後再對應的resultMap中再指定擴充屬性對應的映射,如case等于4的情況。其實我們也可以直接把結果集映射放到case中,但此時我們需要指定case元素的resultType屬性來指定對應的類型,如:

        <case value="4" resultType="com.elim.learn.mybatis.model.Person">

           <result column="mobile" property="mobile"/>

           <result column="email" property="email"/>

        </case>

       我們還可以通過resultMap的繼承機制來定義結果集映射,先定義一個公共屬性映射,然後在最頂層的時候就通過discriminator來區分不同的值對應不同的結果集映射,各個子類型的結果集映射都繼承公用的結果集映射,如:

   <resultMap type="com.elim.learn.mybatis.model.OrganizationBase"

      id="baseResultMap">

        <case value="1" resultMap="OrganizationResultMap" />

        <case value="2" resultMap="DepartmentResultMap" />

        <case value="3" resultMap="PostResultMap" />

   <!-- 公共部分的結果集映射 -->

      id="OrganizationBaseResultMap">

      <result column="org_type" property="type" />

   <!-- 機構結果映射,繼承自OrganizationBaseResultMap -->

   <resultMap type="com.elim.learn.mybatis.model.Organization"

      id="OrganizationResultMap" extends="OrganizationBaseResultMap" />

   <resultMap type="com.elim.learn.mybatis.model.Department" id="DepartmentResultMap"

      extends="OrganizationBaseResultMap" />

   <resultMap type="com.elim.learn.mybatis.model.Post" id="PostResultMap"

參考文檔

<a href="http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps">http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps</a>

(注:本文是基于Mybatis3.3.1所寫)