【Mybatis】Mybatis之使用XML实现增删改查

538 阅读5分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

书接上回,上一篇在Mybatis之在源码项目中连接MySQL测试一文中介绍了如何在Mybatis源码项目中连接MySQL进行测试。本文接上文的内容,介绍一下使用Mybatis的XML进行增删改查的方法。

Mybatis版本:3.5.2

基于POJO对象

  • Mapper接口中的方法
    /**
     * 基于POJO的新增XML方式
     */
    int insertXmlPojo(Purchase purchase);
  • XML中的代码,这里的id与Mapper接口中的方法名必须要完全相同,参数类型则是POJO类的完全限定名,整个SQL语句使用insert标签声明
    <insert id="insertXmlPojo" parameterType="org.apache.ibatis.z_run.pojo.Purchase">
        insert into purchase
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">
                id,
            </if>
            <if test="name != null">
                `name`,
            </if>
            <if test="price != null">
                `price`,
            </if>
            <if test="category != null">
                category,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">
                #{id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="price != null">
                #{price,jdbcType=INTEGER},
            </if>
            <if test="category != null">
                #{category,jdbcType=INTEGER},
            </if>
        </trim>
    </insert>
  • 某些场景中,需要将新插入数据的自增主键ID进行返回,可以使用useGeneratedKeyskeyColumnkeyProperty属性一起来实现
    <insert id="insertXmlPojo" parameterType="org.apache.ibatis.z_run.pojo.Purchase" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into purchase
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">
                id,
            </if>
            <if test="name != null">
                `name`,
            </if>
            <if test="price != null">
                `price`,
            </if>
            <if test="category != null">
                category,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">
                #{id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="price != null">
                #{price,jdbcType=INTEGER},
            </if>
            <if test="category != null">
                #{category,jdbcType=INTEGER},
            </if>
        </trim>
    </insert>
  • 测试类代码
    @Test
    public void insert() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Purchase purchase = new Purchase();
        purchase.setName("火腿");
        purchase.setPrice(3);
        purchase.setCategory(1);
        mapper.insertXmlPojo(purchase);
        System.out.println(purchase);
    }
  • 返回的主键ID
DEBUG [main] - ==>  Preparing: insert into purchase ( `name`, `price`, category ) values ( ?, ?, ? ) 
DEBUG [main] - ==> Parameters: 火腿(String), 3(Integer), 1(Integer)
DEBUG [main] - <==    Updates: 1
Purchase{id=10, name='火腿', price=3, category=1}

image.png

  • 数据库中的数据

image.png

  • Mapper接口中的方法
    /**
     * 基于POJO的查询XML方式
     */
    Purchase findXmlPojoByID(Purchase purchase);
  • XML中的方法
    • 语句使用select标签声明
    • 要查询的字段使用sql标签进行提取,并在SQL语句中进行引用
    • 返回值使用resultMap进行映射,column属性表示数据库表中的字段名,property表示POJO类中的属性名
    • 如果Mapper接口中的方法返回值是集合List<Purchase>,XML代码中的resultMap写法也是如此,即resultMap的属性值是集合中元素的完全限定名
    <sql id="Base_Column_List">
        id, `name`, price, category
    </sql>
    
    <resultMap id="BaseResultMap" type="org.apache.ibatis.z_run.pojo.Purchase">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="price" jdbcType="INTEGER" property="price" />
        <result column="category" jdbcType="INTEGER" property="category" />
    </resultMap>
    
    <select id="findXmlPojoByID" parameterType="org.apache.ibatis.z_run.pojo.Purchase" resultMap="BaseResultMap">
        select 
        <include refid="Base_Column_List" />
        from purchase
        where id = #{id,jdbcType=INTEGER}
    </select>
  • 测试类
    @Test
    public void query() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Purchase purchase = new Purchase();
        purchase.setId(6);
        System.out.println(mapper.findXmlPojoByID(purchase));
    }
  • 查询结果
