MyBatis-入门

1、第一个 mybastis程序

1.1 导入jar包

<mybatis.version>3.4.1</mybatis.version>
<mysql.version>5.1.47</mysql.version>

<!--  mybatis  begin -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>${mybatis.version}</version>
</dependency>
<!--  mybatis  end -->
<!--  mysql  begin -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.version}</version>
</dependency>
<!--  mysql  end -->

1.2 jdcb.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/animedb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
username=root
password=root

1.3 mybatis-config.xml

数据配置信息可以直接在mybatis-config.xml中填写,也可以引入外部数据库配置信息文件;

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

<configuration>
    <!-- 外部引入的数据库配置文件 -->
    <properties resource="jdbc.properties"></properties>

    <typeAliases>
        <!-- 给所有的实体类 批量取别名 -->
        <package name="com.kgc.mybatis.bean"/>
    </typeAliases>

    <!-- 是指数据库配置环境,可以直接写值也可以引入外部配置文件 -->
    <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>
        <package name="com.kgc.mybatis.mapper"/>
    </mappers>

</configuration>

1.4 xxxMapper.xml

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">
<!--
SQl语句映射文件:
   namespace :名称空间
      1.单独使用SqlSession的方法,可以随意的定义,仅限于全限定名的方式,指定sql语句唯一标识字符串使用
      2.再面向接口开发时,不可以随便写了,必须时某个接口的全类名(全路径名:包名+类名)
 -->
<!-- <mapper namespace="huayu">  随意定义命名空间  -->    
<mapper namespace="com.kgc.mybatis.mapper.AnimeMapper">  <!-- 接口开发 -->
    
 <!--
    select标签:查询标签
    id属性:select标签的唯一标识,再面向开发时,对应的时namespace指定接口的某个方法名
    resultType 属性: 返回值类型
    #{id} :获取调用接口,获取参数值
-->
  <select id="selectAnime" parameterType="int" resultType="com.kgc.mybatis.bean.Anime">
    select * from animes where id = #{id}
  </select>
    
</mapper>

1.5 获取 SqlSession 并执行SQL

1.5.1 获取 SqlSessionFactory

  1. 指定mybatis的核心配置文件 “mybatis-config.xml”
  2. 使用mybatis提供Resources 工具类读取核心配置文件,转为输入流对象
  3. 使用SqlSessionFactoryBuilder对象的build方法,获取到DefaultSqlSessionFactory
//指定mybatis的核心配置文件路径
String resource = "mybatis-config.xml";

//使用mybatis提供的Resources 工具类读取核心配置文件,转为输入流对象
InputStream inputStream = Resources.getResourceAsStream(resource);

//使用SqlSessionFactoryBuilder对象的build方法,基于上一步的输入流对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//获取到的是 DefaultSqlSessionFactory
System.out.println(sqlSessionFactory);
//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@45c8e616

1.5.2 sqlSessionFactory 获取 SqlSession 实例

SqlSession 提供了在数据库执行 SQL 命令所需的所有方法,拿到SqlSession 就可以执行;

session.selectOne(statement,parameter); 方法参数分析

参数说明
statement 准备要执行的SQL语句的唯一标识符(mybatis可以识别,目的是可以通过此标识,查找到要执行的SQL语句)
parameter 准备要执行的SQL语句的参数值
//将实例化SqlSession放在 try()中,会自动关闭资源
try (SqlSession session  = sqlSessionFactory.openSession()) {
//通过获取的SqlSession的实例,执行查询操作
//根据动漫编号,查询动漫详情
//<mapper namespace="huayu">单独使用SqlSession的方法,namespace可以随意的定义
//namespace.方法名,通过sql语句唯一标识字符串,找到执行SQL
 Anime anime = (Anime) session.selectOne("huayu.selectAnime", 101);
    
 System.out.println(anime);
    
}

1.5.3 面向接口开发

注意在resource下建立文件夹的时候需要 com/kcg/mybatis ,中间的分隔符要使用反斜杠,不能用点;

  1. 创建接口xxxMapper.java(注意路径要与xxxMapper.xml的包名结构一致;)
  2. 通过session.getMapper(xxxMapper.class); 获取接口代理类
  3. 使用接口代理类,调用接口方法
try (SqlSession session  = sqlSessionFactory.openSession()) {
    //面向接口开发
    //推荐的方式
    AnimeMapper animeMapper = session.getMapper(AnimeMapper.class);
    System.out.println(animeMapper);
    //代理对象  org.apache.ibatis.binding.MapperProxy@4cc0edeb

    //直接就可以通过调用接口的方法,执行对象的SQl语句,并返回接口,不需要关心mybatis底层是调用那个方法
    Anime anime = animeMapper.selectAnime(101);
    System.out.println(anime);
    
}

try()中实例化对象报错问题

https://www.cnblogs.com/xiaoqigui/p/16598100.html

指定maven版本就可以

<plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-compiler-plugin</artifactId>
     <configuration>
         <source>8</source>
         <target>8</target>
     </configuration>
</plugin>

2、配置

2.1 所有配置 和 顺序

注意配置的顺序不能错,可以少写,但是不能顺序错

2.2 properties 属性

1、properties 属性:自定义核心属性配置,也可以引入外部的属性文件,比如jdbc.properties;

  • resource 属性指定外部配置文件,优先级高于property子标签属性配置,可以以单独使用,如果二者同时存在,优先以外部为主
//resource属性引入的外部配置文件,优先级高于property子标签属性
<properties resource="jdbc.properties">
<!-- <property name="driver" value="com.mysql.jdbc.Driver"/>-->
<!--  <property name="url" value="jdbc:mysql://localhost:3306/animedb?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=GMT%2b8"/>-->
<!--  <property name="username" value="root"/>-->
<!--  <property name="password" value="17585273765"/>-->
</properties>

2.3 settings 设置

一些设置的开启;

下面是简单举例,设置用很多,可以直接看官网;

<settings>
    <!-- 开启驼峰自动映射 -->
   <setting name="mapUnderscoreToCamelCase" value="true" />
    <!-- mybatis自带日志 -->
<!--  <setting name="logImpl" value="STDOUT_LOGGING"/>   -->
    <!-- LOG4J日志 -->
<!--  <setting name="logImpl" value="LOG4J"/>   -->
</settings>

2.4 typeAliases 类型别名

在SQL的xml文件中,resultType 可以使用 别名,不区分大小写;

  • 单个取别名,在SQL的xml文件中,resultType 可以使用 别名,不区分大小写
  • 批量取别名 :指定实体所在的父包路径,自动指定包及子包中所有的实体批量取别名,默认的类名是首字母小写,不区分大小写;
  • 建议,如果没有安装插件,从SQL映射xml文件中无法自动定位到目标实体,不利于代码的可读性;
  • mybatis对 Java 的基本数据类型 有 内建的类型别名;
<typeAliases>
    <!-- 单个实体类设置别名 -->
<!-- <typeAlias type="com.kgc.mybatis.bean.Anime" alias="Anime"></typeAlias>-->
    <!-- 对整个个实体包下的实体类设置别名 -->
  <package name="com.kgc.mybatis.bean"/>
</typeAliases>

2.5 environments 环境配置

MyBatis 可以配置成适应多种环境;

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

<environments default="development_test">
    <!--   开发环境     -->
    <environment id="development_dev">
        <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>
    <!--   测试环境     -->
    <environment id="development_test">
        ......
    </environment>
    <!--    生产环境    -->
    <environment id="development_prod">
        .......
    </environment>
</environments>

2.5.1 transactionManager 事务管理

environment 标签的子标签 transactionManager;

  • transactionManager:事务管理,使用的是jdbc的数据管理,以后框架整合会个Spring处理;
  • MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]");
    JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为
  • 如果使用Spring + mybatis :事务管理,交给Spring处理;
