Mybatis中xml各种元素实用总结

52 阅读5分钟

MyBatis的xml对于开发者的意义

mybatis相较于hibernate的区别在于myabtis是一种半自动的映射sql框架,所以我们不可避免都需要写一些xml,从而达到我们开发业务的水平,那么本篇博客就总结一下常用的xml各类元素。

核心元素

首先就是我们常用的增删改查操作的四个元素

  • <select>:数据库查询
    可以输入输出根据下面的属性来制定, 关键属性如下

    • id:标识符,用于映射到Mapper接口方法。
    • resultType:定义返回对象的类型。
    • parameterType:定义输入参数的类型。
    • resultMap:定义结果映射的映射器,用于复杂类型的映射。

    实例:

     <select id="selectUser" parameterType="int" resultType="com.example.User">
       SELECT * FROM users WHERE id = #{id}
     </select>
    
  • <insert>:插入
    可以进行批量插入,最后返回插入行的ID, 关键属性

    • id:映射到Mapper接口方法的标识符。
    • parameterType:输入参数的类型。
    • useGeneratedKeys:是否使用数据库自动生成的主键。
    • keyProperty:指定自动生成的主键应该映射到的Java对象属性。

    示例

    ```xml
    <insert id="insertUser" parameterType="com.example.User">
      INSERT INTO users (name, email) VALUES (#{name}, #{email})
    </insert>
    ```
    
  • <update>:更新
    最后返回更新条数,关键属性

    • id:映射到Mapper接口方法的标识符。
    • parameterType:输入参数的类型。

    示例

    <update id="updateUser" parameterType="com.example.User">
      UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
    </update>
    
  • <delete>:删除 返回删除的条数,关键属性

    • id:映射到Mapper接口方法的标识符。
    • parameterType:输入参数的类型。

    示例

    <delete id="deleteUser" parameterType="int">
      DELETE FROM users WHERE id = #{id}
    </delete>
    

当然如果有一些代码是可以复用的,我们可以使用下面两个元素将它们抽离出来

  • <sql>:用于定义可重用的SQL代码片段。

  • <include>:如何使用此标签引入<sql>片段。

    实例:

    <sql id="userColumns">
        username, email, status
    </sql>
    
    <select id="selectUsers" resultType="com.example.User">
      SELECT 
        <include refid="userColumns"/>
      FROM users
    </select>
    

xml与代码的映射关系

xml可以通过各种不同的元素与Mybatis进行结果的映射,提供了比如resultMapresultTypeparameterType等元素,那么我们可以通过这些标签去自定义我们的映射关系

  • <parameterType>:这个元素主要就是方便我们传入的时候指定元素,一般是我们的实体类或者是Map,当然我们如果不指定也能使用,相当于范式一样的规范,这里就不演示了

  • <resultType>:这种情况一般是我们在外面定义好了实体,然后我们可以直接将这个实体加到标签上面,就可以实现映射

    <select id="selectAllUsers" resultType="com.example.User">
      SELECT * FROM users
    </select>
    
  • <resultMap>:它是MyBatis中最复杂也最强大的元素,当我们的字段与实体字段没有很好的映射时,resultMap用于定义如何从数据库结果集中映射到Java对象。

    这个resultMap我们可以直接写到xml中,然后我们使用resultType在引用它,例如

    <resultMap id="voMap" type="java.util.Map">
        <id property="Id" column="Id"/>
        <result property="Name" column="Name"/>
        <result property="count" column="count"/>
    </resultMap>
    <select id="getVisitPage" resultType="voMap">
        ...
    </select>
    

这里面column对应的是数据库的列名或别名;property对应的是结果集的字段或属性。


除了上面的实例之外,它还有别的用法,比如:我们有如下两个案例,一个是我们的元素要返回的对象中需要关联一个对象,另外一个需要关联集合,那么这个时候我们就需要使用如下的两个嵌套标签

association嵌套对象

假设有两个实体类UserUserProfile,其中User有一个UserProfile的引用,对应于一对一关系。

  • User类:

    public class User {
        private Long id;
        private String username;
        private UserProfile profile; // 与UserProfile的一对一关系
        // getters and setters...
    }
    
  • UserProfile类:

    public class UserProfile {
        private Long id;
        private String address;
        private String phone;
        // getters and setters...
    }
    

