MyBatis

一、概述

二、简介

2.1概念

MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发。MyBatis 本是 Apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis。2013 年 11 月迁移到 Github。官网:https://mybatis.org/mybatis-3/zh/index.html(持久层是负责将数据到保存到数据库的那一层代码;JavaEE 三层架构:表现层、业务层、持久层)

2.2MyBatis的改进点

三、快速入门

3.1整体步骤

查询 user 表中所有数据

  1. 创建 user 表,添加数据
  2. 创建模块,导入坐标
  3. 编写 MyBatis 核心配置文件 --> 替换连接信息 解决硬编码问题
  4. 编写 SQL 映射文件 --> 统一管理 sql 语句,解决硬编码问题
  5. 编码
    1. 定义 POJO 类
    2. 加载核心配置文件,获取 SqlSessionFactory 对象
    3. 获取 SqlSession 对象,执行 SQL 语句
    4. 释放资源
<!--连接池信息-->
<dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///db1?useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="1234"/>
</dataSource>
<select id="selectAll" resultType="com.itheima.pojo.User">
    select * from tb_user;
</select>

下面主要是讲第5.2到5.4步。

3.2实验步骤讲解

1、核心配置文件:

<?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.itheima.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://localhost:3306/testmybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </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://localhost:3306/testmybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--加载sql映射文件-->
        <mapper resource="com/itheima/mapper/UserMapper.xml"/>
    </mappers>

</configuration>

注意这一段是加载映射文件的:

 <mappers>
        <!--加载sql映射文件-->
        <mapper resource="com/itheima/mapper/UserMapper.xml"/>
    </mappers>

2、加载核心配置文件,获取 SqlSessionFactory 对象;获取 SqlSession 对象,执行 SQL 语句;释放资源。

package com.itheima;

import com.itheima.pojo.User;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisDemoM {
    public static void main(String[] args) throws IOException {
//        获取SqlSessionFactory:这段代码直接从官网获取
//        下面加载的是核心配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        使用sqlsession来执行sql语句,传入映射文件的方法时,需要加入命名空间,以便后面有多个selectAll好区分
       List<User> userList=  sqlSession.selectList("com.itheima.mapper.UserMapper.selectAll");
        System.out.println(userList);
//        释放资源
        sqlSession.close();

    }
}

结果:

四、代理开发

4.1整体步骤

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

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

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

编码

  1. 通过 SqlSession 的 getMapper方法获取 Mapper 接口的代理对象
  2. 调用对应方法完成 sql 的执行

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

<mappers>
    <!--加载sql的映射文件-->
    <!--<mapper resource="com/itheima/mapper2/UserMapper.xml"/>-->
    <package name="com.itheima.mapper"/>
</mappers>

4.2实验

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

注意在resources目录下要新建多级目录的时候使用的是“/”,而不是“.”,如果使用"com.itheima.mapper"系统会直接将"com.itheima.mapper"当成一个目录创建,而不是多级目录,正确的方式如下:

只有这样才能达到Mapper 接口和 SQL 映射文件放置在同一目录下的效果:

虽然 创建成功后显示的是这样的:

但是已经达到了Mapper 接口和 SQL 映射文件放置在同一目录下的效果。

2、设置 SQL 映射文件的namespace属性为 Mapper 接口全限定名:

<?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.itheima.mapper.UserMapper">
<!--
    namespace:命名空间
-->
<select id="selectAll" resultType="user">
        select *
        from tb_user;
    </select>
</mapper>

3、在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的id,并保持参数类型和返回值类型一致:

package com.itheima.mapper;

import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

值得注意的是xml映射文件中的selectAll的返回值是User,但是在Mapper中我们要依据语义来确定是使用List接收还是使用单个User接收,因为即使Mapper层的返回值类型是List,但是xml映射文件只与List的泛型,即User对齐即可。