<transactionManager type="JDBC"/>

2.5.2 dataSource 数据源

  • 数据源配置:有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
    以后由Spring处理;
<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

2.6 mappers 映射器

  • 用于指定SQL文件的加载,作用:告诉MySql 到哪里去找 映射文件

2.6.1 单个SQL映射文件的加载

  • resource
<mappers>
	<mapper resource="com/kgc/mybatis/AnimeMapper.xml"></mapper>
</mappers>
  • 通过接口
<mappers>
	<mapper class="com.kgc.mybatis.mapper.AnimeMapper"></mapper>
</mappers>

2.6.2 批量SQL映射文件加载

只需要指定mapper接口的所在包;

<mappers>
	<package name="com.kgc.mybatis.mapper"/>
</mappers>

3、MyBatis 执行过程

3.1 代码分析执行过程

//1、加载全局配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

//2、实例化 SqlSessionFactoryBuilder 构建器
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

//3、解析 配置信息文件流,并返回  defaultSessionFactory
SqlSessionFactory defaultSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

//4、实例化 SqlSession
SqlSession sqlSession = defaultSessionFactory.openSession();
System.out.println(sqlSession);
//org.apache.ibatis.session.defaults.DefaultSqlSession@20398b7c

//5、获取接口的代理实现类
AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

//调用接口的 代理类,执行方法
Anime anime = animeMapper.selectAnimeById(101);
System.out.println(anime);
//Anime(id=101, cid=1, name=斗破苍穹, author=土豆, actor=萧炎, produce=玄机科技, createDate=Sun Aug 07 00:00:00 CST 2022)

3.2 源码分析执行过程分析

3.2.1 Resources 加载全局配置文件

//加载全局配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

3.2.2 SqlSessionFactoryBuilder 构建器

//实例化 SqlSessionFactoryBuilder 构建器
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

3.2.3 sqlSessionFactoryBuilder.build(inputStream);

解析 配置信息文件流,并返回 defaultSessionFactory;

//解析 配置信息文件流,并返回  defaultSessionFactory
SqlSessionFactory defaultSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

手动实现,体验实例化Configuration;

//手动解析配置文件流,实例化Configuration,体验Mybatsi自动实例化Configuration;
//实例化 XMLConfigBuilder  参数:配置文件流,环境名字,Properties实例
//XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream, "development_dev", new Properties());

//解析配置文件流
//Configuration configuration = xmlConfigBuilder.parse();

