Mybatis表对象继承实现

本文介绍使用MyBatis实现表对象继承的方法,通过基类和扩展类的方式处理不同类型的组织结构,涵盖增删改查操作及联合查询的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       Mybatis表对象继承

       我们一般用表来表现对象之间的继承关系时通常有三种方式。第一种是把所有对象包含的属性都存放在一张表中,然后用一个字段来区分当前记录对应的对象类型;第二种是每个子类型一张表,每张表都存该对象所有的属性;第三种是基类作为一张表进行存储,每个子类特性的属性都新建一张表进行保存,然后在基类对应的表里面通过一个字段来区分对象的类型。第二种跟普通的处理方式没有什么区;第一种和第三种大同小异,本文将主要围绕第三种方式的实现来描述。

 

       举个例子,就拿我们的组织关系来说吧,组织上有机构、部门、岗位、人员,它们都属于组织,从对象的角度来讲,可以把它们共性的东西,如ID、上级ID、名称等抽取出来作为一个基类,其它特性的东西在子类中表现。本文只是为了表明这种继承的意思,会简化很多。为此,我们可以定义一个基类OrganizationBase,其定义如下。

/**

 * 组织基类

 *

 * @author Elim 20161217

 */

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) {

      this.type = type;

   }

 

   /**

    * @return the parentId

    */

   public Integer getParentId() {

      return parentId;

   }

 

   /**

    * @param parentId

    *            the parentId to set

    */

   public void setParentId(Integer parentId) {

      this.parentId = parentId;

   }

 

}

 

       在基类中的type属性是用来区分组织类型的,机构、部门、岗位都没有自己的特殊属性,都完全从父类继承,但是从程序的角度来讲,它们的类定义还是需要的,为此我们定义了OrganizationDepartmentPost类,它们的类定义的结构类似,其中Organization类的定义如下。

/**

 * 机构

 * @author Elim

 * 20161217

 */

public class Organization extends OrganizationBase {

 

   public Organization() {

      super(OrgType.ORG);

   }

 

}

 

       我们给员工定义了一个Person类,其简单的扩充了mobile属性和email属性,具体定义如下。

