Mybatis笔记——基础篇
Mybatis笔记——基础篇

Mybatis笔记——基础篇

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

  • JavaEE 三层架构:
    • 表现层:页面展示
    • 业务层:业务处理
    • 持久层:数据持久化,负责将数据到保存到数据库的层
  • 框架:

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

原生 JDBC 缺点:

  1. 硬编码
  • 注册驱动,获取连接
  • sql 语句繁琐

mybatis 解决方案:使用 properties 文件

  1. 操作繁琐
  • 手动设置参数
  • 手动封装结果集

mybatis 解决方案:封装为方法

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

1,MyBatis 快速入门

写在前面,参照项目和文件目录结构如下:

  1. 创建 user 表,添加数据
create database mybatisdb;
use mybatisdb;

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. 创建模块,导入坐标
<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.46</version>
    </dependency>

</dependencies>
  1. 编写 MyBatis 核心配置文件 — >替换连接信息 解决硬编码问题

导入 logback.xml 文件(通用模板):

<?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.itheima" 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-config.xml 文件,写入配置信息:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://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://localhost:3306/mybatisdb?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="320320"/>
<!--                //暂时不知道为什么,不能配置这几个,会报错-->
<!--                <property name="initialSize" value="30" />-->
<!--                <property name="maxActive" value="100" />-->
<!--                <property name="autoReconnect" value="true" />-->
            </dataSource>
        </environment>
    </environments>
    <mappers>
<!--        加载sql映射文件,等会修改-->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>
  1. 编写 SQL 映射文件 –>统一管理 sl 语句,解决硬编码问题

因为现在要操作 User 表,pojo 对也是 User,所以新建 pojo 下的 User 对象:

public class User {
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String addr;
    //记得加setter和getter和toString方法
 }

然后新建 UserMapper.xml,写入配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
        
<!--先使用test命名空间,等下改-->
<mapper namespace="test">
    <select id="selectUser" resultType="com.example.pojo.User">
        select * from tb_user
    </select>
</mapper>

这个配置里面包含了一个 select 操作,具体方法为 selectUser()

  1. 编码
    1. 定义 POJO 类
    2. 加载核心配置文件,获取 SqlSessionFactory 对象如下:
    3. 获取 SqlSession 对象,执行 SQL 语句如下:
    4. 释放资源如下:
public class MybatisDemo {
    public static void main(String[] args) throws IOException {
        //1,加载核心配置文件,获取 SqlSessionFactory 对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2,获取 SqlSession 对象
        SqlSession session = sqlSessionFactory.openSession();
        //3,执行 SQL 语句
        List<User> users = session.selectList("test.selectAll");
        for (User user:users) {
            System.out.println(user.toString());
        }
        //4,释放资源
        session.close();
    }
}

执行 main 方法,输出如下,成功:

2,Mapper 代理开发

上面的方法中:

        List<User> users = session.selectList("test.selectAll");

还是硬编码,需要解决,目标是只要一调用 userMapper.selectAll()就返回一个 List 集合

方法是 mapper 代理

mapper 代理开发规范要求:

  • 定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下
  • 设置 SQL 映射文件的 namespace 属性为 Mapper 接口全限定名
  • 在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的 id,并保持参数类型和返回值类型一致

下面一步一步实现

  1. 定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下

这里我们先在 java.com。example 目录下新建 mapper.UserMapper 接口,然后再 resources 下面新建 com/example/mapper 包,注意,resources 下不会认为点。是文件夹分隔符,需要使用/来表示多级文件夹

然后将 UserMapper.xml 拖到新建的 com/example/mapper 下去:

这样一来,编译后映射文件 UserMapper.xml 和 UserMapper 接口就在同一目录下了

接着,由于我们改变了 UserMapper.xml 的路径,所以需要去 mybatis-config.xml 里面修改 mapper 的路径:

直接右键 UserMapper.xml,copy from source root:

粘贴到 mybatis-config.xml 的 mapper:

    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
    </mappers>
  1. 设置 SQL 映射文件的 namespace 属性为 Mapper 接口全限定名
  1. 在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的 id,并保持参数类型和返回值类型一致

可以看到,我们在 UserMapper.xml 中写了一个 id 为 selectAll 的 select,返回值为 User,我们要去 UserMapper 接口中去实现:

<select id="selectAll" resultType="com.example.pojo.User">
public interface UserMapper {
//  <select id="selectAll" resultType="com.example.pojo.User">
    List<User> selectAll();
    
}