`SqlSessionFactoryBuilder源码分析:

sqlSessionFactoryBuilder.build(inputStream)方法,首先进入sqlSessionFactoryBuilder类

调用当前类的SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)方法;

其中,又调用当前类的一个build方法解析配置文件,并实例化DefaultSqlSessionFactory;

3.2.4 defaultSessionFactory.openSession();

//实例化 SqlSession
SqlSession sqlSession = defaultSessionFactory.openSession();
//org.apache.ibatis.session.defaults.DefaultSqlSession@20398b7c

DefaultSqlSessionFactory源码分析:

openSession方法调用openSessionFromConnection;

openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) 方法:方法:

3.2.5 sqlSession.getMapper(xxx.class);

//获取到接口的代理实现类
AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
//org.apache.ibatis.binding.MapperProxy@5abca1e0

//也可以通过,解析配置文件流 创建配置对象,并通过配置对象直接getMapper,前提是获得了sqlSession
//Configuration configuration = xmlConfigBuilder.parse();
//AnimeMapper animeMapper = configuration.getMapper(AnimeMapper.class, sqlSession);

3.2.6 animeMapper.selectAnimeById(101);

//调用接口代理类,执行方法
Anime anime = animeMapper.selectAnimeById(101);
//Anime(id=101, cid=1, name=斗破苍穹, author=土豆, actor=萧炎, produce=玄机科技, createDate=Sun Aug 07 00:00:00 CST 2022)

3.2.7 总结

------------------------------------------------------------------------------------------------------

1、insert 标签

1.1 获取SqlSessionFactory 对象的通用方法

方便后面分测试;

//获取SqlSessionFactory 对象的通用方法
public SqlSessionFactory getSqlSessionFactory() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    return new SqlSessionFactoryBuilder().build(inputStream);
}

1.2 属性说明

属性名说明
id namespace指定接口中的方法名
parameterType 指定接口方法入参类型,可写可不写(mybatis可用根据接口方法,自动推断类型)
useGeneratedKey insert标签的属性,告诉mybatis,执行插入操作,需要返回自增的主键
keyColumn 自增主键的 字段名(可以不写,一张表只用能有一个自增主键)
keyPropert 指定返回的自增主键值,交给入参实体的哪个属性保存

注意:增删改操作,和select查询标签最大的区别是:返回只有影响行数,所有没有resultType属性,而查询必须有resultType;

1.3 SQL

<!--   int insertAnime(Anime animeForm);  -->
<insert id="insertAnime" useGeneratedKeys="true" keyColumn="id" keyProperty="id" >
    insert into `animes`(
        `cid`,
        `name`
    )values(
        #{cid},
        #{name}
    )
</insert>

1.4 测试

注意:mybatis 默认对增删改操作,事务不自动提交(自动提交是关闭的);

需要开启自动提交或这是手动提交

开启自动提交openSession(true);
手动提交 sqlSession.commit();
@Test
public void testMybatisMapperC() throws IOException {
    //获取SqlSession对象
    // SqlSession sqlSession = getSqlSessionFactory().openSession();
    //方式2:创建SqlSession对象时,指定事务自动提交-true,默认false
    SqlSession sqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现对象
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    System.out.println(animeMapper); //org.apache.ibatis.binding.MapperProxy@224edc67

    //模拟从前端获取参数,封装请求实体
    Anime animeForm = new Anime();
    animeForm.setCid(1);
    animeForm.setName("蜡笔小新");

    //执行添加动漫
    int row = animeMapper.insertAnime(animeForm);

    //mybatis 默认对增删改操作,事务是不自动提交(自动提交是关闭的)
    //方式1:手动提交
    //sqlSession.commit();

    System.out.println(String.format("----------执行添加动漫,影响行数:%d--------", row));
    
    //获取自增主键
    System.out.println(String.format("----------执行添加动漫,新增的自增id:%d--------", animeForm.getId()));
    
}

2、update 标签

2.1 SQL

<!--  int updateAnimeById(Anime animeForm);  -->
<update id="updateAnimeById">
    update `animes` set
        `cid` = #{cid},
        `name` = #{name}
    where `id` = #{id}
</update>

2.2 测试

@Test
public void testMybatisMapperU() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession(true);
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    //模拟从前端获取参数,封装请求实体
    Anime animeForm = new Anime();
    animeForm.setId(648);
    animeForm.setCid(1);
    animeForm.setName("蜡笔小新5");
    //执行修改动漫
    int row = animeMapper.updateAnimeById(animeForm);

    System.out.println(String.format("----------执行修改动漫,影响行数:%d--------", row));

}

3、delete 标签

3.1 SQL

<!--   int deleteAnimeById(Integer animeId);  -->
<delete id="deleteAnimeById">
    delete from `animes`
    where `id` =  #{animeId}
</delete>

3.2 测试

@Test
public void testMybatisMapperD() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession(true);
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    //模拟从前端获取参数,封装请求实体
    Anime animeForm = new Anime();
    animeForm.setId(648);
    //执行删除动漫
    int row = animeMapper.deleteAnimeById(animeForm.getId());

    System.out.println(String.format("----------执行删除动漫,影响行数:%d--------", row));

}

4、select 标签

4.1 属性说明

属性名说明
id 对应就是namespace指定接口中的查询方法名
parameterType 指定接口方法入参类型可写可不写(建议不写)
resultType 指定接口返回的目标类型(建议使用全类名,也可以使用别名)
  • #{id}:这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中;
  • 如果接口只有一个参数参数名可以随便写,建议跟形参名保持一致

4.2 一个参数

4.2.1 SQL

接口只有一个参数,参数名,可以随便写,建议跟形参名保持一致;

<!--  Anime selectAnimeById(Integer animeId);  -->
<select id="selectAnimeById" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from `animes`
    where `id` = #{animeId}
</select>

4.2.2 测试

@Test
public void testMybatisMapperSelectParams1() throws IOException{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    //调用mapper接口,根据动漫编号查询动漫信息
    Anime anime = animeMapper.selectAnimeById(101);

    System.out.println(String.format("1.一个参数 根据动漫编号:%d,查询动漫详情%s",anime.getId(),anime));

}

4.3 两个参数

多个参数,没有自定义参数别名时可以使用

  • 形参名
  • 内置参数 0,1,...
  • 内置参数 param1,param2,...

自定义参数别名@Param("自定义参数名")时可以使用:

  • 自定义参数名
  • 内置参数 param1,param2,...

4.3.1 SQL

4.3.1.1 没有自定义参数别名:
Anime selectAnimeByNameAndCid(String animeName,Integer animeId); 
<!-- Anime selectAnimeByNameAndCid(String animeName,Integer animeId);   -->
<select id="selectAnimeByNameAndCid" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
     from `animes`

    where `name` = #{animeName}
    and `cid` = #{animeId}

<!--
    where `name` = #{0}
    and `cid` = #{1}
-->
<!--
    where `name` = #{ param1}
    and `cid` = #{param2}
-->
</select>
4.3.1.2 自定义参数别名
Anime selectAnimeByNameAndCid(@Param("animeName") String animeName,@Param("animeId") Integer animeId);
Anime selectAnimeByNameAndCid(@Param("animeName") String animeName,@Param("animeId") Integer animeId);
<select id="selectAnimeByNameAndCid" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
     from `animes`
    where `name` = #{animeName}
    and `cid` = #{animeId}
<!--
    where `name` = #{ param1}
    and `cid` = #{param2}
--> 
</select>

自定义了参数名,如果使用#{0},#{1}会报错

Cause: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [animeId, animeName, param1, param2]

4.3.2 测试

@Test
public void testMybatisMapperSelectParams2() throws IOException{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    //通过动漫名称和分类编号查询动漫信息
    Anime anime = animeMapper.selectAnimeByNameAndCid("完美世界",3);

    System.out.println(String.format("2.两个参数 根据动漫编号:%d,查询动漫详情%s",anime.getId(),anime));

}

4.4 实体参数

不自定义参数别名:

  • 必须 只写 属性名 cid author

自定义参数别名:

  • 使用 别名.属性名
  • 使用 param1.属性名

4.4.1 SQL

4.4.1.1 不自定义参数别名
Anime selectAnimeByAnime(Anime animeForm);
<select id="selectAnimeByAnime" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from `animes`
	where `cid` = #{cid}
    and `author` = #{author}
</select>
4.4.1.2 自定义参数别名
 Anime selectAnimeByAnime(@Param("animeForm") Anime animeForm);
<select id="selectAnimeByAnime" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from `animes`
<!--    
 	where `cid` = #{animeForm.cid}
     and `author` = #{animeForm.author}
 -->
     where `cid` = #{param1.cid}
     and `author` = #{param1.author}
</select>

4.4.2 测试

@Test
public void testMybatisMapperSelectParams3() throws IOException{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    //通过动漫分类编号和作者查询动漫信息
    Anime animeForm = new Anime();
    animeForm.setCid(1);
    animeForm.setAuthor("三少");
    Anime anime = animeMapper.selectAnimeByAnime(animeForm);
    System.out.println(String.format("3.实体参数 根据动漫编号:%d,查询动漫详情%s",anime.getId(),anime));

}

4.5 Map集合参数

  • 通过Map 的 **键值 **获取参数

4.5.1 SQL

<!--
  Anime selectAnimeByActorAndCid(Map<String,Object> queryMap);
-->
<select id="selectAnimeByActorAndCid" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
     from `animes`
    where `cid` = #{cid}
    and `actor` = #{actor}
</select>

4.5.2 测试

 @Test
public void testMybatisMapperSelectParams4() throws IOException{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    //通过动漫分类编号和主角查询动漫信息
    Map<String,Object> queryMap = new HashMap<>();
    queryMap.put("cid","2");
    queryMap.put("actor","路飞");

    Anime anime = animeMapper.selectAnimeByActorAndCid(queryMap);

    System.out.println(String.format("4.集合参数 根据动漫编号:%d,查询动漫详情%s",anime.getId(),anime));

}

4.6 #{} 和 ${} 的区别

#{}:

  • 类似于PreparedStatement

  • 可以获取普通参数,自定义参数,实体参数,集合参数等;

  • 底层使用的是?占位符,会进行预编译处理,可以防止SQL注入问题,安全性高;

  • 不可以进行表达式运算

${}:

  • 类似于Statement

  • 正常情况下,跟#{}获取参数的写法没有区别;

  • 区别:不能随意获取参数不能使用内置参数必须起别名;

  • 底层是字符串拼接,不是占位符,不安全,当#{}解决不了,就必须使用${};

  • 可以使用,动态表名,动态列名,表达式运算等

建议:MyBatis的SQL映射文件中,能优先使用#{},就必须使用,除非特殊情况,必须使用字符串拼接,才可以使用${};

4.6.1 SQL

使用动态表名查询动漫;

<!--   Anime selectProduceAndCid(@Param("produce") String produce,@Param("cid")Integer cid,@Param("tableName")String tableName);  -->
    <select id="selectProduceAndCid" resultType="com.kgc.mybatis.bean.Anime">
        select `id`,
                `cid`,
                `name`
        from    ${tableName}
        where  `produce` = #{produce}
        and `cid` = #{cid}
</select>

4.6.2 测试

@Test
public void testMybatisMapperSelectParams5() throws IOException{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    Anime anime = animeMapper.selectProduceAndCid("腾讯动漫", 2, "animes");

    System.out.println(String.format("4.集合参数 根据动漫编号:%d,查询动漫详情%s",anime.getId(),anime));

}

4.7 查询多条数据

查询多条数据, resultType 属性值还是实体类

resultType="com.kgc.mybatis.bean.Anime"

Mybatis会自己走 返回集合的方法自动将数据放到集合中

 //var1 select标签的id属性的值
<E> List<E> selectList(String var1);

//var2 为接口方法的参数
<E> List<E> selectList(String var1, Object var2);

//var3 为分页对象
<E> List<E> selectList(String var1, Object var2, RowBounds var3);

4.7.1 SQL

<!--   List<Anime> selectAnimeListByCid(Integer Cid);  -->
<select id="selectAnimeListByCid" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from   `animes`
    where  `cid` = #{cid}
</select>

4.7.2 测试

@Test
public void testMyBatisMapperSelectResults() throws  IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
	//根据动漫分类编号查询动漫
    List<Anime> animeList = animeMapper.selectAnimeListByCid(3);

    for (Anime anime : animeList) {
        System.out.println(anime);
    }

}

4.8 模糊查询

使用 #{} 建议使用 concat('%',#{name},'%');

使用 ${name} 必须给参数起别名

4.8.1 SQL

4.8.1.1 使用 #{name}
<!--  List<Anime> selectAnimeListByName(String name);   -->
<select id="selectAnimeListByName" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from   `animes`
    <!-- where  name like '%'#{name}'%'-->
    where  name like concat('%',#{name},'%')
</select>
4.8.1.2 使用${name}
<!-- List<Anime> selectAnimeListByName(@Param("name") String name);  -->
<select id="selectAnimeListByName" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from   `animes`
    where  name like '%${name}%'
</select>

使用${name} 如果不取别名, 取不到参数;

There is no getter for property named 'name' in 'class java.lang.String'

4.8.2 测试

 @Test
public void testMyBatisMapperSelectResults2() throws  IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    //根据动漫名称 模糊 查询动漫列表
    List<Anime> animeList = animeMapper.selectAnimeListByName("魁");
    for (Anime anime : animeList) {
        System.out.println(anime);
    }
}

4.9 分页查询

  • 方法一:调用接口的时候,计算好分页起始行,SQL中直接获取参数((pageNo - 1)*pageSize),实现分页;
  • 方法二:使用 limit ${(pageNo - 1)*pageSize},#{pageSize} (不建议使用)
  • 方法三:使用select的子标签bind自定义属性,<bind name="pageIndex" value="((pageNo - 1)*pageSize)"/>

4.9.1 SQL

<select id="selectAnimeListByProduce" resultType="com.kgc.mybatis.bean.Anime">
    <bind name="pageIndex" value="((pageNo - 1)*pageSize)"/>
    select `id`,
    `cid`
    from   `animes`
    where  `produce` like concat('',#{produce},'%')
    <!-- 方式一:$符进行计算 -->
    <!-- limit ${(pageNo - 1)*pageSize},#{pageSize} -->
    <!-- 方式2:bind,自定义参数 -->
    limit #{pageIndex},#{pageSize}
</select>

4.9.2 测试

@Test
public void testSelectAnimeListByProduce() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    //模拟获取分页参数
    Integer pageNo = 1;
    Integer pageSize = 3;

    //调用mapper接口,模糊查询,查询分页列表

    //方法一:调用接口的时候,计算好分页起始行,SQL中直接获取参数,实现分页
    //方法二:使用 limit ${(pageNo - 1)*pageSize},#{pageSize}
    //方法三:使用select的子标签bind,<bind name="pageIndex" value="((pageNo - 1)*pageSize)"/>

    List<Anime> animeList = animeMapper.selectAnimeListByProduce("爱奇艺",pageNo,pageSize);

    animeList.forEach(System.out::println);
}

4.10 返回Map集合

4.10.1 列名作为key,数值作为value

  • 以动漫详情为例模拟返回map集合,将列名作为key,数值作为value;
  • 实际开发中,查询结果集,是单条记录,且没有实体对应,比如:数据统计,只有统计结果,没有统计实体;
5.10.1.1 SQL

Map<String,Object>,方法的返回类型是Map,key是String类型,value是Object类型,因为每个字段有不同的类型

resultType="java.util.HashMap",因为将数据映射到map中;

<!--   Map<String,Object> selectAnimeMapById(Integer id);  -->
<select id="selectAnimeMapById" resultType="java.util.HashMap">
    select  `id`,
    `cid`,
    `name`
    from   `animes`
    where  `id` = #{id}
</select>
4.10.1.1 测试
@Test
public  void testSelectAnimeByMapById() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    Map<String, Object> animeMap = animeMapper.selectAnimeMapById(301);

    System.out.println(animeMap);

    Object actor = animeMap.get("actor");
    System.out.println("actor==>"+actor);
}

4.10.2主键key,实体为value

  • 以动漫详情为例模拟返回map集合,将数据主键列值作为key,整条数据作为value
  • 实际开发中返回多条记录,并需要根据key,快速遍历,比如分组查询;
4.10.2.1 SQL

因为它不知道你要将哪个属性作为map的key值,所以需要@MapKey("id")指定一个实体的属性作为map的key值;

//以动漫详情为例模拟返回map集合,将数据主键列值作为key,整条数据作为value
@MapKey("id")   //这里的MapKey 是实体的一个属性
Map<Integer,Anime> selectAnimeMapByCid(Integer cid);

resultType="com.kgc.mybatis.bean.Anime",虽然返回的结果是map,但是数据是映射到Anime动漫实体中;

<!--   @MapKey("id")   //这里的MapKey 是实体的一个属性  -->
<!--   Map<Integer,Anime> selectAnimeMapByCid(Integer cid);  -->
<select id="selectAnimeMapByCid" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`
    from   `animes`
    where  `cid` = #{cid}
