Mybatis

Mybatis概念

  • MyBatis 是一款优秀的==持久层框架==,用于简化 JDBC 开发

  • MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github

  • 官网:https://mybatis.org/mybatis-3/zh/index.html

持久层:

  • 负责将数据到保存到数据库的那一层代码。

    以后开发我们会将操作数据库的Java代码作为持久层。而Mybatis就是对jdbc代码进行了封装。

  • JavaEE三层架构:表现层、业务层、持久层

1. Mybatis快速入门

需求:查询user表中所有的数据

  • 创建user表,添加数据

    create database mybatis;
    use mybatis;
    
    drop table if exists tb_user;
    
    create table tb_user(
    	id int primary key auto_increment,
    	username varchar(20),
    	password varchar(20),
    	gender char(1),
    	addr varchar(30)
    );
    
    INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
    INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
    INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
    

1.1 MyBatis集成步骤

1.创建Maven项目,导入坐标

在创建好的模块中的 pom.xml 配置文件中添加依赖的坐标

	<dependencies>
    <!--mybatis 依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
  <!--mysql 驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.46</version>
    </dependency>
  </dependencies>

2. 添加MyBatis核心配置文件

在模块下的 resources 目录下创建mybatis的配置文件 mybatis-config.xml,内容如下:

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

    <typeAliases>
        <package name="com.example.pojo"/>
    </typeAliases>

    <!--
    environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
    -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--加载sql映射文件-->
<!--        <mapper resource="UserMapper.xml"/>-->
<!--        如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载。也就是将核心配置文件的加载映射配置文件的配置修改为-->
        <package name="com.example.mapper" />
    </mappers>
</configuration>

3. 创建Mapper接口

给IDEA安装MyBatisX插件,方便通过mapper接口方法生成对于的xml节点

public interface UserMapper {
    List<User> selectAll();
    User selectById(int id);
}

4.resources 新建xml文件

在模块的 resources 目录下创建配置的包扫描路径,**例如com.example.mapper,**映射配置文件 UserMapper.xml,内容如下:

这里的sql语句如果希望能够带提示编写,需要在ideadatabase视图下配置mysql链接,此处忽略

<?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">
<!--namespace:与之对应的mapper 接口-->
<mapper namespace="com.example.mapper.UserMapper">
    <select id="selectAll" resultType="com.example.pojo.User">
        select *
        from tb_user;
    </select>
<!--    typeAliases package-->
<!--    resultType:返回类型,如果配置了包路径,可省去前面的包路径-->
    <select id="selectById" resultType="User">
        select *
        from tb_user
        where id = #{id};
    </select>
</mapper>

5. 在pojo包下创建实体类

com.example.pojo 包下创建 User类

public class User {
    private int id;
    private String username;
    private String password;
    private String gender;
    private String addr;
    
    //此处省略了 setter 和 getter
}

6.编写测试类

编写 MybatisDemo 测试类

public static void main(String[] args) throws IOException {
        //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 通过反射或的mapper接口
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //4. 执行sql
        List<User> users = mapper.selectAll();
        System.out.println(users);
        System.out.println("----------------");
        User user = mapper.selectById(1);
        System.out.println(user);
        //5. 释放资源
        sqlSession.close();
    }

2. MyBatis增删改查练习

目标

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

需求

  • 查询
    • 查询所有数据
    • 查询详情
    • 条件查询
  • 添加
  • 修改
    • 修改全部字段
    • 修改动态字段
  • 删除
    • 删除一个
    • 批量删除

2.1 环境准备

2.1.1 SQL语句

-- 删除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);

2.1.2 MyBatis配置