然后写个测试 demo2:

public class MybatisDemo2 {
    public static void main(String[] args) throws IOException {
        //1,加载核心配置文件,获取 SqlSessionFactory 对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2,获取 SqlSession 对象
        SqlSession session = sqlSessionFactory.openSession();
        //3,执行SQL 语句
        //  获取UserMapper接口的代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<User> users = userMapper.selectAll();
        for (User user:users) {
            System.out.println(user.toString());
        }
        //4,释放资源
        session.close();
    }
}

执行,输出结果:

可以看到结果一致

mapper 的简化开发路径匹配:

如果 Mapper 接口名称和 SQL 映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化 SQL 映射文件的加载(前提是遵守三个规则):

<package name=”com.example.mapper”/>

用以下方式代替

<mapper resource=”com/example/mapper/UserMapper.xml”/>

即:

    <mappers>
<!--    <mapper resource="com/example/mapper/UserMapper.xml"/>-->
        <package name="com.example.mapper"/>
    </mappers>

3,MyBatis 核心配置文件

mybatis-config.xml 如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置别名,配好后UserMapper.xml中可以直接用resultType="User"-->
    <typeAliases>
        <package name="com.example.pojo.User"/>
    </typeAliases>
    
    <!--配置数据库连接环境,可以有多套environment,对应不同环境,通过default属性切换不同environment-->
    <environments default="development">
        <environment id="development"><!--开发环境-->
            <!--暂时用jdbc事务管理方式-->
            <transactionManager type="JDBC"/>
            <!--数据库连接池-->
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="320320"/>
            </dataSource>
        </environment>
        <environment id="product"><!--生产环境-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="320320"/>
            </dataSource>
        </environment>
    </environments>
    <!--包名映射-->
    <mappers>
        <!--<mapper resource="com/example/mapper/UserMapper.xml"/>-->
        <package name="com.example.mapper"/>
    </mappers>
</configuration>

另外还可以在 mybatis-config.xml 中配置别名:

<!--配置别名,配好后UserMapper.xml中可以直接用resultType="User"-->
<typeAliases>
    <package name="com.example.pojo"/>
</typeAliases>

注意到 pojo 就可以了,不要后面再加上对应的实体类名

配好后 UserMapper.xml 中可以直接用 resultType=”User”:

<!--使用包扫描,resultType直接写"User"即可-->
<select id="selectAll" resultType="User">
    select * from tb_user
</select>

对于基本数据类型,mybatis 已经自动配置好了别名(不区分大小写):

另外,xml 文件属性配置顺序最好遵循官网介绍的顺序,否则有时候会报错:

4,配置文件完成增删改查

要完成的功能列表清单

  1. 查询
    1. 查询所有数据
    2. 查看详情
    3. 条件查询
  2. 添加
  3. 修改
    1. 修改全部字段
    2. 修改动态字段
  4. 删除
    1. 删除一个
    2. 批量删除

0,初始环境准备

建表:

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


SELECT * FROM tb_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;
    }

创建 test 类:

安装 mybatisX 插件:

Mybatisx 是一款基于 IDEA 的快速开发插件,为效率而生

主要功能:

XML 和 接口方法的相互跳转

根据接口方法生成 statement

安装完成后重启 idea,可以发现小鸟出现了:

红色小鸟是 sql 映射文件,蓝色小鸟是 Mapper 接口

点击蓝色鸟可以直接跳到对应的 mapper 接口,点击红色的直接跳到 sql 映射 xml 文件

在接口中新建方法,点击 create statemaent 可以自动生成:

然后自己里面写 sql 语句即可

查询

(1)查询所有数据

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

解决方案:起别名:对不一样的列名起别名,让别名和实体类的属性名一样

起别名缺点:每次查询都要定义一次别名

  解决方案:sql 片段

<sql id="brand_column">
    id,brand_name as brandName,company_name as companyName,ordered,description,status
</sql>
<select id="selectAll" resultType="Brand">
    select
        <include refid="brand_column"/>
    from tb_brand
</select>

    sql 片段缺点:不灵活

最终解决方案:resultMap

  1. 定义<resultMap>标签
  2. 在<select>标签中,使用 resultMap 属性替换 resultType 属性

完整代码如下:

    <!--id:唯一标识type: 映射的类型,支持别名-->
    <resultMap id="brandResultMap" type="brand">