</select>
4.10.2.2 测试
@Test
public void  testSelectAnimeMapByCid() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    Map<Integer, Anime> animeMap = animeMapper.selectAnimeMapByCid(3);

    System.out.println(animeMap);

    Anime anime = animeMap.get(301);
    System.out.println(anime);
}

5、ResultMap 结果集映射

5.1 列名 和 属性名 不匹配

  • 可以取别名,查询出来的列名,取别名,跟实体的属性名一致
  • 自定义resultMap映射

5.1.1 SQL

resultMap的参数说明
id resultMap唯一id
type 返回值类型
autoMapping 是否开启自动映射

resultMap自定义标签内指定的列才会映射,如果查询的结果列,不在自定义映射标签中,但是满足自动映射的条件(列名和实体属性名一致), 仍然会自动映射;

除非指定resultMap标签的autoMapping属性为falseautoMapping="false"),没有自定义映射的其他字段才不会自动映射

<!--  Anime selectAnimeByResultMap(Integer id);  -->
<select id="selectAnimeByResultMap" resultMap="animeResultMap">
    select `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date` `crateTime`
    from   `animes`
    where  `id` = #{id}
</select>
<!-- 
  autoMapping="false"  关闭自动映射,只使用自定义映射;
-->
<resultMap id="animeResultMap" type="com.kgc.mybatis.bean.Anime" autoMapping="false">
    <!--   主键映射标签  -->
    <id column="id" property="id"></id>
    <!--  普通列映射标签     -->
    <result column="cid" property="cid"></result>
    <result column="name" property="name"></result>
    <result column="crateTime" property="createDate"></result>
</resultMap>

5.1.2 测试

@Test
public void  testSelectAnimeByResultMap() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    //使用自定义映射,查询动漫详情,解决列名和属性名不同意的映射
    Anime anime = animeMapper.selectAnimeByResultMap(301);

    System.out.println(anime);
}

5.2 级联映射

实体属性,级联映射;

5.2.1 一对一

级联映射只适合一对一;

要求:查询动漫Anime,并且查询出动漫的 实体属性category 的信息;

一个动漫 对 一个动漫分类

5.2.1.1 实体

动漫实体Anime

public class Anime {
    //动漫编号
    private Integer id;
    //分类编号
    private  Integer cid;
    //名称
    private  String name;
	......
    //分类详情实体,一对一  (也可以定义分类名称冗余到实体中也可以解决)
    private Category category;
}

分类实体Category

public class Category {
    //分类编号
    private  Integer id;
    //分类名称
    private  String name;
}
5.2.1.2 SQL
<result column="cid" property="category.id"></result>
参数说明
column="cid" 连表查询出来的字段
property="category.id" 实体属性 的属性

先通过连表查询,将动漫信息和分类信息查询出来,再根据字段一 一 映射

<resultMap id="animeResultMapCascade" type="com.kgc.mybatis.bean.Anime" >
    <!--   主键映射标签  -->
    <id column="id" property="id"></id>
    <!--  普通列映射标签     -->
    <result column="cid" property="cid"></result>
    <result column="name" property="name"></result>
    <result column="author" property="author"></result>
    <result column="create_date" property="createDate"></result>
    <!-- 级联映射,通过 内部实体属性名.属性 -->
    <result column="cid" property="category.id"></result>
    <result column="cname" property="category.name"></result>
</resultMap>
5.2.1.3 测试
@Test
public void  testSelectAnimeByResultMapCascade() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    //级联映射,动漫实体内部,分类实体属性的 级联映射
    Anime anime = animeMapper.selectAnimeByResultMapCascade(301);

    System.out.println(anime);
    //Anime(id=301,
    //		cid=3, 
    //		name=完美世界,
    //        ......
    //		category=Category(id=3, name=科幻)
    //      )
}