DEBUG [main] - ==>  Preparing: select id, `name`, price, category from purchase where id = ? 
DEBUG [main] - ==> Parameters: 6(Integer)
DEBUG [main] - <==      Total: 1
Purchase{id=6, name='火腿', price=3, category=1}

注意:使用resultMap进行结果集映射时,通常是在数据库列名与POJO属性名不同的情况下,本文中的数据库列名与POJO属性名是相同的,其实没必要写这个resultMap,这里只是做一个演示。

  • Mapper接口中的方法
    /**
     * 基于POJO的修改XML方式
     */
    int updateXmlPojoByID(Purchase purchase);
  • XML中的代码,SQL语句使用update标签声明
    <update id="updateXmlPojoByID" parameterType="org.apache.ibatis.z_run.pojo.Purchase">
        update purchase
        <set>
            <if test="name != null">
                `name` = #{name,jdbcType=VARCHAR},
            </if>
            <if test="price != null">
                `price` = #{price,jdbcType=INTEGER},
            </if>
            <if test="category != null">
                category = #{category,jdbcType=INTEGER},
            </if>
        </set>
        where id = #{id,jdbcType=INTEGER}
    </update>
  • 测试类
    @Test
    public void update() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Purchase purchase = new Purchase();
        purchase.setId(6);
        //将价格修改为6
        purchase.setPrice(6);
        mapper.updateXmlPojoByID(purchase);
    }

image.png

  • Mapper接口中的方法
    /**
     * 基于POJO的删除XML方式
     */
    int deleteXmlPojoByID(Purchase purchase);
  • XML中的代码,SQL使用delete标签声明
    <delete id="deleteXmlPojoByID" parameterType="org.apache.ibatis.z_run.pojo.Purchase">
        delete from purchase where id = #{id,jdbcType=INTEGER}
    </delete>
  • 测试类
    @Test
    public void delete() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Purchase purchase = new Purchase();
        purchase.setId(6);
        mapper.deleteXmlPojoByID(purchase);
    }

基于POJO的方式是主要的方式,虽然需要维护参数POJO,但是代码的可读性较好,易于维护。

基于Map

  • 基于Map的方式进行增删改查就十分的便捷了,只需要将参数中的POJO替换成Map即可
  • Mapper接口中的方法
    /**
     * 新增:基于Map的XML方式
     */
    int insertMapPojo(Map<String, Object> purchase);

    /**
     * 查询:基于Map的XML方式
     */
    Purchase findMapPojoByID(Map<String, Object> purchase);

    /**
     * 修改:基于Map的XML方式
     */
    int updateMapPojoByID(Map<String, Object> purchase);

    /**
     * 删除:基于Map的XML方式
     */
    int deleteMapPojoByID(Map<String, Object> purchase);
  • XML中的代码,与POJO传参方式的区别就是参数类型parameterType,这里是使用的Map
   <insert id="insertMapPojo" parameterType="java.util.Map">
        insert into purchase
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">
                id,
            </if>
            <if test="name != null">
                `name`,
            </if>
            <if test="price != null">
                `price`,
            </if>
            <if test="category != null">
                category,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">
                #{id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="price != null">
                #{price,jdbcType=INTEGER},
            </if>
            <if test="category != null">
                #{category,jdbcType=INTEGER},
            </if>
        </trim>
    </insert>

    <select id="findMapPojoByID" parameterType="java.util.Map" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from purchase
        where id = #{id,jdbcType=INTEGER}
    </select>

    <update id="updateMapPojoByID" parameterType="java.util.Map">
        update purchase
        <set>
            <if test="name != null">
                `name` = #{name,jdbcType=VARCHAR},
            </if>
            <if test="price != null">
                `price` = #{price,jdbcType=INTEGER},
            </if>
            <if test="category != null">
                category = #{category,jdbcType=INTEGER},
            </if>
        </set>
        where id = #{id,jdbcType=INTEGER}
    </update>

    <delete id="deleteMapPojoByID" parameterType="java.util.Map">
        delete from purchase where id = #{id,jdbcType=INTEGER}
    </delete>
  • 测试类,在测试类中,所有的参数都使用Map来进行封装
    @Test
    public void insert() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Map<String, Object> param = new HashMap<>();
        param.put("name", "辣条");
        param.put("price", "1");
        param.put("category", "2");
        mapper.insertMapPojo(param);
    }

    @Test
    public void query() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Map<String, Object> param = new HashMap<>();
        param.put("id", 7);
        System.out.println(mapper.findMapPojoByID(param));
    }

    @Test
    public void update() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Map<String, Object> param = new HashMap<>();
        param.put("id", 7);
        param.put("price", "3");
        mapper.updateMapPojoByID(param);
    }

    @Test
    public void delete() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Map<String, Object> param = new HashMap<>();
        param.put("id", 7);
        mapper.deleteMapPojoByID(param);
    }

