本文速览

  • 使用映射配置文件实现CRUD操作
  • 使用注解实现CRUD操作

环境准备

  1. 创建对应数据库
USE mybatis
-- 删除tb_brand表
DROP TABLE IF EXISTS tb_brand;
-- 创建tb_brand表
CREATE TABLE tb_brand
(
    -- id 主键
    id           INT PRIMARY KEY AUTO_INCREMENT,
    -- 品牌名称
    brand_name   VARCHAR(20),
    -- 企业名称
    company_name VARCHAR(20),
    -- 排序字段
    ordered      INT,
    -- 描述信息
    description  VARCHAR(100),
    -- 状态:0:禁用  1:启用
    STATUS       INT
);
-- 添加数据
INSERT INTO tb_brand (brand_name, company_name, ordered, description, STATUS)
VALUES ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
       ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
       ('小米', '小米科技有限公司', 50, 'are you ok', 1);
  1. com.xzxhappy.pojo 下创建实体类 Brand
public class Brand {
    // id 主键
    private Integer id;
    // 品牌名称
    private String brandName;
    // 企业名称
    private String companyName;
    // 排序字段
    private Integer ordered;
    // 描述信息
    private String description;
    // 状态:0:禁用  1:启用
    private Integer status;
    
    //省略 setter and getter 和 toString方法。自己写时要补全这部分代码
}
  1. test/java 目录下面建 com.xzxhappy.test.MyBatisTestDemo测试类
package com.xzxhappy.test;

import com.xzxhappy.mapper.BrandMapper;
import com.xzxhappy.pojo.Brand;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest {
    @Test
    public void testSelectAll() throws Exception {
        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法 (其他不用动,如果要测试其他增删改方法就改下面两行代码就行)
        List<Brand> brands = brandMapper.selectAll();
        System.out.println(brands);
        
        // 5. 关闭资源
        sqlSession.close();
    }
}

查询所有

1. 编写 Mapper 接口的对应方法

com.xzxhappy.mapper 包写创建名为 BrandMapper接口。并在该接口中定义 List<Brand> selectAll() 方法。(显然查询所有不需要参数)

public interface BrandMapper {
    /**
     * 查询所有
     */
    List<Brand> selectAll();
}

2. 编写xml文件中的对应sql语句

resources 下创建 com/xzxhappy/mapper 目录结构(注意resources目录下面不能建包,只能目录,所以建立的语法得用目录的 / ),并在该目录下创建名为 BrandMapper.xml 的映射配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xzxhappy.mapper.BrandMapper">
    <select id="selectAll" resultType="brand">
        select *
        from tb_brand;
    </select>
</mapper>

<!-- 由于在实体类中属性名是 `brandName` 和 `companyName` ,
而表中的字段名为 `brand_name` 和 `company_name`,
所以会导致这两个字段名的内容查出来是null的情况。
那么如何解决呢
下面有三种方法 -->

<!--
    解决方法把上述<mapper>标签中的代码改成下面这些即可
    * 1. 起别名
    * 2. sql片段
    * 3. 使用resultMap标签
-->

<!-- 方法一,起别名 -->
    <select id="selectAll" resultType="Brand">
        select id, brand_name brandName, company_name companyName, ordered, description, status
        from tb_brand;
    </select>

<!-- 方法二,sql片段 -->    
    <sql id="brand_column">
        id, brand_name brandName, company_name companyName, ordered, description, status
    </sql>

    <select id="selectAll" resultType="Brand">
        select <include refid="brand_column"/>
        from tb_brand;
    </select>

<!-- 方法三,使用resultMap标签,这种方法也是最常用的,使用这种方法那么原本 select 标签中的 resultType 属性要替换成 resultMap 属性--> 
    <resultMap id="brandResultMap" type="brand">
        <!--
                id:完成主键字段的映射
                    column:表的列名
                    property:实体类的属性名
                result:完成一般字段的映射
                    column:表的列名
                    property:实体类的属性名
            -->
        <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>
    </resultMap>

    <select id="selectAll" resultMap="brandResultMap">
        select id, brand_name , company_name , ordered, description, status
        from tb_brand;
        
    </select>

3. 编写测试代码

@Test
public void testSelectAll() throws IOException {
    //1. 获取SqlSessionFactory
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    //2. 获取SqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //3. 获取Mapper接口的代理对象
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

    //4. 执行方法
    List<Brand> brands = brandMapper.selectAll();
    System.out.println(brands);

    //5. 释放资源
    sqlSession.close();

}

根据id查询

1. 编写 Mapper 接口的对应方法

  • BrandMapper 接口中定义根据id查询数据的方法