5.3 关联映射 (高级映射)

实体属性,关联映射;

5.3.1 association 一对一

要求:查询动漫Anime,并且查询出动漫的 实体属性category 的信息 ;

一个动漫 对 一个动漫分类

5.3.1 实体

动漫实体Anime

public class Anime {
    //动漫编号
    private Integer id;
    //分类编号
    private  Integer cid;
    //名称
    private  String name;
	......
    //分类详情实体,一对一  
    private Category category;
}

分类实体Category

public class Category {
    //分类编号
    private  Integer id;
    //分类名称
    private  String name;
}
5.3.2 SQL

先通过连表查询,查询出动漫信息,和动漫分类信息;

再通过association标签,对动漫的 实体属性 category 进行赋值

<!--  Anime selectAnimeByResultMapAssociation(Integer id);   -->
<select id="selectAnimeByResultMapAssociation" resultMap="animeResultMapAssociation">
    select a.`id`,
    a.`cid`,
    a.`name`,
    a.`author`,
    a.`actor`,
    a.`produce`,
    a.`create_date`,
    c.`name` 'cname'
    from   `animes` a,`category` c
    where  a.`cid` = c.`id`
    and a.`id` = #{id}
</select>

<resultMap id="animeResultMapAssociation" type="com.kgc.mybatis.bean.Anime" >
    <!--   主键映射标签  -->
    <id column="id" property="id"></id>
    <!--  普通列映射标签     -->
    <result column="cid" property="cid"></result>
    <result column="name" property="name"></result>
    <result column="author" property="author"></result>
    <result column="produce" property="produce"></result>
    <result column="create_date" property="createDate"></result>
    <!-- 关联映射,内部实体一对一 -->
    <association property="category" javaType="com.kgc.mybatis.bean.Category">
        <id column="cid" property="id"></id>
        <result column="cname" property="name"></result>
    </association>
</resultMap>
5.3.3 测试
@Test
public void  testSelectAnimeByResultMapAssociation() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);

    Anime anime = animeMapper.selectAnimeByResultMapAssociation(301);

    System.out.println(anime);
    //Anime(id=301,
    //		cid=3, 
    //		name=完美世界,
    //        ......
    //		category=Category(id=3, name=科幻)
    //      )
}

5.3.2 collection 一对多

要求:查询分类Category,并且查询出分类下的动漫集合属性 animes 信息

一个动漫分类 对 多个动漫

5.3.1 实体

分类实体Category

public class Category {
    //分类编号
    private  Integer id;
    //分类名称
    private  String name;
    //当前 分类下的 动漫集合
    private List<Anime> animes;
}

动漫实体Anime

public class Anime {
    //动漫编号
    private Integer id;
    //分类编号
    private  Integer cid;
    //名称
    private  String name;
	......
}
5.3.2 SQL

先通过连表查询,查询出动漫分类信息,和动漫信息;

再通过collection标签,对动漫分类的 集合属性 animes 进行赋值

<!--   Category selectCategoryByResultMapCollection(Integer id);    -->
<select id="selectCategoryByResultMapCollection" resultMap="categoryByResultMapCollection">
    select c.`id`,
    c.`name`,
    a.`id` 'aid',
    a.`cid`,
    a.`name` 'aname',
    a.`author`,
    a.`actor`,
    a.`create_date`,
    a.`produce`
    from   `category` c,`animes` a
    where  c.`id` = a.`cid`
    and c.`id` = #{id}
</select>

<resultMap id="categoryByResultMapCollection" type="com.kgc.mybatis.bean.Category">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <!-- 高级映射,使用集合 关联映射,解决内部集合映射,一对多 -->
    <collection property="animes" ofType="com.kgc.mybatis.bean.Anime">
        <id column="aid" property="id"></id>
        <result column="cid" property="cid"></result>
        <result column="aname" property="name"></result>
        <result column="author" property="author"></result>
        <result column="actor" property="actor"></result>
        <result column="produce" property="produce"></result>
        <result column="create_date" property="createDate"></result>
    </collection>
</resultMap>
5.3.3 测试
@Test
public void  testSelectCategoryByResultMapCollection() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    CategoryMapper categoryMapper = sqlSession.getMapper(CategoryMapper.class);

    //查询动漫分类详情,内部集合类型 映射
    Category category = categoryMapper.selectCategoryByResultMapCollection(3);

    System.out.println(category);
    //Category(id=3, 
    //		   name=科幻, 
    //		   animes=[Anime(id=301, cid=3, name=完美世界, ...), 
    //                 Anime(id=649, cid=3, name=蜡笔小新, ...)
    //                 ]
    //         )
}

5.4 嵌套select, 延迟加载 和 分步查询

  • 开启延迟加载,默认false,需要支持延迟,改为true;
    • <setting name="lazyLoadingEnabled" value="true"></setting>
  • 按需加载 ,是否全部加载,
    • (前提必须是言辞加载是打开的,而且是自定义映射,配合高级嵌套select查询);
    • 3.4.1及之前的版本中默认为true,全部加载(任意一方法的调用都会加载该对象的所有延迟加载属性);
    • 只有改为false,才会分步加载,需要调用该方法时,才进行 延迟加载属性;
    • <setting name="aggressiveLazyLoading" value="false"/>

5.4.1 association 一对一

要求:查询动漫Anime,并且查询出动漫的 实体属性category 的信息 ;

一个动漫 对 一个动漫分类

5.4.1.1 实体

跟 关联映射 association 一对一 的实体一样;

5.4.1.2 SQL
<association property="category" select="com.kgc.mybatis.mapper.CategoryMapper.selectCategoryById" column="cid" fetchType="eager">
</association>
参数说明
property 实体的属性
select 指定嵌套的select语句的唯一标识
column 指定嵌套的sleect语句执行需要的参数,多参数JSON格式{key1=col1,key2=col2}
fetchType 是否适配系统延迟加载,默认是lazy,如果需要局部关闭延迟加载,改为eager

先通过id查询动漫Anime,再通过动漫的cid,去查询 动漫分类

AnimeMapper.xml

<!--Anime selectAnimeByResultMapAssociationLazyLoadingStep(Integer id);-->
<select id="selectAnimeByResultMapAssociationLazyLoadingStep" resultMap="associationLazyLoadingStep">
    select `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date` `crateTime`
    from   `animes`
    where  `id` = #{id}
</select>
<!--  嵌套select语句实现 延迟加载 和 分布查询  -->
<resultMap id="associationLazyLoadingStep" type="com.kgc.mybatis.bean.Anime">
    <!--   主键映射标签  -->
    <id column="id" property="id"></id>
    <!--  普通列映射标签     -->
    <result column="cid" property="cid"></result>
    <result column="name" property="name"></result>
    <result column="author" property="author"></result>
    <result column="produce" property="produce"></result>
    <result column="crateTime" property="createDate"></result>
    <!-- 高级映射,内部实体一对一 ,嵌套select语句, 延迟加载和分布查询  -->
    <!-- fetchType="eager" 局部覆盖按需加载  -->
    <!--
     select属性,指定嵌套的select语句的唯一标识(myabtis框架可识别的)
     column属性:指定嵌套的sleect语句执行需要的参数,即将当前查询某列的值作为参数,传递到指定的查询语句中,如果有多个参数,可以使用JSON格式{key1=col1,key2=col2}
     fetchType属性:设置当前自定高级映射是否适配系统延迟加载,默认是lazy,如果需要局部关闭延迟加载,改为eager
     -->
    <association property="category" select="com.kgc.mybatis.mapper.CategoryMapper.selectCategoryById" column="cid" fetchType="eager">
    </association>
</resultMap>

CategoryMapper.xml