针对这种一对一关系,就可以在<resultMap>中使用<association>标签定义关联:

  • resultMap:

    <resultMap id="userMap" type="User">
        <id property="id" column="user_id"/>
        <result property="username" column="username"/>
        <association property="profile" javaType="UserProfile">
            <id property="id" column="profile_id"/>
            <result property="address" column="address"/>
            <result property="phone" column="phone"/>
        </association>
    </resultMap>
    

collection 嵌套集合

假设User类包含多个Order对象的集合,对应于一对多关系。

  • User类 (扩展):

    public class User {
        // ... 其他属性 ...
        private List<Order> orders; // 与Order的一对多关系
        // getters and setters...
    }
    
  • Order类:

    public class Order {
        private Long id;
        private String orderDetails;
    }
    

针对这种一对多关系,您可以在<resultMap>中使用<collection>标签定义关联:

  • resultMap:

    <resultMap id="userMap" type="User">
        <!-- User属性的映射 -->
        <collection property="orders" ofType="Order">
            <id property="id" column="order_id"/>
            <result property="orderDetails" column="order_details"/>
        </collection>
    </resultMap>
    

动态sql

讲完上面的一些核心用法,那么下面我在讲讲如何利用一些动态sql提高我们的代码质量

<where><set>

这里可以自动帮我们处理ANDOR等,避免一些没用的sql语句,比如我们拼接sql的过程中搭配<if>标签使用的时候
   <select id="findUsers" resultType="User">
 SELECT * FROM users
 <where>
   <if test="username != null">
     AND username = #{username}
   </if>
  </where>
   </select>
   
   <update id="updateUser">
     UPDATE users
     <set>
       <if test="username != null">
         username = #{username},
       </if>
       <if test="email != null">
         email = #{email}
       </if>
     </set>
     WHERE id = #{id}
   </update>

使用这两元素之后我们就只管拼接sql就可以了,不需要担心出现where AND ...从而报错的发生

<foreach> - 集合处理

用途主要就是用于遍历集合,常用于构建动态的IN查询。

示例:查询多个用户ID。

 <select id="findUsersByIds" resultType="User">
   SELECT * FROM users
   WHERE id IN
   <foreach item="id" collection="ids" open="(" separator="," close=")">
     #{id}
   </foreach>
 </select>

<choose>, <when>, <otherwise>

这些元素一般组合使用,类似于Java中的switch语句,用于在多个条件中选择一个执行,直接看下面实例

示例:根据不同的用户状态选择不同的查询条件。

 <select id="findUserByStatus" resultType="User">
   SELECT * FROM users
   <where>
     <choose>
       <when test="status == 'active'">
         AND status = 'active'
       </when>
       <when test="status == 'inactive'">
         AND status = 'inactive'
       </when>
       <otherwise>
         AND status = 'pending'
       </otherwise>
     </choose>
   </where>
 </select>

开发心得

最后是我在写xml中的一些技巧分享吧

  • 能多使用提供的函数就尽量使用一些xml提供的函数,如:

    在比较数字的时候可以使用函数将数字转化为varchar类型比较

    <if test="phone != '' and phone != null ">
        and u.phone = #{phone, jdbcType=VARCHAR}
    </if>
    

    在使用模糊查询的时候,使用拼接函数对字段进行拼接

    <if test="name != '' and name != null ">
        and u.name like CONCAT('%',#{name},'%')
    </if>
    

    在判断集合为空的时候,使用函数判断isEmpty()集合是否为空

    <if test="map.userList != null and !map.userList.isEmpty()">
        AND xro.user_id IN
        <foreach collection="map.userList" item="item" index="index" open="(" separator="," close=")">
            #{item}
        </foreach>
    </if>
    
  • 我们在入参的时候,我一般都会将实体类对象转化为map在进行传入,然后我会在代码层面将数据尽量处理好,比如:

    Map<String, Object> map = new HashMap<>(); // new HashMap<>();// CollUtil.newHashMap();
    if (StrUtil.isNotEmpty(request.getNikename())) {
        String nikeName = URLUtil.decode(request.getName());
        map.put("name", nikeName);
    }
    if (ObjectUtil.isNotNull(request.getStatus())) {
        map.put("status", request.getStatus() ? 1 : 0);
    }
    dao.getList(map);
    

尽量减轻数据库压力,从而提高代码效率