<!--        id:完成主键字段的映射-->
<!--        result:完成一般字段的映射-->
<!--            column: 表的列名-->
<!--            property: 实体类的属性名-->
        <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>
    </resultMap>
    <select id="selectAll" resultMap="brandResultMap">
        select * from tb_brand
    </select>

(2)根据字段值查询

这里涉及到了传入查询的参数:

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

使用时候,直接传入参数即可:

int id=3;
Brand brand = brandMapper.selectById(id);

#{id}称为参数占位符:

  1. #{}:会将其替换为 ?,为了防止 SQL 注入
  1. ${}: 拼 sql,会存在 SQL 注入问题
  1. 使用时机:
  • 参数传递的时候: #{}
  • 表名或者列名不固定的情况下: ${},但时还是会存在 SQL 注入问题

特殊字符:比如小于号,会被认为是 xml 标签开始符号

处理:

1.转义字符:比如小于号使用&lt:

select * from tb_brand where id &lt ${id};

2.CDATA 区:

<![CDATA[内容]]>
比如:
<![CDATA[ < ]]>

CDATA 区内的字符串会被认为是纯字符

(3)条件查询

<select id="selectByCondition" resultMap="brandResultMap">
    select * from tb_brand where
    status=#{status}
    and company_name like #{companyName}
    and brand_name like #{brandName}
</select>

如图,需要通过三个字段查询数据,有三种传参方式:

  1. 散装参数: 如果方法中有多个参数,需要使用@Param(”SQL 参数占位符名称”)
List<Brand> selectByCondition(@Param("status") int status,@Param("companyName") String conpanyName,@Param("companyName") String companyName);
int status=3;
String companyName="小";
String brandName="米";
companyName="%"+companyName+"%";
brandName="%"+brandName+"%";
List<Brand> brands = brandMapper.selectByCondition(status,companyName,brandName);

略麻烦,要处理参数

  1. 实体类封装参数

只需要保证 SQL 中的参数名和实体类属性名对应上,即可设置成功

List<Brand> selectByCondition(Brand brand);
int status=1;
String companyName="小";
String brandName="米";
companyName="%"+companyName+"%";
brandName="%"+brandName+"%";
//封装成对象
Brand brandParam=new Brand(brandName,companyName,status);
  1. map 集合封装参数

只需要保证 SQL 中的参数名和 map 集合的键的名称对应上,即可设置成功

List<Brand> selectByCondition(Map map);
int status=1;
String companyName="小";
String brandName="米";
companyName="%"+companyName+"%";
brandName="%"+brandName+"%";

//封装集合
Map map=new HashMap<>();
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);

List<Brand> brands = brandMapper.selectByCondition(map);

(4)多条件动态 SQL

SQL 语句会随着用户的输入或外部条件的变化而变化,我们称为 动态 SQL

使用 if 表达式

<select id="selectByCondition" resultMap="brandResultMap">
    <!--动态sql-->
    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>
</select>

这样一来就可以不传入所有参数,而 sql 语句会动态变化,但是存在问题,如果不传入第一个参数,那么产生的表达式直接是:

select * from tb_brand where and company_name like #{companyName}

会产生语法错误,解决方案:

1,恒等式:

<select id="selectByCondition" resultMap="brandResultMap">
    <!--动态sql-->
    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>

2,<where>标签

<select id="selectByCondition" resultMap="brandResultMap">
    <!--动态sql-->
    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>

原理是如果某个条件成立 mybatis 就自动去掉语句前面的 and

(5)单条件动态 sql

从多个条件中选择一个

Choose (when,otherwise): 选择,类似于 Java 中的 switch 语句

List<Brand> selectByConditionSimple(Brand brand);
<select id="selectByConditionSimple" resultMap="brandResultMap">
    select * from tb_brand where
    <choose><!--相当于switch-->
        <when test="status!=null"><!--相当于case-->
            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>
        int status=1;
        String companyName="小";
        String brandName="米";
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";
//        //封装成对象
        Brand brandParam=new Brand();
        brandParam.setBrandName(brandName);
        
        List<Brand> brands = brandMapper.selectByConditionSimple(brandParam);

上面的 <otherwise>1=1</otherwise>是为了应对一个参数都不传的情况,如果觉得麻烦或者没必要可以使用<where>:

<select id="selectByConditionSimple" resultMap="brandResultMap">
    select * from tb_brand where
    <where>
        <choose><!--相当于switch-->
            <when test="status!=null"><!--相当于case-->
                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>

