【MyBatis 基础】

【MyBatis 基础】

一、MyBatis 简介及快速入门

  • MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发
  • MyBatis 本是 Apache 的一个开源项目 iBatis ,2010年这个项目由 Apache Software Foundation 迁移到了 Google Code,并且改名为 MyBatis ,2013年11月迁移到 GitHub
  • 官网:MyBatis 官网网址
  • 持久层:

    • 负责将数据保存到数据库的那一层代码
    • JavaEE 三层架构:表现层、业务层、持久层
  • 框架:

    • 框架就是一个半成品的软件,是一套可重用的、通用的、软件基础代码模型
    • 在框架的基础之上构建软件编写更加高效、规范、通用、可扩展

1. MyBatis 简化 JDBC

JDBC 缺点:

  1. 硬编码
    • 注册驱动,获取连接
    • SQL 语句
  2. 操作繁琐
    • 手动设置参数
    • 手动封装结果集

MyBatis 简化:

  1. 将硬编码写入配置文件
  2. 将操作繁琐的步骤自动完成

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作

2. MyBatis 的快速入门

(1)查询 user 表中所有数据
  1. 创建 user 表,添加数据
  2. 创建模块,导入坐标
  3. 编写 MyBatis 核心配置文件 → 替换连接信息,解决硬编码问题
  4. 编写 SQL 映射文件 → 统一管理 SQL 语句,解决硬编码问题
  5. 编码
    1. 定义 POJO 类
    2. 加载核心配置文件,获取 SqlSessionFactory 对象
    3. 获取 SqlSession 对象,执行 SQL 语句
    4. 释放资源
① 创建 user 表,添加数据
drop table if exists user;
create table user(
    id int primary key auto_increment,
    username varchar(20),
    password varchar(20),
    gender char(1),
    addr varchar(30)
);
insert into user values (1,'张三','123','男','北京');
insert into user values (2,'李四','234','女','天津');
insert into user values (3,'王五','345','男','西安');
② 创建模块,导入坐标
    <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>
        <!-- 添加 junit 依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <!-- 添加 logback-classic 依赖 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- 添加 logback-core 依赖 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>

并且需要一个配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--
        CONSOLE :表示当前的日志信息是可以输出到控制台的。
    -->
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg %n</pattern>
        </encoder>
    </appender>

    <logger name="com.xxxxxxx" level="DEBUG" additivity="false">
        <appender-ref ref="Console"/>
    </logger>


    <!--

      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
     , 默认debug
      <root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
      -->
    <root level="DEBUG">
        <appender-ref ref="Console"/>
    </root>
</configuration>
③ 编写 MyBatis 核心配置文件

MyBatis 官网中给出了入门案例,可根据示例进行操作

Ⅰ、 官网操作

以下出自官网

  1. 创建文件并将配置文件命名为:xxx.xml 文件即可(例如:mybatis-config.xml
  2. XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>
Ⅱ、 根据需求更改

个人对配置文件进行更改:

<?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>
    <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="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>
④ 编写 SQL 映射文件
Ⅰ、 官网操作
  1. 创建 SQL 映射文件:
<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>
  1. 映射文件的命名一般遵循驼峰命名并且:操作哪个表并且加上 Mapper 和后缀即可(例如:UserMapper.xml
Ⅱ、 根据需求更改
  1. 首先需要在以下示例的地方创建 User 实体类

创建 User 类

  1. 对 SQL 映射文件进行进一步修改
<?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 namespace="test">
    <select id="selectAll" resultType="com.xxxxxxx.pojo.User">
    /* 这里的 id 是这条语句唯一标识,resultType 是操作对应的 User 类型*/
        select * from user ;
    </select>
</mapper>
⑤ 编码
  1. 创建的 User 类
public class User {
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String addr;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}
  1. 在 com.xxxxxxx 下创建测试类
Ⅰ、 官网操作
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
Ⅱ、 根据需求更改
        // 1. 加载 mybatis 的核心配置文件,获取 SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

测试类:

public class 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. 执行 SQL 语句
        List<User> users = sqlSession.selectList("test.selectAll");
        System.out.println(users);
        // 4. 释放资源
        sqlSession.close();
    }
}