3、编码:通过 SqlSession 的 getMapper方法获取 Mapper 接口的代理对象;调用对应方法完成 sql 的执行

package com.itheima.pojo;

import com.itheima.mapper.UserMapper;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisDemoM2 {
    public static void main(String[] args) throws IOException {
        //        获取SqlSessionFactory:这段代码直接从官网获取
//        下面加载的是核心配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        通过sqlSession来获取Mapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//        然后使用Mapper执行相关的方法即可
        List<User> userList =userMapper.selectAll();
        System.out.println(userList);
        sqlSession.close();
    }
}

4、使用包扫描的方式简化 SQL 映射文件的加载

<?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.itheima.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://localhost:3306/testmybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </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://localhost:3306/testmybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
         <!--Mapper代理方式-->
        <package name="com.itheima.mapper"/>
    </mappers>

</configuration>

改的是这一段:

<mappers>
         <!--Mapper代理方式-->
        <package name="com.itheima.mapper"/>
    </mappers>

五、Mybatis核心配置文件

上面是Mybatis核心配置文件可以配置的一些东西(从官网截取),需要注意的是配置时需要严格遵循上面的顺序,否则会报错,下面主要是抽取几个重要的来进行介绍,不再逐一介绍了。

1、环境

像环境的话是可以设置多个环境的,通过default属性切换不同的environment:

 <!--
    environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
    -->
    <environments default="development">
        <environment id="development">
<!--            事务不需要mybatis来接管,将来Spring会接管,包括数据源-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/testmybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="guangxun.kg"/>
            </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://localhost:3306/testmybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="guangxun.kg"/>
            </dataSource>
        </environment>
    </environments>

2、别名

通过来起别名,在引用时不区分实体类的大小写了,也不需要再写包名了。

<!--相当于给pojo这个包下面的所有实体类都取了别名,在引用时不区分实体类的大小写了,也不需要再写包名了-->
    <typeAliases>
        <package name="com.itheima.pojo"/>
    </typeAliases>

起了别名后映射文件就可以这样写了:

<!--statement-->
    <select id="selectAll" resultType="user">
        select *
        from tb_user;
    </select>

六、增删改查功能

6.1实验准备——安装Mybatisx插件

Mybatisx插件可以将映射文件中的sql语句(statement)与mapper接口相对应,sql语句是红色的小鸟对应的,mapper接口是蓝色的小鸟对应的,点击小鸟会相互跳转。同时可以生成mapper对应的sql语句标签和返回值类型。(sql语句要自己写)

6.2实验

6.2.1查询所有

1、mapper:

List<Brand> selectAll();

2、xml映射文件:

<select id="selectAll" resultType="brand">
        select *
        from tb_brand;
    </select>/

3、测试

package com.itheima.test;

import com.itheima.mapper.BrandMapper;
import com.itheima.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest2 {
    public static void main(String[] args) throws IOException {
//        获取sqlsessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        获取mapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//        使用执行相关方法
       List<Brand>brandList =  brandMapper.selectAll();
        System.out.println(brandList);
        sqlSession.close();
    }
}

重要的是分析业务需要什么参数,需要返回什么结果。

但是我们可以看到有的字段没有封装上显示是空值,这是因为实体类属性的名称与数据库表的字段名称不一致:

解决方法如下:

1、方法一:我们可以使用as在映射文件中为数据库表的字段名取一个别名,让别名和实体类的属性名一样:

<select id="selectAll" resultType="brand">
        select id, brand_name as brandName, company_name as companyName, ordered, description, status
        from tb_brand;
    </select>

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

2、方法二:定义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"></include>
            from tb_brand;
    </select>

缺点:不灵活。

3、使用resultMap定义字段与实体类属性的映射:

  1. 定义标签2. 在标签中;
  2. 使用resultMap属性替换 resultType属性。
<!--
        id:唯一标识
        type:映射的类型,支持别名
    -->
    <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 * from tb_brand;
</select>