[MyBatis集成过程此处忽略,详见《MyBatis集成步骤》](# 1.1 MyBatis集成步骤)

新建一个Test类,初始化MyBatis链接对象

public class MyBatisTest {
    SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() {
        System.out.println("mybatis初始化");
        String resource = "mybatis-config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 增删改查操作

MyBatis操作数据分需要3个文件

  • SqlSession:操作数据对象
  • interface XXXMapper 对应的Mapper接口操作对象
  • resources目录下与之对应的xml文件,编写SQL语句(注解操作后面提到)
  • 如果设计实体对象,还需要在pojo目录下创建实体类对象

3.1 查询全部数据

/**
     * 查找全部
     */
    @Test
    public void selectAll() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        List<Brand> brands = mapper.selectAll();
        System.out.println(brands);
        sqlSession.close();
    }

  public interface BrandMapper {
      //查询全部
      List<Brand> selectAll();
  }


<!--    resultMap用于映射数据库和Java对象定义不一致的字段-->
    <resultMap id="brandResultMap" type="brand">
        <result column="brand_name" property="brandName" />
        <result column="company_name" property="companyName" />
    </resultMap>

    <select id="selectAll" resultMap="brandResultMap">
        select *
        from tb_brand;
    </select>

**resultMap:**实体类属性名 和 数据库表列名 不一致,不能自动封装数据

3.2 多条件插件(动态SQL)

Mybatis对动态SQL有很强大的支撑:

  • if:用于判断是否传入查询的条件

  • choose (when, otherwise):用于判断选择性条件,多条件和单条件

  • trim (where, set):

  • foreach:用来迭代任何可迭代的对象(如数组,集合)

这些标签定义在xml文件中,用于简单的 if choose foreach...控制语句 :

/**
     * 多条件查询的正确方式
     */
    @Test
    public void selectConditionLabel() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        Brand brand = new Brand();
//        brand.setStatus(1);
        brand.setCompanyName("%米%");
        List<Brand> brands = mapper.selectByConditionLabel(brand);
        System.out.println(brands);
        sqlSession.close();
    }

此处忽略BrandMapper接口方法,详看源码

<!--    动态SQL-->
    <select id="selectByConditionLabel" resultMap="brandResultMap">
        select *
        from tb_brand
        <where>
            <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>
        </where>

    </select>

3.3 单条件查询

省略测试方法,仅看SQL语句

<select id="selectConditionSingle" resultMap="brandResultMap">
        select *
        from tb_brand
        <where>
            <choose>
                <when test="status != null">
                    and status = #{status}
                </when>
                <when test="companyName != null and companyName != '' ">
                    and company_name like #{companyName}
                </when>
                <when test="brandName != null and brandName != ''">
                    and brand_name like #{brandName}
                </when>
            </choose>
        </where>
    </select>

3.4 添加数据

添加数据需要注意2点,

  • 数据添加完成,如果没有手动配置自动提交事务,需要手动commit,凡事操作数据都需要提交事务
  • 主键返回,用于记录插入数据的id,需要在mapper文件添加useGeneratedKeys = true,keyProperty = id
/**
     * 插入一条数据
     */
    @Test
    public void addData() {
        //模拟数据
        int status = 1;
        String companyName = "波导手机";
        String brandName = "波导";
        String description = "手机中的战斗机";
        int ordered = 100;

        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        //封装数据
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        mapper.addData(brand);
        //主键返回,用于记录插入数据的id,需要在mapper文件添加useGeneratedKeys = true,keyProperty = id
        Integer id = brand.getId();
        System.out.println("id:" + id);
        sqlSession.commit();
        sqlSession.close();
    }
<!--    添加一条数据-->
    <!--    * useGeneratedKeys:是够获取自动增长的主键值。true表示获取-->
    <!--    * keyProperty  :指定将获取到的主键值封装到哪儿个属性里-->
    <insert id="addData" useGeneratedKeys="true" keyProperty="id">
        insert into tb_brand(brand_name, company_name, ordered, description, status)
        VALUES (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
    </insert>

3.5 更新数据

此处省略测试方法

<!--    更新数据-->
    <update id="update">
        update tb_brand
        <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.6 批量删除数据

@Test
    public void deleteByIds(){
        int[] ids = {3,6};
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        //删除第5行数据
        mapper.deleteByIds(ids);
        sqlSession.commit();
        sqlSession.close();
    }

foreach 标签

用来迭代任何可迭代的对象(如数组,集合)。

  • collection 属性:
    • mybatis会将数组参数,封装为一个Map集合。
      • 默认:array = 数组
      • 使用@Param注解改变map集合的默认key的名称
  • item 属性:本次迭代获取到的元素。
  • separator 属性:集合项迭代之间的分隔符。foreach 标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。
  • open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
  • close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<delete id="deleteByIds">
        delete from tb_brand where id
        in
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

4.Mybatis参数传递

Mybatis 接口方法中可以接收各种各样的参数,如下:

  • 多个参数
  • 单个参数:单个参数又可以是如下类型
    • POJO 类型
    • Map 集合类型
    • Collection 集合类型
    • List 集合类型
    • Array 类型
    • 其他类型

4.1 多个参数

如下面的代码,就是接收两个参数,而接收多个参数需要使用 @Param 注解,那么为什么要加该注解呢?这个问题要弄明白就必须来研究Mybatis 底层对于这些参数是如何处理的。

User select(@Param("username") String username,@Param("password") String password);
<select id="select" resultType="user">
	select *
    from tb_user
    where 
    	username=#{username}
    	and password=#{password}
</select>

我们在接口方法中定义多个参数,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);

4.2 单个参数

  • 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类型,参数占位符名称 叫什么都可以。尽量做到见名知意

5.MyBatis注解实现增删改查

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

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

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

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

简单演示Select

@Test
    public void selectUser(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        List<User> userTables = mapper.selectUser();
        System.out.println(userTables);
        sqlSession.close();
    }
public interface BrandMapper {
		//使用注解查询SQL
    @Select(value = "select * from tb_user;")
    List<User> selectUser();
  }

注意:

  • 注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要再映射配置文件中书写对应的 statement
  • 注解完成简单功能,配置文件完成复杂功能。
Loading...