我們一般用表來表現對象之間的繼承關系時通常有三種方式。第一種是把所有對象包含的屬性都存放在一張表中,然後用一個字段來區分目前記錄對應的對象類型;第二種是每個子類型一張表,每張表都存該對象所有的屬性;第三種是基類作為一張表進行存儲,每個子類特性的屬性都建立一張表進行儲存,然後在基類對應的表裡面通過一個字段來區分對象的類型。第二種跟普通的處理方式沒有什麼區;第一種和第三種大同小異,本文将主要圍繞第三種方式的實作來描述。
舉個例子,就拿我們的組織關系來說吧,組織上有機構、部門、崗位、人員,它們都屬于組織,從對象的角度來講,可以把它們共性的東西,如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所寫)