3. 解决 SQL 映射文件的警告提示

  • 产生原因: IDEA 和数据库没有建立连接,不识别表信息
  • 解决方式:在 IDEA 中配置 MySQL 数据库连接

配置
配置

二、 Mapper 代理开发

  • 解决原生方式中的硬编码
  • 简化后执行 SQL 语句
    硬编码
    Mapper 代理接口

1. 使用 Mapper 代理方式完成入门案例

步骤:需要满足前三条规则,再进行编码开发

  1. 定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下
  2. 设置 SQL 映射文件的 username 属性为 Mapper 接口的全限定名
  3. 在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中的 SQL 语句的 id,并保持参数类型和返回值类型一致
  4. 编码
    • 通过 SqlSession 的 getMapper 方法获取 Mapper 接口的代理对象
    • 调用对应方法完成 SQL 语句的执行

细节:如果 Mapper 接口名称和 SQL 映射文件名称相同,并且在同一目录下,则可以使用包扫描的方式简化 SQL 映射文件的加载

(1) 将 Mapper 接口和 SQL 映射文件放置在同一目录下

将 Mapper 接口和 SQL 映射文件放置在同一目录下
为了实现这种情况:在 resources 创建 directory(目录)时,写成呢个:com/xxxxxxx/mapper 的形式,只有通过这种写法,才能实现编译之后的文件, Mapper 接口和 SQL 映射文件放置在同一目录下
编译之后
注意:SQL 映射文件(UserMapper.xml 文件)被拖动移动进入新的地方后,需要更改 Mybatis 核心配置文件(mybatis-config.xml)的路径
更改路径

(2) 设置 SQL 映射文件的 username 属性为 Mapper 接口的全限定名

设置 SQL 映射文件的 username 属性为 Mapper 接口的全限定名

(3) 定义接口方法

方法名就是 SQL 映射文件中的 SQL 语句的 id,并保持参数类型和返回值类型一致

public interface UserMapper {
    List<User> selectAll();
}
(4) 编码
    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. 执行 SQL 语句
        // 获取 UserMapper 接口的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.selectAll();
        System.out.println(users);
        // 4. 释放资源
        sqlSession.close();
    }
(5) 优化 SQL 配置文件

优化

使用这种包扫描的方式,可以简化代码量。

三、Mybatis 的核心配置文件

1. Mybatis 核心配置文件的顶层结构

顶层结构
更多细节,可以通过官网查看MyBatis 官网网址
细节:配置各个标签时,需要遵守前后顺序,否则会有报错提示

2. 类型别名(typeAliases)

使用类型别名:
在 Mybatis 核心配置文件中:

<?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.xxxxxxx.pojo"/>
    </typeAliases>
    <!-- 这种操作相当于为此包下的实体类起了一个别名,在 SQL 映射文件中的返回类型可以不用区分大小写  -->
    <!--
        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:///test?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 加载 sql 的映射文件 -->
        <!-- 不使用 Mapper 代理的开发方式 -->
        <!-- <mapper resource="com/xxxxxxx/mapper/UserMapper.xml"/> -->
        <!-- Mapper 代理的开发方式 -->
        <!-- 前提是符合以下三条规则: -->
        <!-- 1. 定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下-->
        <!-- 2. 设置 SQL 映射文件的 username 属性为 Mapper 接口的全限定名-->
        <!-- 3. 在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中的 SQL 语句的 id,并保持参数类型和返回值类型一致-->
        <!-- 使用包扫描的方式简化 SQL 映射文件的加载 -->
        <package name="com.xxxxxxx.mapper"/>
    </mappers>
</configuration>

在 SQL 映射文件中:

<?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 namespace="com.itheima.mapper.UserMapper">
    <select id="selectAll" resultType="user">
    /* 使用类型别名,这里的返回值类型就可以不用区分大小写且直接简化写法 */
    /* 这里的 id 是这条语句唯一标识,resultType 是操作对应的 User 类型 */
        select * from user;
    </select>
</mapper>

四、 配置文件完成增删改查

1. 完成品牌数据的增删改查操作

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