/**
  * 查看详情:根据Id查询
  */
Brand selectById(int id);

2. 编写xml文件中的对应sql语句

<!-- 
    mybatis提供了两种参数占位符:

    * #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。从上述例子可以看出使用#{} 底层使用的是 `PreparedStatement`

    * ${} :拼接SQL。底层使用的是 `Statement`,会存在SQL注入问题。

-->
<select id="selectById"  resultMap="brandResultMap">
    select *
    from tb_brand where id = #{id};
</select>

3. 编写测试代码

@Test
    public void testSelectById() throws Exception {
        // 假装是传过来的参数
        int id = 1;

        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法
        Brand brand = brandMapper.selectById(id);
        System.out.println(brand);

        // 5. 关闭资源
        sqlSession.close();

    }

4. sql语句中特殊字符处理

SQL语句中写一下特殊字符,比如 <

  1. 使用xml的转义字符
    例如:
原句:
<select id="selectById" resultMap="brandResultMap">
        select *
        from tb_brand
        where id < #{id};
</select>

转义:
<select id="selectById" resultMap="brandResultMap">
        select *
        from tb_brand
        where id &lt; #{id};
    </select>
  1. 使用CDATA区(该区域内均被解析为正常文本)
    例如:
原句:
<select id="selectById" resultMap="brandResultMap">
        select *
        from tb_brand
        where id < #{id};
</select>

转义:
<select id="selectById" resultMap="brandResultMap">
        select *
        from tb_brand
        where id
         <![CDATA[
            <
          ]]>
         #{id};
</select>

条件查询

多条件查询

  1. 使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位

  2. 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致。

  3. 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。

  4. 代码示例:

1. 编写 Mapper 接口的对应方法

  • BrandMapper.java
/**
     * 条件查询
     */
    /*<1> 散装参数*/
    // List<Brand> selectByCondition(@Param("status") int status,@Param("companyName") String companyName,@Param("brandName") String brandName);
    /*<2> brand对象*/
    // List<Brand> selectByCondition(Brand brand);
    /*<3> map对象*/
     List<Brand> selectByCondition(Map map);

2. 编写xml文件中的对应sql语句

  • BrandMapper.xml(此文件在三种条件下不变)
<select id="selectByCondition" resultMap="brandResultMap">
        select *
        from tb_brand
        where status = #{status}
        and company_name like #{companyName}
        and brand_name like #{brandName}
    </select>

3. 编写测试代码

  • MyBatisTest.java
/*
    条件查询
     */
    @Test
    public void testSelectByCondition() throws Exception {
        // 假装是传过来的参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

        // 处理参数
        companyName = "%" + companyName + "%";
        brandName = "%" + brandName + "%";

        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法

        // <1>散装参数
        // List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);

        // <2> brand对象
        // <2>封装brand对象
        // Brand brand = new Brand();
        // brand.setStatus(status);
        // brand.setCompanyName(companyName);
        // brand.setBrandName(brandName);
        // List<Brand> brands = brandMapper.selectByCondition(brand);
        
        // <3> map对象
        // <3>封装map对象
        Map map = new HashMap();
        map.put("status",status);
        map.put("companyName",companyName);
        map.put("brandName",brandName);
         List<Brand> brands = brandMapper.selectByCondition(map);

        System.out.println(brands);

        // 5. 关闭资源
        sqlSession.close();

    }

动态SQL多条件查询

1. 编写 Mapper 接口的对应方法

/**
     * 动态sql
     */
    List<Brand> selectByCondition(Map map);

2. 编写xml文件中的对应sql语句

<!-- 情况一,但是这种情况下,如果条件一不存在那么sql语句会报错,因为where后面直接跟了and... -->
<select id="selectByCondition" resultMap="brandResultMap">
    select *
    from tb_brand
    where
    <!-- 
        if 标签:条件判断
            test 属性:逻辑表达式 
        -->
    <if test="status != null">
        status = #{status}
    </if>
    <if test="companyName != null and companyName != '' ">
        and company_name like #{companyName}
    </if>
    <if test="brandName != null and brandName != '' ">
        and brand_name like #{brandName}
    </if>
</select>

<!-- 情况二,在where后面拼接一个恒等式,思想值得学习,但不是最常用的-->
<select id="selectByCondition" resultMap="brandResultMap">
        select *
        from tb_brand
        where 1 = 1
            <if test="status != null">
                and   status = #{status}
            </if>
            <if test="companyName != null and companyName != '' ">
                and company_name like #{companyName}
            </if>
            <if test="brandName != null and brandName != '' ">
                and brand_name like #{brandName}
            </if>
    </select>