实际上如果使用Mybatis做持久层框架的话,sql数据表字段是下划线命名,实体类属性是驼峰命名的话,可以在application.properties配置文件中开启驼峰命名自动映射,这样就可以不用再配置别名之类的了,只需要在数据表的字段中严格按照下划线命名,实体类中属性按照驼峰命名即可:

mybatis.configuration.map-underscore-to-camel-case=true

要是.yml配置文件的话可以这样:

mybatis:
  configuration:
    map-underscore-to-camel-case: true

6.2.2查看详情

1、分析思路:

2、实现:

mapper:

 Brand selectById(int id);

映射文件:

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

    </select>

3、说明:

a.#{id}中的参数名与mapper对应。

mybatis中参数占位符有两种:#{}和${}。

​ #{}:会将其替换为 ?,为了防止SQL注入

​ ${}:拼sql。会存在SQL注入问题

​ 使用时机:

  • 参数传递的时候:#{}
  • 表名或者列名不固定的情况下:${} 会存在SQL注入问题,表名或者列名不固定:
<select id="selectById"  resultMap="brandResultMap">
        select *
        from ${tableNmae} where id = #{id};

    </select>

b.参数类型:parameterType:可以省略,直接依据mapper中的即可,要写的话是这样:

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

    </select>

c.特殊字符处理(以"<"符号为例):

转义字符(字符少的时候用):

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

    </select>

CDATA区(输入CD会自动补齐,且在字符多的时候用):

 select *
        from tb_brand
        where id
         <![CDATA[
            <
         ]]>
         #{id};

6.2.3条件查询

1、分析:

​ 像公司名称这些文字性的描述就使用就使用”like“模糊查询,像id,status状态码这些就使用”=查询“即可。

2、实现

a.情形一:散装参数:如果方法中有多个参数,需要使用@Param(“SQL参数占位符名称”)

mapper:

List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);

散装参数:如果方法中有多个参数,需要使用@Param(“SQL参数占位符名称”),也就是说@Param中的参数名要与映射文件中的sql语句的#{}参数占位符一致。

映射文件:

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

测试(测试时就加上几个,参数且处理一下,其余不变):

//接收参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

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

b.情形二:对象参数:对象的属性名称要和参数占位符名称一致

mapper写好对应的方法:

 List<Brand> selectByCondition(Brand brand);

映射文件不需要改动,只需要将原来散装的测试封装为对象传递即可:

@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);
        
        //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.selectByCondition(brand);
        System.out.println(brands);

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

    }

c.情形三:使用map封装

使用maper封装,map的key值要与sql语句中的参数占位符保持一致,xml映射文件依然不用变:
mapper:

List<Brand> selectByCondition(Map map);

测试:

 @Test
    public void testSelectByCondition() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

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

        //封装map
        Map map = new HashMap();
         map.put("status" , status);
        map.put("companyName", companyName);
         map.put("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. 获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

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

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

    }

6.2.3多条件动态查询

1.

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

我们要是这样写的话会存在一定的弊端,比如说用户想要只输入一个条件,比如不输入companyName,来查询所有名为companyName的公司的信息,是无法实现的,因为其他值不输入,sql语句中的值就会赋值为空,而数据表中又没有这样的记录。

不输入brandName:

 Map map = new HashMap();
 map.put("status" , status);
 map.put("companyName", companyName);
//map.put("brandName" , brandName);

所以这时候就需要进行动态判断,依据用户的输入或外部条件的变化而变化,if:用于判断参数是否有值,使用test属性进行条件判断:

  <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>
    </select>

注意第一个里面不需要加and,然后的test属性写的是判断条件,且引用的是实体类的属性值。这样就可以实现动态查询了:

当然这样写是会存在bug的,当我们只给最后一个值赋值时就会多出一个and,会报错
 Map map = new HashMap();
 //map.put("status" , status);
 //map.put("companyName", companyName);
 map.put("brandName" , brandName);