2. 准备环境

  • 数据库表 brand
  • 实体类 Brand
  • 测试用例
  • 安装 MybatisX 插件
(1) 数据库表
create table brand(
    id int primary key auto_increment,
    brand_name varchar(20),
    company_name varchar(20),
    ordered int,
    description varchar(100),
    status int
);
insert into brand (brand_name, company_name, ordered, description, status)
VALUES ('三只松鼠','三只松鼠股份有限公司',5,'好吃',0),
        ('华为','华为技术有限公司',100,'华为致力于',1),
        ('小米','小米科技有限公司',50,'小米雷布斯',1);
(2) 实体类
public class Brand {
    // 在实体类中,基本数据类型建议使用对应的包装类
    private Integer id;
    private String brandName;
    private String companyName;
    private Integer ordered;
    private String description;
    private Integer status;

    @Override
    public String toString() {
        return "Brand{" +
                "id=" + id +
                ", brandName='" + brandName + '\'' +
                ", companyName='" + companyName + '\'' +
                ", ordered=" + ordered +
                ", description='" + description + '\'' +
                ", status=" + status +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBrandName() {
        return brandName;
    }

    public void setBrandName(String brandName) {
        this.brandName = brandName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public Integer getOrdered() {
        return ordered;
    }

    public void setOrdered(Integer ordered) {
        this.ordered = ordered;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }
}
(3) 测试用例

创建测试用例:
测试用例

(4) 安装 MybatisX 插件
  • MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生
  • 主要功能
    • XML 和接口方法相互转换
    • 根据接口生成 statement(UserMapper 中的 SQL 语句)

3. 操作

(1) 查询
① 查询所有数据
  1. 编写接口方法:Mapper 接口
    • 参数:无
    • 结果:List<Brand>
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法,测试
public interface UserMapper {
    List<User> selectAll();
}
<?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 namespace="com.xxxxxxx.mapper.BrandMapper">

    <select id="selectAll" resultType="brand">
        select * from brand;
    </select>
</mapper>
    @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();
    }

总结操作:编写接口方法 → 编写 SQL → 执行方法

② 运行结果有瑕疵

数据库的字段名称和实体类的属性名称不一样,则不能自动封装数据

解决

  • 第一种方式:起别名:对不一样的列名起别名,让别名和实体类的属性名一样
    • 缺点:每次查询都要定义一次别名
      • 解决:提取出来,起别名 SQL 片段
        • 缺点:不够灵活
    <select id="selectAll" resultType="brand">
        select id, brand_name as brandName, company_name as companyName, ordered, description, status from brand;
    </select>
    <sql id="brand_colum">
        id, brand_name as brandName, company_name as companyName, ordered, description, status
    </sql>
    <select id="selectAll" resultType="brand">
        select <include refid="brand_colum"/> from brand;
    </select>
  • 第二种方式:resultMap
    1. 定义 <resultMap> 标签
    2. <select> 标签中,使用 resultMap 属性替换 resultType 属性
    <!--
        id:唯一标识
        type:映射的类型,支持别名
    -->
    <resultMap id="brandResultMap" type="brand">
        <!--
            id:完成主键字段的映射
                column:表的列名
                property:实体类的属性名
            result:完成一般字段的映射
        -->
        <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>
    </resultMap>
    <select id="selectAll" resultMap="brandResultMap">
        select * from brand;
    </select>
③ 查看详情
  1. 编写接口方法:Mapper 接口
    • 参数:id
    • 结果:Brand
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法:测试
Brand selectById(int id);
    <select id="selectById" resultMap="brandResultMap">
        select * from brand where id = #{id};
    </select>
    @Test
    public void testSelectById() throws IOException {
        // 接收参数
        int id=1;
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        Brand brand = brandMapper.selectById(id);
        System.out.println(brand);
        sqlSession.close();
    }

解析:在 SQL 映射文件中的 #{id} (或 ${id} )写法

  • 第一个问题:
    参数占位符:

    1. #{id} :会将其替换为 ? ,为了防止 SQL 注入
    2. ${id} :拼接 SQL 语句,会存在 SQL 注入问题
    3. 使用时机:
      • 参数传递的时候:#{id}
      • 表名或者列名不固定的情况下:${id},会存在 SQL 注入问题
  • 第二个问题:
    参数类型:

    • parameterType :可以省略
  • 第三个问题:
    特殊转义字符:字符较少的情况下

    1. 转义字符:
      select * from brand where id &lt; #{id};
    2. CDATA 区:
      select * from brand where id <![CDATA[ < ]]> #{id};
④ 多条件查询
  1. 编写接口方法:Mapper 接口
    • 参数:所有查询条件
    • 结果:List<Brand>
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法,测试

条件查询三种方式

  • 参数接收
    1. 散装参数:如果方法中有多个参数,需要使用 @Param("SQL 参数占位符名称") 注解标注,该变量给到哪个占位符
    2. 对象参数:对象的属性名称要和参数的占位符名称一致
    3. Map 集合参数
	// 第一种
    List<Brand> selectByCondition(@Param("status") int status,@Param("companyName") String companyName,@Param("brandName") String brandName);
    // 第二种
    List<Brand> selectByCondition(Brand brand);
    // 第三种
    List<Brand> selectByCondition(Map map);
  • 散装参数方式:
    <select id="selectByCondition" resultMap="brandResultMap">
        select *
        from brand
        where status=#{status}
        and company_name like #{companyName}
        and brand_name like #{brandName};
    </select>
    @Test
    public void testSelectByCondition() throws IOException {
        // 接收参数
        int status=1;
        String companyName="华为";
        String brandName="华为";
        // 处理参数
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
        System.out.println(brands);
        sqlSession.close();
    }
  • 对象参数方式:

SQL 映射文件不需要加以修改

    @Test
    public void testSelectByCondition() throws IOException {
        // 接收参数
        int status=1;
        String companyName="华为";
        String brandName="华为";
        // 处理参数
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";
        // 封装对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        List<Brand> brands = brandMapper.selectByCondition(brand);
        System.out.println(brands);
        sqlSession.close();
    }
  • Map 集合参数方式:

SQL 映射文件不需要加以更改

    @Test
    public void testSelectByCondition() throws IOException {
        // 接收参数
        int status=1;
        String companyName="华为";
        String brandName="华为";
        // 处理参数
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";
        // 封装对象
        HashMap map = new HashMap();
        map.put("status",status);
        map.put("companyName",companyName);
        map.put("brandName",brandName);
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        List<Brand> brands = brandMapper.selectByCondition(map);
        System.out.println(brands);
        sqlSession.close();
    }
  • 总结
    SQL 语句设置参数有三种方式:
  1. 散装参数:需要使用 @Param("SQL 参数占位符名称")
  2. 实体类封装参数:只要保证 SQL 中的参数名和实体类属性名对应上,即可设置成功
  3. Map 集合:只需要保证 SQL 中的参数名和 Map 集合的键的名称对应上,即可设置成功

但是以上多条查询的方式存在漏洞,假如用户只输入两种条件限制或者更少那么就无法输出,下面进行优化

⑤ 多条件的动态条件查询
  • SQL 语句会随着用户的输入或外部条件的变化而变化,我们称之为动态 SQL
  • Mybatis 对动态 SQL 有很强大的支撑
    • if
    • choose(when,otherwise)
    • trim(where,set)
    • foreach

依据以上的多条件查询,只有 SQL 映射文件需要加以更改

  • 使用 if 的方式:
    <select id="selectByCondition" resultMap="brandResultMap">
        select *
        from 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>
    </select>

依据这样用户就可以输入少量(但是必须有 status 这个条件,否则 status 为空的话,SQL 语句就不正确,不符合语法)的查询条件达到目的,所以还是存在局限条件

  • if :条件判断
    • test :逻辑表达式
  • 存在 SQL 语句问题:
    • 解决一:使用恒等式过渡:1=1 ,这样 SQL 语法就符合要求了
    • 解决二:使用 <where> 替换 where 关键字
    <select id="selectByCondition" resultMap="brandResultMap">
        select *
        from 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>
    <select id="selectByCondition" resultMap="brandResultMap">
        select *
        from 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>
⑥ 单条件的动态条件查询
  • 从多个条件中选择一个
    • choose(when,otherwise) :选择,类似于 Java 中的 switch 语句
    List<Brand> selectByConditionSingle(Brand brand);
    <select id="selectByConditionSingle" resultMap="brandResultMap">
        select *
        from brand
        where
        <choose>
            <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>
                1=1
            </otherwise>
        </choose>
    </select>

或者:

    <select id="selectByConditionSingle" resultMap="brandResultMap">
        select *
        from brand
        <where>
            <choose>
                <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>
            </choose>
        </where>
    </select>
    @Test
    public void testSelectByConditionSingle() throws IOException {
        // 接收参数(这其中可能只存在一个)
        int status=1;
        String companyName="华为";
        String brandName="华为";
        // 处理参数
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";
        // 封装对象
        Brand brand = new Brand();
        // 这三者选其一就可以执行(如果一个都不选,就显示全部)
        brand.setStatus(status);
        // brand.setCompanyName(companyName);
        // brand.setBrandName(brandName);
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        List<Brand> brands = brandMapper.selectByConditionSingle(brand);
        System.out.println(brands);
        sqlSession.close();
    }
(2) 添加
① 简单添加
  1. 编写接口方法:Mapper 接口
    • 除了 id 之外的所有数据
    • 结果:void
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法,测试
    • Mybatis 事务:
    • openSession() :默认开启事务,进行增删改操作后需要使用 sqlSession.commit(); 手动提交事务
    • openSession(true) :可以设置为自动提交事务(关闭事务)
    void add(Brand brand);
    <insert id="add">
        insert into brand (brand_name, company_name, ordered, description, status)
        values (#{brandName},#{companyName},#{ordered},#{description},#{status});
    </insert>
    @Test
    public void testAdd() throws IOException {
        // 接收参数
        int status=1;
        String companyName="诺基亚手机";
        String brandName="诺基亚";
        String description="砸核桃";
        int ordered=100;
        // 封装对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession sqlSession = sqlSessionFactory.openSession(true); // 自动提交事务
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        brandMapper.add(brand);
        // 提交事务
        sqlSession.commit();
        sqlSession.close();
    }
② 主键返回添加

在数据添加成功后,需要获取插入数据库数据的主键
例如:添加订单和订单项

  1. 添加订单
  2. 添加订单项,订单项中需要设置所属订单的 id

这样需要在 SQL 映射文件中添加两个条件:<insert id="add" useGeneratedKeys="true" keyProperty="id">

    <insert id="add" useGeneratedKeys="true" keyProperty="id">
        insert into brand (brand_name, company_name, ordered, description, status)
        values (#{brandName},#{companyName},#{ordered},#{description},#{status});
    </insert>
    @Test
    public void testAdd() throws IOException {
        // 接收参数
        int status=1;
        String companyName="摩托罗拉手机";
        String brandName="摩托罗拉";
        String description="砸核桃";
        int ordered=100;
        // 封装对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession sqlSession = sqlSessionFactory.openSession(true); // 自动提交事务
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        brandMapper.add(brand);
        Integer id = brand.getId();
        System.out.println(id);
        // 提交事务
        sqlSession.commit();
        sqlSession.close();
    }
(3) 修改
① 修改全部字段
  1. 编写接口方法:Mapper 接口
    • 参数:所有数据
    • 结果:void
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法,测试
    int update(Brand brand);
    <update id="update">
        update brand
        set
            brand_name = #{brandName},
            company_name = #{companyName},
            ordered = #{ordered},
            description = #{description},
            status = #{status}
        where id=#{id};
    </update>
    @Test
    public void testUpdate() throws IOException {
        // 接收参数
        int status=1;
        String companyName="摩托罗拉手机";
        String brandName="摩托罗拉";
        String description="摩托罗拉就是好用";
        int ordered=200;
        int id=6;
        // 封装对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        brand.setId(id);
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession sqlSession = sqlSessionFactory.openSession(true); // 自动提交事务
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        int count = brandMapper.update(brand);
        System.out.println("受影响的行数:"+count);
        // 提交事务
        sqlSession.commit();
        sqlSession.close();
    }
② 修改动态字段
  1. 编写接口方法:Mapper 接口
    • 参数:部分数据,封装到对象中
    • 结果:void
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法:测试
    <update id="update">
        update 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>
(4) 删除
① 删除一个
  1. 编写接口方法:Mapper 接口
    • 参数:id
    • 结果:void
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法:测试
    void deleteById(int id);
    <delete id="deleteById">
        delete from brand where id=#{id};
    </delete>
    @Test
    public void testDeleteById() throws IOException {
        // 接收参数
        int id=6;
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession sqlSession = sqlSessionFactory.openSession(true); // 自动提交事务
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        brandMapper.deleteById(id);
        // 提交事务
        sqlSession.commit();
        sqlSession.close();
    }
② 批量删除
  1. 编写接口方法:Mapper 接口
    • 参数:id 数组
    • 结果:void
  2. 编写 SQL 语句:SQL 映射文件
  3. 执行方法:测试

Mybatis 会将数组参数封装为一个 Map 集合

  • 默认 array = 数组 (即 array 表示接口中的数组)
  • 使用 @Param 注解名改变 Map 集合的默认的 key 的名称
    void deleteByIds(@Param("ids") int [] ids);
    <!--
        Mybatis 会将数组参数封装为一个 Map 集合
        * 默认 array = 数组 (即 array 表示接口中的数组)
        * 使用 @Param 注解名改变 Map 集合的默认的 key 的名称
    -->
    <delete id="deleteByIds">
        delete from brand where id
        in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
    @Test
    public void testDeleteByIds() throws IOException {
        // 接收参数
        int[] ids= {5,6,7};
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession sqlSession = sqlSessionFactory.openSession(true); // 自动提交事务
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        brandMapper.deleteByIds(ids);
        // 提交事务
        sqlSession.commit();
        sqlSession.close();
    }

4. Mybatis 参数传递

Mybatis 接口方法中可以接收各种各样的参数,Mybatis 底层对于这些参数进行不同的封装处理方式

  • 单个参数:
    1. POJO (简单的实体类)类型:直接使用,属性名和参数占位符名称一致
    2. Map 集合:直接使用,键名和参数占位符名称一致
    3. Collection:封装为 Map 集合,可以使用 @Param 注解,设置对应参数的名称,这个注解是用来替换 Map 集合中默认的键名(arg)
      map.put("arg0",collection 集合)
      map.put("collection",collection 集合)
    4. List:封装为 Map 集合,可以使用 @Param 注解,设置对应参数的名称,这个注解是用来替换 Map 集合中默认的键名(arg)
      map.put("arg0",list 集合)
      map.put("collection",list 集合)
      map.put("list",list 集合)
    5. Array:封装为 Map 集合,可以使用 @Param 注解,设置对应参数的名称,这个注解是用来替换 Map 集合中默认的键名(arg)
      map.put("arg0",数组)
      map.put("array",数组)
    6. 其他类型:直接使用
  • 多个参数:封装为 Map 集合,可以使用 @Param 注解,设置对应参数的名称,这个注解是用来替换 Map 集合中默认的键名(arg)。
    map.put("arg0",参数值1)
    map.put("param1",参数值1)
    map.put("arg1",参数值2)
    map.put("param2",参数值2)
    使用 @Param("username")经历一次变换后变为了:
    map.put("username",参数值1)
    map.put("param1",参数值1)
    map.put("arg1",参数值2)
    map.put("param2",参数值2)

Mybatis 提供了 ParamNameResolver 进行参数封装
建议:将来都使用 @Param 注解来修改 Map 集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高!

5. 注解开发

使用注解开发会比配置文件开发更加方便
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好使用 XML 来映射语句
选择何种方式来配置映射,以及认为是否应该统一映射语句定义的形式,完全取决于个人和团队,不要拘泥于一种方式,可以轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。

  • 查询:@Select
  • 添加:@Insert
  • 修改:@Update
  • 删除:@Delete
  • 提示:注解完成简单的功能,配置文件完成复杂功能

示例:

@Select("select * from user where id=#{id}")
public User selectById(int id);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值