<!-- 情况三,使用where标签,如果条件一不存在那么where标签会自动检测,把where后面跟的and删去,where是一个神奇的标签-->
<!-- 
    where 标签作用:
        1. 替换where关键字
        2. 会动态的去掉第一个条件前的 and 
        3. 如果所有的参数没有值则不加where关键字
-->
<select id="selectByCondition" resultMap="brandResultMap">
    select *
    from tb_brand
    <where>
        <if test="status != null">
            status = #{status}
        </if>
        <if test="companyName != null and companyName != '' ">
            and company_name like #{companyName}
        </if>
        <if test="brandName != null and brandName != '' ">
            and brand_name like #{brandName}
        </if>
    </where>
</select>

3. 编写测试代码

// 模仿用户,故意少了第一个参数
Map map = new HashMap();
//        map.put("status" , status);
        map.put("companyName", companyName);
        map.put("brandName" , brandName);
        List<Brand> brands = brandMapper.selectByCondition(map);
        System.out.println(brands);

动态SQL单条件查询

1. 编写 Mapper 接口的对应方法

/**
     * 单条件动态sql
     * @param brand
     * @return
     */
    List<Brand> selectByConditionSingle(Brand brand);

2. 编写xml文件中的对应sql语句

<select id="selectByConditionSingle" resultMap="brandResultMap">
    select *
    from tb_brand
    <!-- 考虑到这个条件3选1,若用户都不选,用where标签,到时候sql语句里直接没where -->
    <where>
        <!-- choose相当于switch -->
        <choose>
            <!-- when相当于case -->
            <when test="status != null">
                status = #{status}
            </when>
            <when test="companyName != null and companyName != '' ">
                company_name like #{companyName}
            </when>
            <when test="brandName != null and brandName != '' ">
                brand_name like #{brandName}
            </when>
            <!-- otherwise相当于default -->
            <otherwise>
                1 = 1
            </otherwise>
        </choose>
    </where>
</select>

3. 编写测试代码

// 4. 执行对应方法
        // <2> brand对象
        // <2>封装brand对象
         Brand brand = new Brand();
         //brand.setStatus(status);
         //brand.setCompanyName(companyName);
         //brand.setBrandName(brandName);
         List<Brand> brands = brandMapper.selectByConditionSingle(brand);

添加数据

正常添加

1. 编写 Mapper 接口的对应方法

 /**
     * 添加数据
     */
    void add(Brand brand);

2. 编写xml文件中的对应sql语句