添加

<insert id="add">
    insert into tb_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="QQ";
        int ordered=300;
        String description="腾讯投资公司";
//        //封装成对象
        Brand brandParam=new Brand();
        brandParam.setBrandName(brandName);
        brandParam.setStatus(status);
        brandParam.setCompanyName(companyName);
        brandParam.setDescription(description);
        brandParam.setOrdered(ordered);


        //1,加载核心配置文件,获取 SqlSessionFactory 对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2,获取 SqlSession 对象
        SqlSession session = sqlSessionFactory.openSession();
        //3,执行SQL 语句
        //  获取UserMapper接口的代理对象
        BrandMapper brandMapper = session.getMapper(BrandMapper.class);
        brandMapper.add(brandParam);
        //注意查询操作需要提交事务
        session.commit();
        //4,释放资源
        session.close();
    }

这里需要注意的是查询操作需要提交事务,需要手动 commit,如果不想这么麻烦,可以在 opensession 时传入参数 true 表示打开自动提交:

SqlSession session = sqlSessionFactory.openSession(true);

主键返回:在数据添加成功后,需要获取插入数据库数据的主键的值

需要在 Mapper 中配置两个属性:

<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>
void add(Brand brand);//接口方法

brandMapper.add(brandParam);
System.out.println(brandParam.getId());//打印7

修改

修改全部字段

<update id="update">
    update tb_brand
    set
        brand_name=#{brandName},
        company_name=#{companyName},
        ordered=#{ordered},
        description=#{description},
        status=#{status}
    where id=#{id}
</update>
@Test
public void testUpdate() throws IOException {
    //封装成对象
    Brand brandParam=new Brand();
    brandParam.setBrandName("腾讯1");
    brandParam.setStatus(0);
    brandParam.setCompanyName("QQ1");
    brandParam.setDescription("腾讯投资公司1");
    brandParam.setOrdered(301);
    brandParam.setId(6);

    //1,加载核心配置文件,获取 SqlSessionFactory 对象
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //2,获取 SqlSession 对象
    SqlSession session = sqlSessionFactory.openSession(true);
    //3,执行SQL 语句
    //  获取UserMapper接口的代理对象
    BrandMapper brandMapper = session.getMapper(BrandMapper.class);
    int count=brandMapper.update(brandParam);
    System.out.println(count);
    //4,释放资源
    session.close();
}

动态修改字段

当只需要修改少数几个字段时候,需要使用动态修改字段:

<update id="update">
    update tb_brand
    <set>
        <if test="status!=null">
            status=#{status},
        </if>
        <if test="ordered!=null">
            ordered=#{ordered},
        </if>
        <if test="companyName!=null and companyName!=''">
            company_name = #{companyName},
        </if>
        <if test="brandName!=null and brandName!=''">
            brand_name = #{brandName},
        </if>
        <if test="description!=null and description!=''">
            description = #{description}
        </if>
    </set>
    where id=#{id}
</update>
        //封装成对象
        Brand brandParam=new Brand();
        brandParam.setId(6);
        brandParam.setBrandName("腾讯2");
        brandParam.setStatus(0);
        //1,加载核心配置文件,获取 SqlSessionFactory 对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2,获取 SqlSession 对象
        SqlSession session = sqlSessionFactory.openSession(true);
        //3,执行SQL 语句
        //  获取UserMapper接口的代理对象
        BrandMapper brandMapper = session.getMapper(BrandMapper.class);
        int count=brandMapper.update(brandParam);
        System.out.println(count);
        //4,释放资源
        session.close();

删除

单个删除:

<delete id="deleteById">
    delete from tb_brand where id=#{id}
</delete>
    int deleteById(int id);
@Test
public void testById() throws IOException {
    Brand brandParam=new Brand();
    brandParam.setId(6);
    //1,加载核心配置文件,获取 SqlSessionFactory 对象
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //2,获取 SqlSession 对象
    SqlSession session = sqlSessionFactory.openSession(true);
    //3,执行SQL 语句
    BrandMapper brandMapper = session.getMapper(BrandMapper.class);
    int count=brandMapper.deleteById(brandParam.getId());
    System.out.println(count);
    //4,释放资源
    session.close();
}

批量删除:

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

  • 默认: array = 数组
  • 还可以使用@param()注解改变 map 集合的默认 key 的名称
  1. array 数组方式:
int[] ids={8,9};
...
int count=brandMapper.deleteByIds(ids);
int deleteByIds(int[] ids);
<delete id="deleteByIds">
    delete from tb_brand where id in
        <!--默认方式array-->
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
</delete>
  1. 注解方式:
int deleteByIds(@Param("ids") int[] ids);//表示传入mybatis的map集合更改名称为ids
<delete id="deleteByIds">
    delete from tb_brand where id in
        <!--注解方式-->
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
</delete>

@param 底层原理

MyBatis 参数封装:

  1. 单个参数:
  • POJO 类型: 直接使用,属性名一定要和参数占位符名称一致
  • Map 集合:直接使用,键名 和 参数占位符名称 一致
  • Collection:封装为 Map 集合,可以使用@Param 注解,替换 Map 集合中默认的 arg 键名
  • List:封装为 Map 集合,可以使用@Param 注解,替换 Map 集合中默认的 arg 键名
  • Array:封装为 Map 集合,可以使用@Param 注解,替换 Map 集合中默认的 arg 键名
  • 其他类型:比如 int ,直接使用
  1. 多个参数: 封装为 Map 集合,可以使用@Param 注解,替换 Map 集合中默认的 arg 键名

对于有多个传入参数的情况,接口文件需要使用注解,比如上面(3)条件查询中使用的散装参数注解方式:

List<Brand> selectByCondition(@Param("status") int status,@Param("companyName") String conpanyName,@Param("companyName") String companyName);
int status=3;
String companyName="小";
String brandName="米";
companyName="%"+companyName+"%";
brandName="%"+brandName+"%";
List<Brand> brands = brandMapper.selectByCondition(status,companyName,brandName);

其实底层是通过一个 ParamNameResolver()方法来封装的,源码如下:

public Object getNamedParams(Object[] args) {
    int paramCount = this.names.size();
    if (args != null && paramCount != 0) {
        if (!this.hasParamAnnotation && paramCount == 1) {
            Object value = args[(Integer)this.names.firstKey()];
            return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);
        } else {
            Map<String, Object> param = new MapperMethod.ParamMap();
            int i = 0;

            for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                Map.Entry<Integer, String> entry = (Map.Entry)var5.next();
                param.put(entry.getValue(), args[(Integer)entry.getKey()]);
                String genericParamName = "param" + (i + 1);
                if (!this.names.containsValue(genericParamName)) {
                    param.put(genericParamName, args[(Integer)entry.getKey()]);
                }
            }

            return param;
        }
    } else {
        return null;
    }
}

比如传入两个参数,zhangsan 和 age,那么首先会判断是否是一个参数,不是的话创建一个 Map<String, Object> param 集合,然后每个参数 put 两次:

第一个参数:

  • 第一次 param.put(”arg0″,”zhangsan”)
  • 第二次 param.put(”param1″,”zhangsan”)

第二个参数:

  • 第一次 param.put(”arg1″,”age”)
  • 第二次 param.put(”param2″,”age”)

后面的参数同理

所以在 mapper 中可以直接用 arg0 和 param1 来直接获取参数:#{arg0},但是不推荐这样因为代码很难阅读,所以使用@param 注解来替换默认的 arg 键名,比如两个参数使用 username 和 age 注解:

第一个参数:

  • 第一次 param.put(”username”,”zhangsan”)
  • 第二次 param.put(”param1″,”zhangsan”)

第二个参数:

  • 第一次 param.put(”age”,”age”)
  • 第二次 param.put(”param2″,”age”)

建议: 都使用@ Param 注解来修改 Map 集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高!

5,注解完成增删改查

  • 注解完成简单功能
  • 配置文件完成复杂功能

例:注解完成查询:

先把 BrandMapper.xml 中的 selectById 注释掉,然后:

BrandMapper.java映射文件:


@Select("select * from tb_brand where id=#{id};")
Brand selectById(int id);

测试:

@Test
public void testSelectById() throws IOException {
    int id=1;
    //1,加载核心配置文件,获取 SqlSessionFactory 对象
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //2,获取 SqlSession 对象
    SqlSession session = sqlSessionFactory.openSession();
    //3,执行SQL 语句
    //  获取UserMapper接口的代理对象
    BrandMapper brandMapper = session.getMapper(BrandMapper.class);
    Brand brand = brandMapper.selectById(id);
    System.out.println(brand.toString());
    //4,释放资源
    session.close();
}

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不以心,还会让你本就复杂的 SQL 语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Index