基于Map传参的方式一般不使用,原因在于代码的可读性较差,如果只关注于Maper接口中的方法,完全不知道这个方法中传递的参数具体是什么。

基于注解

  • 基于注解的方式一般用于参数较少(参数个数大于1,当参数个数等于1时,不需要使用注解)的操作中。

  • Mapper接口中的方法

    List<Purchase> findByPriceAndCategory(@Param("price") Integer price, @Param("category") Integer category);

  • XML中的代码,此时的参数类型不需要声明,当两个参数类型不一致的时候,无法声明。
    <select id="findByPriceAndCategory" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from purchase
        where price = #{price,jdbcType=INTEGER}
        and category = #{category,jdbcType=INTEGER}
    </select>
  • 测试代码及结果
        System.out.println(mapper.findByPriceAndCategory(3,1));
DEBUG [main] - ==>  Preparing: select id, `name`, price, category from purchase where price = ? and category = ? 
DEBUG [main] - ==> Parameters: 3(Integer), 1(Integer)
DEBUG [main] - <==      Total: 1
[Purchase{id=1, name='可乐', price=3, category=1}]

在方法中有两个参数时,如果不加Param注解,就会抛出以下异常。

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'price' not found. Available parameters are [arg1, arg0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'price' not found. Available parameters are [arg1, arg0, param1, param2]

此时,由于没有指定参数的名称,在Mybatis中默认使用arg0,arg1(从0开始),或param1,param2(从1开始)来进行参数映射。上文中的SQL进行如下改写即可正常运行。

    <select id="findByPriceAndCategory" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from purchase
        where price = #{arg0,jdbcType=INTEGER}
        and category = #{arg1,jdbcType=INTEGER}
    </select><select id="findByPriceAndCategory" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from purchase
        where price = #{param1,jdbcType=INTEGER}
        and category = #{param2,jdbcType=INTEGER}
    </select>

Tips

此外,在Maper接口中的方法,除了查询方法外,其他方法的返回值都是使用的int,因为这些方法执行完成后可以返回修改数据的条数,可以据此来判断操作是否成功。

  • 例如,执行以下测试用例:
    @Test
    public void delete() {
        PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
        //组装参数
        Map<String, Object> param = new HashMap<>();
        param.put("id", 7);
        System.out.println(mapper.deleteMapPojoByID(param));
    }
  • 第一次执行,删除了一条数据,返回值为1
DEBUG [main] - ==>  Preparing: delete from purchase where id = ? 
DEBUG [main] - ==> Parameters: 7(Integer)
DEBUG [main] - <==    Updates: 1
1
  • 第二次执行,数据在第一次操作中已经被删除了,本次执行没有删除数据,因此返回值为0
DEBUG [main] - ==>  Preparing: delete from purchase where id = ? 
DEBUG [main] - ==> Parameters: 7(Integer)
DEBUG [main] - <==    Updates: 0
0

以上便是对在Mybatis中使用XML进行增删改查的实现方式的介绍。