<insert id="add">
        insert into  tb_brand (brand_name, company_name, ordered, description, status)
        values (#{brandName},#{companyName},#{ordered},#{description},#{status})
    </insert>

3. 编写测试代码

/**
     * 添加数据
     */
    @Test
    public void testAdd() throws Exception {
        // 假装是传过来的参数
        int status = 1;
        String companyName = "波导";
        String brandName = "波导";
        int ordered = 0;
        String description = "波导手机6666";

        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        // SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setOrdered(ordered);
        brand.setDescription(description);

        brandMapper.add(brand);

        // 默认开启事务,即需要手动提交事务
        //sqlSession.commit();

        // 5. 关闭资源
        sqlSession.close();
    }

添加后返回主键

在数据添加成功后,有时候需要获取插入数据库数据的主键,只需要把BrandMapper.xml中的<insert>标签添加两个属性就行

  • 在 insert 标签上添加如下属性:
    • useGeneratedKeys:是够获取自动增长的主键值。true表示获取
    • keyProperty :指定将获取到的主键值封装到哪个属性里
<insert id="add" useGeneratedKeys="true" keyProperty="id">
    insert into tb_brand (brand_name, company_name, ordered, description, status)
    values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>

MyBatisTest.java 里面就能直接获取到id

	brandMapper.add(brand);
        Integer id = brand.getId();
        System.out.println(id);

修改数据

动态修改

1. 编写 Mapper 接口的对应方法

/**
     * 修改数据
     */
    // 返回值可以是void,也可以是int,如果是int则返回受影响的行数
    int update(Brand brand); 

2. 编写xml文件中的对应sql语句

<!-- 传入了什么参数就修改什么参数,其他的保持不变 -->
<update id="update" >
        update tb_brand
        <!-- 类似于where标签,set标签可以智能的识别sql语句,让其符合语法规范 -->
        <set>
            <if test="brandName != null and brandName != ''">
                brand_name = #{brandName},
            </if>
            <if test="companyName != null and companyName != ''">
                company_name = #{companyName},
            </if>
            <if test="ordered != null ">
                ordered = #{ordered},
            </if>
            <if test="description != null and description != ''">
                description = #{description},
            </if>
            <if test="status != null ">
                status = #{status}
            </if>
        </set>

        where id = #{id};
    </update>

3. 编写测试代码

/**
     * 修改数据
     */
    @Test
    public void testUpdate() throws Exception {
        // 假装是传过来的参数
        int status = 1;
        String companyName = "波导";
        String brandName = "波导";
        int ordered = 0;
        String description = "波导手机666655555";
        int id = 5;

        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        // SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法
        Brand brand = new Brand();
        //brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setOrdered(ordered);
        brand.setDescription(description);
        brand.setId(id);

        int count = brandMapper.update(brand);
        System.out.println(count);


        // 默认开启事务,即需要手动提交事务
        //sqlSession.commit();

        // 5. 关闭资源
        sqlSession.close();

    }

删除数据

删除一个

1. 编写 Mapper 接口的对应方法

/**
     * 删除一个
     */
    void deleteById(int id);

2. 编写xml文件中的对应sql语句

<delete id="deleteById">
        delete from tb_brand
        where id = #{id};
    </delete>

3. 编写测试代码

/**
     * 删除一行数据
     */
    @Test
    public void testDeleteById() throws Exception {
        // 假装是传过来的参数
        int id = 6;

        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        // SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法

        brandMapper.deleteById(id);


        // 默认开启事务,即需要手动提交事务
        //sqlSession.commit();

        // 5. 关闭资源
        sqlSession.close();

    }

批量删除

1. 编写 Mapper 接口的对应方法

/**
     * 批量删除
     * 使用注解那么这个参数会替换xml文件中foreach标签里面的“ids”
     */
    void deleteByIds(@Param("ids") int [] ids);

2. 编写xml文件中的对应sql语句

<delete id="deleteByIds">
        delete from tb_brand
        where id in
        <!-- 
        collection:要遍历的集合
        item:遍历出来的项的名称
        separator:项之间用什么分隔开
        open:给foreach标签加上开始符号,close同理 
        -->
          <foreach collection="ids" item="id" separator="," open="(" close=")">
              #{id}
          </foreach>
          ;
    </delete>

3. 编写测试代码

/**
     * 批量删除
     */
    @Test
    public void testDeleteByIds() throws Exception {
        // 假装是传过来的参数
        int [] ids = {5,7};

        // 1. 获取sqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取sqlSession对象
        // SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 3. 获取接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        // 4. 执行对应方法

        brandMapper.deleteByIds(ids);


        // 默认开启事务,即需要手动提交事务
        //sqlSession.commit();

        // 5. 关闭资源
        sqlSession.close();

    }

参数传递问题

BrandMapper.java 文件的方法中传递参数,我们建议使用注解传参。
例如:

User select(@Param("username") String username,@Param("password") String password);

我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:

  • 以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:

    • map.put("arg0",参数值1);
    • map.put("arg1",参数值2);
  • 以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:

    • map.put("param1",参数值1);
    • map.put("param2",参数值2);

举例:

  • 接口方法
User select(@Param("username") String username, String password);
  • xml配置文件
<select id="select" resultType="user">
	select *
    from tb_user
    where 
    	username=#{username}
    	and password=#{param2}
</select>

总结

  • POJO 类型
    直接使用。要求 属性名参数占位符名称 一致

  • Map 集合类型
    直接使用。要求 map集合的键名参数占位符名称 一致

  • Collection 集合类型
    Mybatis 会将集合封装到 map 集合中,如下:

    map.put(“arg0”,collection集合);
    map.put(“collection”,collection集合);
    可以使用 @Param 注解替换map集合中默认的 arg 键名。

  • List 集合类型
    Mybatis 会将集合封装到 map 集合中,如下:

    map.put(“arg0”,list集合);
    map.put(“collection”,list集合);
    map.put(“list”,list集合);
    可以使用 @Param 注解替换map集合中默认的 arg 键名。

  • Array 类型
    Mybatis 会将集合封装到 map 集合中,如下:

    map.put(“arg0”,数组);
    map.put(“array”,数组);
    可以使用 @Param 注解替换map集合中默认的 arg 键名。

  • 其他类型
    比如int类型,参数占位符名称 叫什么都可以。尽量做到见名知意

注解实现CRUD

使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发

@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);

注:注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要在xml配置文件中书写对应的sql代码了。

Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:

  • 查询 :@Select
  • 添加 :@Insert
  • 修改 :@Update
  • 删除 :@Delete

请注意:注解方式完成简单功能,xml配置文件完成复杂功能。

Q.E.D.


在读程序猿+指弹发烧友+力量举、街头健身爱好者。