<!--   Category selectCategoryById(Integer id);  -->
<select id="selectCategoryById" resultType="com.kgc.mybatis.bean.Category">
    select `id`,
    `name`
    from `category`
    where `id`= #{id}
</select>
5.4.1.3 测试
 @Test
public void  testSelectAnimeByResultMapAssociationLazyLoadingStep() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    AnimeMapper animeMapper = sqlSession.getMapper(AnimeMapper.class);
    
    //高级映射,内部实体一对一 ,使用嵌套select 延迟加载和分布查询
    Anime anime = animeMapper.selectAnimeByResultMapAssociationLazyLoadingStep(301);

    System.out.println("如果只使用动漫的信息,不使用加载 动漫分类的SQL");
    System.out.println(anime.getName());

    System.out.println("=============================================");
    System.out.println("动漫的分类名称:"+anime.getCategory().getName());
}

开启延迟加载和按需加载

关闭延迟加载和按需加载,或者局部关闭延迟加载

5.4.2 collection 一对多

要求:查询分类Category,并且查询出分类下的动漫集合属性 animes 信息

一个动漫分类 对 多个动漫

5.4.2.1 实体

跟 关联映射 collection 一对多 的实体一样;

5.4.2.2 SQL

先通过id查询分类Category,再通过动漫的id,去查询cid等于id的动漫

CategoryMapper.xml

<!--Category selectCategoryByResultMapCollectionAssociationLazyLoadingStep(Integer id); -->
<select id="selectCategoryByResultMapCollectionAssociationLazyLoadingStep" resultMap="associationLazyLoadingStep" >
    select `id`,
    `name`
    from `category`
    where `id`= #{id}
</select>

<resultMap id="associationLazyLoadingStep" type="com.kgc.mybatis.bean.Category">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <!-- 高级映射,使用集合映射,解决内部 集合映射,一对多 -->
    <collection property="animes" select="com.kgc.mybatis.mapper.AnimeMapper.selectAnimeListByCid" column="{cid=id}" fetchType="lazy">
    </collection>
</resultMap>

AnimeMapper.xml

<!--   Map<Integer,Anime> selectAnimeMapByCid(Integer cid);  -->
<select id="selectAnimeMapByCid" resultType="com.kgc.mybatis.bean.Anime">
    select `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date`
    from   `animes`
    where  `cid` = #{cid}
</select>
5.4.2.3 测试
@Test
public void testSelectCategoryByResultMapCollectionAssociationLazyLoadingStep() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    CategoryMapper categoryMapper = sqlSession.getMapper(CategoryMapper.class);

    Category category = categoryMapper.selectCategoryByResultMapCollectionAssociationLazyLoadingStep(1);

    System.out.println("分类名称:"+category.getName());

    System.out.println("=============================================");
    System.out.println("该分类下的动漫:"+category.getAnimes());
}

开启延迟加载和按需加载

关闭延迟加载和按需加载,或者局部关闭延迟加载

------------------------------------------------------------------------------------------------------
 

1、if

<if>动态标签:判断参数时满足test指定的条件,如果满足,就执行if(增加if标签中的SQL语句);

注意:test里面使用的参数,可以是mybatis的默认参数,也可以是实体属性名,但是不能是没有指定别名的参数名(尤其是单个参数,也必须起别名,否则异常);

1.1 SQL

单独使用if,如果不满足条件会SQL拼接出问题,一般我门都跟where一起使用;

<!-- List<Anime> selectAnimesByConditionUserIf(@Param("cid") Integer cid,@Param("author") String author);  -->
<select id="selectAnimesByConditionUserIf" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date`
    from `animes`
    where `create_date` &lt; now()
    <if test="cid != null and cid != 0 ">
        cid = #{cid}
    </if>
    <if test="author != null">
        and author like concat('%',#{author},'%')
    </if>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` where `create_date` < now() and author like concat('%',?,'%')

1.2 测试

@Test
public void testMybatisMapperDynamicSQlUserIf() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    //执行动态SQL,查询动漫列表
    List<Anime> animeList = animeMapper.selectAnimesByConditionUserIf(0, "土豆");

    animeList.forEach(System.out::println);
}

2、where + if

  • <where><if> 动态标签组合:当where标签中,有if条件成立时自动增加where关键字,如果所有的if都不成立,也不会多增加where关键字

  • 当where标签中,if成立,增加的SQL语句,前面多出现一个and或者 or关键字,会被自动过滤(剔除),但是末尾出现的,不会被剔除

  • where标签中,也可以增加固定条件,再实际开发过程中,建议where标签中,必须写固定条件,不能全部写if判断;

2.1 SQL