/**

 * 员工

 * @author Elim

 * 20161217

 */

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}

   </insert>

  

   <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>

        <if test="parentId != null">

           parent_id = #{parentId}

        </if>

      </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})

   </insert>

  

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

      delete t_person where id=#{id}

   </insert>

  

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

      update t_person

      <set>

        <if test="email != null">

           email = #{email},

        </if>

        <if test="mobile != null">

           mobile = #{mobile},

        </if>

      </set>

      where id = #{id}

   </update>

 

       我们在新增没有扩展表的记录时,只需要操作OrganizationBaseMapper中的对应操作即可,如下面示例的testInsert();而如果我们是操作有扩展表的,则需要同时操作多个Mapper,如下面示例的testInsertPerson()

   @Test

   public void testInsert() {

      Organization orgBase = new Organization();

      orgBase.setName("TEST_ORG");

      this.orgBaseMapper.insert(orgBase);

   }

  

   @Test

   public void testInsertPerson() {

      Person person = new Person();

      person.setName("ZhangSan");

      person.setEmail("zhangsan@163.com");

      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>

 

       ResultMap定义中,我们通过discriminator元素来指定类型区分列,并在其下通过case元素指定各种区分列的值对应的返回结果类型或ResultMap。我们可以看到对于没有其它扩展属性的,我们可以直接通过case元素指定返回类型,如case等于1的情况;而有扩展属性的,则可以通过case元素指定扩展属性对应的resultMap,然后再对应的resultMap中再指定扩展属性对应的映射,如case等于4的情况。其实我们也可以直接把结果集映射放到case中,但此时我们需要指定case元素的resultType属性来指定对应的类型,如:

   <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" resultType="com.elim.learn.mybatis.model.Person">

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

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

        </case>

      </discriminator>

   </resultMap>

 

       我们还可以通过resultMap的继承机制来定义结果集映射,先定义一个公共属性映射,然后在最顶层的时候就通过discriminator来区分不同的值对应不同的结果集映射,各个子类型的结果集映射都继承公用的结果集映射,如:

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

      id="baseResultMap">

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

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

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

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

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

      </discriminator>

   </resultMap>

 

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

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

      id="OrganizationBaseResultMap">

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

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

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

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

   </resultMap>

 

   <!-- 机构结果映射,继承自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"

      extends="OrganizationBaseResultMap" />

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

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

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

   </resultMap>

 

参考文档

http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps

 

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

 

 

<think>好的,我现在需要帮助用户解决关于MyBatis-Plus实现联查的问题。用户已经提供了一些引用内容,我需要仔细分析这些信息,并结合自己的知识来给出准确的回答。 首先,用户提到的引用[1]和[2]都是关于MyBatis-Plus的多联查,特别是使用了MPJLambdaWrapper和自定义DTO的方法。用户的需求是找到实现联查的方法或示例,所以需要重点讲解这两种方法,并给出具体的代码示例。 接下来,我需要确认MyBatis-Plus本身是否支持多联查。根据我所知,MyBatis-Plus的核心功能并不直接支持复杂的联查询,通常需要借助插件,比如mybatis-plus-join。用户提供的引用中也提到了这个插件,所以应该详细说明如何使用它。 然后,我需要分步骤解释两种方法:使用MPJLambdaWrapper和自定义SQL。对于第一种方法,需要说明如何添加依赖、创建DTO、配置Mapper接口以及编写查询代码。每个步骤都要具体,确保用户能够跟随操作。例如,添加Maven依赖时,要给出正确的groupId和artifactId,避免用户混淆。 第二种方法是使用自定义SQL,这可能更适合复杂的查询情况。需要指导用户如何在XML文件中编写SQL语句,并定义ResultMap来处理结果映射。这部分要注意示例的准确性,比如别名和字段映射的正确性。 同时,需要注意用户可能的疑问点,比如是否需要额外的插件、如何处理结果集映射、分页查询如何实现等。在回答中适当提及这些注意事项,可以增强回答的实用性。 另外,用户提供的示例代码中有使用MPJBaseMapper,需要强调用户必须继承这个接口,否则可能无法使用联查功能。这点很容易被忽略,导致用户遇到问题时无法解决。 最后,生成的相关问题需要围绕用户可能的后续疑问展开,比如性能优化、条件构造器的其他方法、分页查询的实现等,这样可以帮助用户进一步探索解决方案。 总结来说,回答的结构应该清晰,分步骤讲解两种方法,提供代码示例,并指出注意事项和常见问题。同时,引用提供的参考文献,确保信息的可靠性。需要避免使用过于专业的术语,保持语言简洁明了,方便用户理解。</think>以下为您详细介绍MyBatis-Plus实现联查的两种典型方式: ### 一、使用mybatis-plus-join插件(推荐) 需要添加依赖: ```xml <dependency> <groupId>com.github.yulichang</groupId> <artifactId>mybatis-plus-join</artifactId> <version>最新版本</version> </dependency> ``` #### 1. 创建DTO接收结果 ```java @Data public class UserOrderDTO { private Long userId; private String userName; private String orderNo; private LocalDateTime createTime; } ``` #### 2. Mapper接口配置 ```java @Mapper public interface OrderMapper extends MPJBaseMapper<Order> { } ``` #### 3. 联查实现示例 ```java public List<UserOrderDTO> getOrdersWithUsers() { MPJLambdaWrapper<Order> wrapper = new MPJLambdaWrapper<>(); wrapper.selectAll(Order.class) // 主全部字段 .select(User::getUserName) .leftJoin(User.class, User::getId, Order::getUserId) .eq(Order::getStatus, 1); return orderMapper.selectJoinList(UserOrderDTO.class, wrapper); } ``` ### 二、使用自定义SQL(复杂场景) #### 1. XML映射文件 ```xml <select id="selectOrderWithUser" resultMap="orderUserMap"> SELECT o.*, u.name as user_name FROM order o LEFT JOIN user u ON o.user_id = u.id WHERE o.status = #{status} </select> <resultMap id="orderUserMap" type="com.example.UserOrderDTO"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <result property="userName" column="user_name"/> </resultMap> ``` #### 2. Mapper接口方法 ```java @Mapper public interface OrderMapper extends BaseMapper<Order> { List<UserOrderDTO> selectOrderWithUser(@Param("status") Integer status); } ``` ### 三、注意事项 1. 联查询时推荐使用`MPJLambdaWrapper`进行类型安全的字段引用[^2] 2. 分页查询需配合`Page`对象使用: ```java Page<UserOrderDTO> page = orderMapper.selectJoinPage(new Page<>(1,10), UserOrderDTO.class, wrapper); ``` 3. 支持多重关联查询: ```java wrapper.leftJoin(Address.class, Address::getUserId, User::getId) .select(Address::getDetail); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值