我们可以有两种解决方式:

1、在where后面加入恒等式:

<select id="selectByCondition" resultMap="brandResultMap">
        select * from tb_brand
        where 1=1
            <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>

2、使用标签自动识别是否只有一个条件,自动去掉多余的and:

 <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>

6.2.4单条件查询

mapper:

 List<Brand> selectByConditionSingle(Brand brand);

映射文件:

 <select id="selectByConditionSingle" resultMap="brandResultMap">
        select *
        from tb_brand
        where
        <choose><!--相当于switch-->
            <when test="status != null"><!--相当于case-->
                status = #{status}
            </when>
            <when test="companyName != null and companyName != '' "><!--相当于case-->
                company_name like #{companyName}
            </when>
            <when test="brandName != null and brandName != ''"><!--相当于case-->
                brand_name like #{brandName}
            </when>
        </choose>
    </select>

这样就可以实现单条件查询了:

但是这种方法有bug,如果用户一个条件不输入就会报错:

解决方法:

1、在中加恒等式:

 <select id="selectByConditionSingle" resultMap="brandResultMap">
        select *
        from tb_brand
        where
        <choose><!--相当于switch-->
            <when test="status != null"><!--相当于case-->
                status = #{status}
            </when>
            <when test="companyName != null and companyName != '' "><!--相当于case-->
                company_name like #{companyName}
            </when>
            <when test="brandName != null and brandName != ''"><!--相当于case-->
                brand_name like #{brandName}
            </when>
            <otherwise>
                1 = 1
            </otherwise>
        </choose>

    </select>

2、使用标签

    <select id="selectByConditionSingle" resultMap="brandResultMap">
        select *
        from tb_brand
        <where>
            <choose><!--相当于switch-->
                <when test="status != null"><!--相当于case-->
                    status = #{status}
                </when>
                <when test="companyName != null and companyName != '' "><!--相当于case-->
                    company_name like #{companyName}
                </when>
                <when test="brandName != null and brandName != ''"><!--相当于case-->
                    brand_name like #{brandName}
                </when>

            </choose>
        </where>
    </select>
### 6.2.5添加
6.2.5.1普通添加:

1、mapper:

 void add(Brand brand);

2、xml映射文件

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

        //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 = sqlSessionFactory.openSession(true);

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

        //4. 执行方法

        brandMapper.add(brand);

        //提交事务
        sqlSession.commit();

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

    }

注意使用mybatis进行插入需要 sqlSession.commit();提交事务,否则会插入不成功。openSession()默认为开启事务(不会自动提交),需要使用 sqlSession.commit();来提交事务。当然也可以使用SqlSession sqlSession = sqlSessionFactory.openSession(true);进行自动提交事务。

6.2.5.2主键返回

上面会存在一个问题,就是插入后不会返回id,要是我们的订单需要插入订单项时,得不到订单号的话,那么就无法插入订单项就会出问题:

@Test
    public void testAdd2() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "hh手机";
        String brandName = "hh";
        String description = "手机中的战斗机";
        int ordered = 100;


        //封装对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);

        //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 = sqlSessionFactory.openSession(true);

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

        //4. 执行方法

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

        //提交事务
//        sqlSession.commit();

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

    }

这时id返回的是null,要实现注解返回的话我们只需要在xml映射文件中加上两属性即可,useGeneratedKeys设置为true,keyProperty设置为我们要返回的id:

<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>
### 6.2.6修改
6.2.6.1修改手全部字段

1、mapper

int update(Brand brand);

2、xml映射文件

<update id="update">
    update tb_brand
    set brand_name = #{brandName},
        company_name = #{companyName},
        ordered = #{ordered},
        description = #{description},
        status = #{status}
    where id = #{id};
</update>

3、测试