<!--   List<Anime> selectAnimesByConditionUserIfWhere(@Param("cid") Integer cid,@Param("author") String author); -->
<select id="selectAnimesByConditionUserIfWhere" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date`
    from `animes`
    <where>
        <if test="cid != null and cid != 0 ">
            and cid = #{cid}
        </if>
        <if test="author != null">
            and author like concat('%',#{author},'%')
        </if>
    </where>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE author like concat('%',?,'%')

2.2 测试

@Test
public void testMybatisMapperDynamicSQlUserIfWhere() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    //执行动态SQL,查询动漫列表
    List<Anime> animeList = animeMapper.selectAnimesByConditionUserIfWhere(0, "土豆");

    animeList.forEach(System.out::println);
    
}

3、trim + if

  • <trim> +<if> :可以实现动态SQL的定制操作,比如:where标签无法屏蔽末尾多出来的and或者or关键字,前缀 和后缀增加的内容,只有标签中的if标签成立,(需要增加条件,才拼接where);
  • prefix:增加前缀固定字符串;
  • prefixOverrides:前缀覆盖(自动剔除指定的关键字);
  • suffix:增加后缀固定字符串;
  • suffixOverrides:后缀覆盖(自动剔除指定的关键字);

3.1 SQL

  • "and |or" 中间一般都会添加一个空格;
<!--  List<Anime> selectAnimesByConditionUserIfTrim(@Param("cid") Integer cid,@Param("author") String author); -->
<select id="selectAnimesByConditionUserIfTrim" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date`
    from `animes`
    <trim prefix=" where "  prefixOverrides="and |or" suffixOverrides="and |or" suffix=";">
        <if test="cid != null and cid != 0 ">
            cid = #{cid} and
        </if>
        <if test="author != null">
            author like concat('%',#{author},'%') and
        </if>
    </trim>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` where author like concat('%',?,'%') ;

3.2 测试

@Test
public void testMybatisMapperDynamicSQlUserIfTerm() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    //执行动态SQL,查询动漫列表
    List<Anime> animeList = animeMapper.selectAnimesByConditionUserIfTrim(0, "土豆");

    animeList.forEach(System.out::println);

}

4、set + if update

4.1SQL

<!--  int updateAnimeByConditionUserIfSet(Anime animeFOrm); -->
<update id="updateAnimeByConditionUserIfSet">
    update `animes`
    <set>
        <if test="cid != null"> `cid` = #{cid},</if>
        <if test="name != null"> `name` = #{name},</if>
        <if test="author != null"> `author` = #{author},</if>
        <if test="actor != null"> `actor` = #{actor},</if>
        <if test="produce != null"> `produce` = #{produce},</if>
        <if test="createDate != null"> `create_date` = #{createDate},</if>
    </set>
    where `id` = #{id}
</update>

执行SQL:

Preparing: update `animes` SET `name` = ?, `author` = ? where `id` = ?

4.2 测试

@Test
public  void testMybatisMapperDynamicSQlIfSetUpd() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    //模拟前端提供更新参数,实现动态更新,给说明值,就更新什么指端
    Anime animeForm = new Anime();
    animeForm.setId(637);
    animeForm.setName("武动乾坤KGC");
    animeForm.setAuthor("土豆KGC");

    int row = animeMapper.updateAnimeByConditionUserIfSet(animeForm);

    System.out.println(row);

}

5、trim + if update

5.1 SQL

<!--   int updateAnimeByConditionUserIfTrim(Anime animeFOrm); -->
<update id="updateAnimeByConditionUserIfTrim">
    <trim prefix="update `animes` set " prefixOverrides="," suffixOverrides=",">
        <if test="cid != null"> `cid` = #{cid},</if>
        <if test="name != null"> `name` = #{name},</if>
        <if test="author != null"> `author` = #{author},</if>
        <if test="actor != null"> `actor` = #{actor},</if>
        <if test="produce != null"> `produce` = #{produce},</if>
        <if test="createDate != null"> `create_date` = #{createDate},</if>
    </trim>
    where `id` = #{id}
</update>

执行SQL:

Preparing: update `animes` set `name` = ?, `author` = ? where `id` = ?

5.2 测试

@Test
public  void testMybatisMapperDynamicSQlIfTrimUpd() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    //模拟前端提供更新参数,实现动态更新,给说明值,就更新什么指端
    Anime animeForm = new Anime();
    animeForm.setId(637);
    animeForm.setName("武动乾坤22KGC");
    animeForm.setAuthor("土豆22KGC");

    int row = animeMapper.updateAnimeByConditionUserIfTrim(animeForm);

    System.out.println(row);

}

6、where + choose + when (判断条件测试)

这个场景主要在传过来的参数与放进SQL中的参数不一致的时候使用;

比如,前端传过来男/女,但是数据库中查询的时候需要使用1/2;(当然参数也可以在前端或者业务层处理好再放进SQL)

6.1 单引号与双引号的区别

6.1.1 test='cid != null and cid == "1"'

test整体用单引号,里面的判断条件双引号

<!--List<Anime> selectAnimesByConditionUserChooseWhenOtherwise(@Param("cid") String cid); -->
<select id="selectAnimesByConditionUserChooseWhenOtherwise" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
    `cid`,
    `name`,
    `author`,
    `actor`,
    `produce`,
    `create_date`
    from `animes`
    <where>
        <choose>
            <!-- test整体使用单引号,判断条件使用双引号  -->
            <when test='cid != null  and cid == "1"'>
                and cid = 1
            </when>
            <when test='cid != null  and cid == "2"'>
                and cid = 2
            </when>
            <otherwise>
                and cid = 3
            </otherwise>
        </choose>
    </where>
</select>

6.1.1 测试

List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.1.2 test="cid != null and cid == '1'"

test整体用双引号,里面的判断条件单引号

...
<choose>
    <!-- test整体使用双引号,判断条件使用单引号  -->
    <when test="cid != null  and cid == '1'">
        and cid = 1
    </when>
    <when test="cid != null  and cid == '2'">
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...

6.1.2 测试

List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL没有报错,但是 cid == 2 的条件没有成立,而是走了默认参数 cid = 3
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 3

-- 可以查到数据,但是数据不正确,是cid=3的数据
Anime(id=301, cid=3, name=完美世界, author=辰东, actor=石昊, produce=玄机科技, createDate=Tue Apr 05 00:00:00 CST 2022)

6.1.3 "cid != null and cid eq '1'.toString()"

test整体用双引号,里面的判断条件单引号并且判断条件加了toString();

...
<choose>
    <!-- test整体使用双引号,判断条件使用单引号  -->
    <when test="cid != null  and cid == '1'.toString()">
        and cid = 1
    </when>
    <when test="cid != null  and cid == '2'.toString()">
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...

6.1.3 测试

List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.1.4 单引号和双引号总结

总结:

  • test整体用单引号,里面的判断条件双引号
  • 如果要使用test整体用双引号,里面的判断条件单引号,一定要加toString();

6.2 == 和 eq 的区别

6.2.1 ==

...
<choose>
    <when test='cid != null  and cid == "1"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid == "2"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...

6.2.1 测试

List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.2.2 eq

...
<choose>
    <when test='cid != null  and cid eq "1"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "2"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...

6.2.2 测试

List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.3 "str" 和 "str".toString() 的区别

6.3.1 "2" 和 "2"toString()

"2"
...
<choose>
    <when test='cid != null  and cid eq "1"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "2"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)
"2".toString()
...
<choose>
     <when test='cid != null  and cid eq "1".toString()'>
         and cid = 1
     </when>
     <when test='cid != null  and cid eq "2".toString()'>
         and cid = 2
     </when>
     <otherwise>
         and cid = 3
     </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.3.2 "B" 和 "B".toString() 的区别

"B"
...
<choose>
    <when test='cid != null  and cid eq "A"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "B"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("B");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)
"B".toString()
...
<choose>
    <when test='cid != null  and cid eq "A".toString()'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "B".toString()'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("B");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.3.3 "22" 和 "22".toString()

"22"
<choose>
    <when test='cid != null  and cid eq "11"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "22"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("22");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)
"22".toString()
...
<choose>
    <when test='cid != null  and cid eq "11"toString()'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "22"toString()'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("22");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.3.4 "BB" 和 "BB".toString()

"BB"
...
<choose>
    <when test='cid != null  and cid eq "AA"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "BB"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("BB");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)
"BB".toString()
...
<choose>
    <when test='cid != null  and cid eq "AA".toString()'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "BB".toString()'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("BB");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.3.5 "任意字符2" 和 "任意字符2".toString()

"任意字符2"
...
<choose>
    <when test='cid != null  and cid eq "任意字符1"'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "任意字符2"'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("任意字符2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)
"任意字符2".toString()
...
<choose>
    <when test='cid != null  and cid eq "任意字符1".toString()'>
        and cid = 1
    </when>
    <when test='cid != null  and cid eq "任意字符2".toString()'>
        and cid = 2
    </when>
    <otherwise>
        and cid = 3
    </otherwise>
</choose>
...
测试
List<Anime> animeList = animeMapper.selectAnimesByConditionUserChooseWhenOtherwise("任意字符2");

执行SQL:

-- SQL正常
Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE cid = 2

-- 可以查到正确数据
Anime(id=201, cid=2, name=海贼王, author=尾田, actor=路飞, produce=腾讯动漫, createDate=Tue Apr 05 00:00:00 CST 2022)
Anime(id=627, cid=2, name=魁拔3, author=青春树, actor=蛮吉, produce=青春树, createDate=Tue Jan 07 00:00:00 CST 2003)

6.4 总结

只需要将test整体用单引号,里面的判断条件双引号,就可以,加不加.toString(),并不影响;

7、foreach

根据id集合查询动漫集合;

7.1 SQL

7.1.1 起别名 where + foreach (in)

使用 in;

<!--List<Anime> selectAnimesByConditionUserForeach(@Param("ids") List<Integer> ids);-->
<select id="selectAnimesByConditionUserForeach" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
            `cid`,
            `name`,
            `author`,
            `actor`,
            `produce`,
            `create_date`
    from `animes`
	<where>
        <foreach collection="ids" item="id" open="id in(" close=" )" separator=", ">
            #{id}
        </foreach>
	</where>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE id in( ? , ? , ? )

7.1.2 不起别名 where + foreach (in)

使用 in;

<!--   List<Anime> selectAnimesByConditionUserForeach( List<Integer> ids);  -->
<select id="selectAnimesByConditionUserForeach" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
            `cid`,
            `name`,
            `author`,
            `actor`,
            `produce`,
            `create_date`
    from `animes`
    <where>
        <foreach collection="list" item="id" open="id in(" close=" )" separator=", ">
            #{id}
        </foreach>
    </where>   
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` WHERE id in( ? , ? , ? )

7.1.3 起别名 foreach (in)

不用where标签;

使用 in;

<!--List<Anime> selectAnimesByConditionUserForeach(@Param("ids") List<Integer> ids);-->
<select id="selectAnimesByConditionUserForeach" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
            `cid`,
            `name`,
            `author`,
            `actor`,
            `produce`,
            `create_date`
    from `animes`
    <foreach collection="ids" item="id" open=" where id in(" close=" )" separator=", ">
        #{id}
    </foreach>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` where id in( ? , ? , ? )

7.1.4 起别名 trim + foreach (in)

不用where标签;

使用 in;

通过7.1.3和7.1.4 可以总结,trim 和 foreach 都有前缀,后缀和分隔符,可以根据情况进项选择使用;

<!--List<Anime> selectAnimesByConditionUserForeach(@Param("ids") List<Integer> ids);-->
<select id="selectAnimesByConditionUserForeach" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
            `cid`,
            `name`,
            `author`,
            `actor`,
            `produce`,
            `create_date`
    from `animes`
	<trim prefix=" where id in ">
        <foreach collection="ids" item="id" open=" (" close=" )" separator=", ">
            #{id}
        </foreach>
     </trim>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` where id in ( ? , ? , ? )

7.1.5 起别名 foreach (or)

不用where标签;

使用 or;

<!--List<Anime> selectAnimesByConditionUserForeach(@Param("ids") List<Integer> ids);-->
<select id="selectAnimesByConditionUserForeach" resultType="com.kgc.mybatis.bean.Anime">
    select  `id`,
            `cid`,
            `name`,
            `author`,
            `actor`,
            `produce`,
            `create_date`
    from `animes`
    <foreach collection="ids" item="id" open=" where " close=" " separator=" or ">
        id = #{id}
    </foreach>
</select>

执行SQL:

Preparing: select `id`, `cid`, `name`, `author`, `actor`, `produce`, `create_date` from `animes` where id = ? or id = ? or id = ?

7.2 测试

@Test
public void testMybatisMapperDynamicSQlUserForeach() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    //执行动态SQL,查询动漫列表
    List<Anime> animeList = animeMapper.selectAnimesByConditionUserForeach(Arrays.asList(101,102,103));

    animeList.forEach(System.out::println);

}

8、trim + if insert

8.1 SQL

<!-- int insertAnimeByConditionIfTrim(Anime animeForm); -->
<insert id="insertAnimeByConditionIfTrim">
    <trim prefix="insert into `animes` ( " suffix=")" suffixOverrides=",">
        <if test="cid != null"> `cid`,</if>
        <if test="name != null"> `name`,</if>
        <if test="author != null"> `author`,</if>
        <if test="actor != null"> `actor`,</if>
        <if test="produce != null"> `produce`,</if>
        <if test="createDate != null"> `create_date`,</if>
    </trim>
    <trim prefix="values ( " suffix=")" suffixOverrides=",">
        <if test="cid != null"> #{cid},</if>
        <if test="name != null"> #{name},</if>
        <if test="author != null"> #{author},</if>
        <if test="actor != null"> #{actor},</if>
        <if test="produce != null"> #{produce},</if>
        <if test="createDate != null"> #{createDate},</if>
    </trim>
</insert>

执行SQL:

insert into `animes` ( `cid`, `name`, `author`, `actor`, `produce` ) values ( ?, ?, ?, ?, ? )

8.2 测试

@Test
public  void testMybatisMapperDynamicSQlIfTrimInsert() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    Anime animeForm = new Anime();
    animeForm.setCid(1);
    animeForm.setName("知否知否");
    animeForm.setAuthor("关心则乱");
    animeForm.setActor("明兰");
    animeForm.setProduce("优酷");

    //指定if+ trim 冬天SQL,新增动漫
    int row = animeMapper.insertAnimeByConditionIfTrim(animeForm);

    System.out.println(row);
}

9、@ Select

使用注释添加动漫

9.1 SQL

@Select("select `id`,`cid`,`name`,`author`,`actor`,`produce`,`create_date` from `animes` where id = #{id} ")
Anime selectAnimesByConditionUserAnnotationSelect(Integer id);

执行SQL:

Preparing: select `id`,`cid`,`name`,`author`,`actor`,`produce`,`create_date` from `animes` where id = ?

9.2 测试

@Test
public  void  testAnimesByConditionUserAnnotationSelect() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    Anime anime = animeMapper.selectAnimesByConditionUserAnnotationSelect(653);

    System.out.println(anime);

}

10、@Delete 批量删除

10.1 SQL

@Delete({"<script>",
            "delete from `animes`",
            "<foreach collection='ids' item='id' open=' where id in ( ' close= ' ) ' separator= ' , '> " ,
            "      #{id} ",
            "</foreach>" ,
            "</script>"})
int deleteAnimesByConditionUserAnnotationDelete(@Param("ids") List<Integer> ids);

执行SQL:

Preparing: delete from `animes` where id in ( ? , ? , ? )

10.2 测试

@Test
public void testDeleteAnimesByConditionUserAnnotationDelete() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    int row = animeMapper.deleteAnimesByConditionUserAnnotationDelete(Arrays.asList(649, 651, 652));

    System.out.println(row);

}

11、批量加入动漫分类

11.1 SQL

<!-- int insertCategoryBatchUserFoeEach(List<Category> categoryList) -->
<!-- int insertCategoryBatchUserFoeEach(@Param("categoryList") List<Category> categoryList); -->
<insert id="insertCategoryBatchUserFoeEach" >
    insert into `category` (`name`) values
    <!-- 
    默认参数:collection(不自定义参数名的时候可以使用Available parameters are [collection, list])
    自定义参数:categoryList 
    -->
    <foreach collection="collection" item="category" separator=", ">
        (#{category.name})
    </foreach>
</insert>

执行SQL:

Preparing: insert into `category` (`name`) values (?) , (?) , (?)

11.2 测试

@Test
public void testInsertCategoryBatchUserFoeEach() throws IOException {
    SqlSession defaultSqlSession = getSqlSessionFactory().openSession(true);

    //获取mapper接口的代理实现类对象
    AnimeMapper animeMapper = defaultSqlSession.getMapper(AnimeMapper.class);

    Category category1 = new Category();
    Category category2 = new Category();
    Category category3 = new Category();
    category1.setName("aaa");
    category2.setName("bbb");
    category3.setName("ccc");

    List<Category> categoryList = new ArrayList<>();
    categoryList.add(category1);
    categoryList.add(category2);
    categoryList.add(category3);

    int row = animeMapper.insertCategoryBatchUserFoeEach(categoryList);

    System.out.println(row);

}

12、排序无效问题

参考博客:mybatis动态sql排序无效问题

在使用MyBatis解析xml进行排序的时候,遇见排序无效的问题!

  • #将传入的数据当成一个字符串,会对自动传入的数据加一个双引号

如:order by #{user_id},如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”。多加了双引号

  • $将传入的数据直接显示生成在sql中。如:order by ${user_id},

如果传入的值是111,那么解析成sql时的值为order by 111, 如果传入的值是id,则解析成的sql为order by id。参数正常

  • 扩展:关于表名,字段等等的参数,必须使用$,不能使用#,要不然会预编译后,参数多加了 双引号

13、前端时间格式不正确问题

通过Mybatis,自动映射出来的时间,获取出来后时间格式不正确:

显示的格式为: Sat Dec 10 00:00:00 CST 1983

想要显示的格式: 1983-12-10

13.1 修改get实体的方法

因为MyBatis映射的格式就有问题,所以可以在get方法中将时间格式化;

public String getBirthday() {
    return new SimpleDateFormat("yyyy-MM-dd").format(this.birthday);
}

13.2 jstl格式化标签库

通过格式化标签库的 <fmt:formatDate value="" pattern = "" > 进行格式化

<fmt:formatDate value='${userInfo.birthday}' pattern='yyyy-MM-dd'/>

 

posted @ 2022-12-01 19:33  hanease  阅读(49)  评论(0编辑  收藏  举报