@Test
    public void testUpdate() throws IOException {
        //接收参数
        int status = 0;
        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);

        //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 = sqlSessionFactory.openSession(true);

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

        //4. 执行方法


        int count = brandMapper.update(brand);
        System.out.println(count);
        //提交事务
        sqlSession.commit();

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

    }

但是这种方式存在一定问题,但用户只想修改一部分值,其他值不变,用户没有赋值时,字段就会赋空值,就不符合要求了:

所以就需要使用到动态修改了。

6.2.6.2修改动态字段

主要是使用到标签判空,然后使用就是在字段只有一个修改时且该字段不是最后一个字段时,标签会帮我们处理最后一个多出来的",",或者说没有一个字段是需要修改时,标签也会帮我们灵活处理。

  <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>
### 6.2.7删除
6.2.7.1删除单个

1、mapper

 void deleteById(int id);

2、xml映射文件

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

3、测试

@Test
    public void testDeleteById() throws IOException {
        //接收参数

        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 = sqlSessionFactory.openSession(true);

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

        //4. 执行方法

        brandMapper.deleteById(id);

        //提交事务
        sqlSession.commit();

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

    }
6.2.7.2批量删除(这里以数组的形式传递参数)

1、mapper

 void deleteByIds(int[] ids);

2、xml映射文件

 <delete id="deleteByIds">
        delete from tb_brand where id in
                             <foreach collection="array" item="id" separator="," open="(" close=")">
                                 #{id}
                             </foreach>
    </delete>

mybatis会将数组参数,封装为一个Map集合。默认:key为array, value为我们的数组,所以在这里要指定参数名array。

或者也可以使用@Param注解在Mapper层中指定名称:

void deleteByIds(@Param("ids") int[] ids);

这样xml中就可以直接使用"ids"这个名称了:

<delete id="deleteByIds">
        delete from tb_brand where id in
                             <foreach collection="ids" item="id" separator="," open="(" close=")">
                                 #{id}
                             </foreach>
    </delete>

collection是指定数组名称,item指定遍历出来的单个字段,separator是分隔符,open是左闭合,close是右闭合。

3、测试

 @Test
    public void testDeleteByIds() throws IOException {
        //接收参数

        int[] ids = {2,3};


        //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 = sqlSessionFactory.openSession(true);

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

        //4. 执行方法

        brandMapper.deleteByIds(ids);

        //提交事务
        sqlSession.commit();

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

    }

6.2.8参数传递

6.2.8.1多个参数传递

多个参数:封装为Map集合,默认的参数名如下:
map.put(“arg0”,参数值1)
map.put(“param1”,参数值1)
map.put(“param2”,参数值2)
map.put(“agr1”,参数值2)

当然可以使用@Param注解,替换Map集合中默认的arg键名:

mapper:

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

xml:

<select id="select" resultType="user">
        select *
        from tb_user
        where
            username = #{username}
        and password = #{password}
    </select>

也可以反过来验证,要是没有@Param:

User select(String username,String password);

也验证了什么参数的默认命名。也就是说下面这两种写法都可以:

 <select id="select" resultType="user">
        select *
        from tb_user
        where
            username = #{arg0}
        and password = #{arg1}
    </select>

但是一般使用@Param来在mapper中指定名字,其逻辑是通过用指定的命名来替换对应的arg:

mapper:

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

xml:

<select id="select" resultType="user">
        select *
        from tb_user
        where
            username = #{arg0}
        and password = #{param2}
    </select>

可以看到arg0被username替换掉了。当然也可以debug来验证它们的默认命名:

6.2.8.2单个参数

1、结论:

  • 单个参数:
    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. 其他类型:直接使用

2、验证:

可以看到Collection是默认arg0、collection两个默认命名,List和Array则就可以通过代码看出。

虽然有很多默认命名,但是在开发中我们使用@Param来进行映射会使得代码更具有可读性。

七、注解开发

以select为例,其他的类似:

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

直接在mapper层写就好了,简单的用注解,复杂的用xml映射文件。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