MyBatis基础入门教程

文章目录

  • 一、基本概述
    • (一)三层架构
    • (二)MyBatis与ORM思想简介
    • (三)MyBatis核心对象
    • (四)mapper映射文件常用标签
      • ①mappers标签
      • ②基础SQL标签
        • 1.select标签
        • 2.增、删、改标签
      • ③collection与association标签
      • ④resultMap标签
    • (五)基本工作流程
      • ①编写核心配置文件
      • ②编写mapper映射文件实现插入功能
      • ③编写测试类
      • ④编写工具类
  • 二、log4j的使用
    • (一)概述
      • ①日志级别
      • ②Appender
      • ③Layout
    • (二)MyBatis中的基本配置
  • 三、MyBatis实现CRUD
    • (一)MyBatis获取参数值的两种方式
      • ①单个字面量类型的参数
      • ②多个字面量类型的参数
      • ③map集合类型的参数
      • ④接收实体类类型参数
      • ⑤字面量类型和实体类类型参数
      • ⑥使用Param注解
    • (二)MyBatis各种常用功能
      • 前置:本项目的所有代码实现基础
      • ①高级映射思想
      • ②自定义映射resultMap
        • 1.字段和属性映射关系的处理
        • 2.多对一映射处理
          • 2.1 使用级联的方式进行处理
          • 2.2 使用association的方式进行处理
          • 2.3 使用分步查询的方式进行处理
        • 3.一对多映射处理
          • 3.1 使用collection的方式进行处理
          • 3.2 使用分步查询的方式进行处理
      • ③别名映射的实现
        • 1.别名的定义
        • 2.MyBatis定义的别名
      • ④动态SQL
        • 1.if标签
        • 2.where标签
        • 3.choose标签
        • 4.trim标签
        • 5.foreach标签
          • 5.1属性说明
          • 5.2 遍历单个List对象
          • 5.3 遍历单个array对象
          • 5.3 遍历单个map对象
        • 6.set标签
        • 7.sql标签与include标签
      • ⑤各种查询功能的实现
        • 1.获取包含所有实体类的集合
        • 2.主键查询
        • 3.查询总记录数
        • 4.获取单个实体类的字段map映射集合
        • 5.获取所有实体类的字段map映射集合
        • 6.模糊查询
          • 6.1使用${}
          • 6.2 使用字符串拼接函数concat
          • 6.3 直接拼接
      • ⑥各种插入功能的实现
        • 1.插入一个实体类对象并获取自增主键
        • 2.集合方式批量插入
      • ⑦各种修改功能的实现
        • 1.全字段修改
        • 1.动态修改
      • ⑧各种删除功能的实现
        • 1.删除单个对象
        • 2.批量删除
  • 四、MyBatis缓存
    • (一)一级缓存
      • ①缓存失效的四种情况
        • 1.使用不同的SqlSession
        • 2.同一个SqlSession查询条件不同
        • 3.同一个SqlSession两次查询期间执行了任何一次增删改的操作
        • 4.同一个SqlSession两次查询期间手动清空了缓存
      • ②一级缓存的清空
    • (二)二级缓存
      • ①二级缓存开启的三个条件
        • 1.核心配置文件
        • 2.Mapper映射文件
        • 3.修改标签语句
      • ②二级缓失效的情况与相关配置
  • 五、逆向工程
    • ①配置Maven插件
    • ②创建配置文件
    • ③使用插件
  • 六、注解开发


一、基本概述

(一)三层架构

  三层架构来源于后端开发的一种分层的思想,将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer),目的是为了"高内聚低耦合"的设计思想,各层的作用如下:

  • 界面层(UI):指与用户交互的界面,用于接收用户输入的数据和显示处理后用户需要的数据。
  • 业务逻辑层(BLL):主要负责接收用户的请求,并转换为对数据层的操作,一般放在service目录当中。
  • 数据访问层(DAL):主要完成各个对数据文件的操作,包括增、删、改、查等,一般放在dao目录当中。

事实上,还有一个隐含的"实体层(Entity)",其实现了面向对象思想中的"封装",贯穿于三层之间用于传递数据。如,要传递员工的信息包括:员工号、姓名、年龄、性别、工资等,此时若使用实体Employee作为参数,则会大大提高效率

(二)MyBatis与ORM思想简介

  MyBatis是一个半自动的ORM(Object Relation Mapping,对象关系映射,指在Java对象和数据库的关系模型之间建立一种对应关系)持久层(DAL)框架,其对JDBC操作数据库的过程进行了封装,使得开发者只需关注SQL本身。我们原来使用JDBC操作数据库,需要手动的写代码去注册驱动、获取connection、获取statement等等,现在Mybaits帮助我们把这些事情做了,我们只需要关注我们的业务sql即可,这样可以提高我们的开发效率。
ORM:Object Relational Mapping,在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。

  • 对象:Java的实体类对象。
  • 关系:关系型数据库。
  • 映射:实体类和数据库之间的对应关系。
Java概念 数据库概念
属性 字段/列
对象 记录/行

持久化:Persistence,即,将数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。

持久层:Persistence Layer,专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。

与其它持久化层技术的对比

  • JDBC
    • SQL夹杂在Java代码中耦合度高,导致硬编码。
    • 维护不易且实际开发需求中SQL有变化、频繁修改的情况很常见。
    • 代码冗余,开发效率低。
  • Hibernate与JPA
    • 操作简单,开发效率高,是一种全自动的ORM框架,不需要编写SQL语句,只需要定义好ORM映射关系即可直接进行CRUD操作。
    • 程序中长难复杂的SQL需要绕过框架。
    • 内部自动生成SQL,不易做特殊优化。
    • 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。
    • 反射操作太多,导致数据库性能下降。
  • MyBatis
    • 轻量级,性能出色。
    • SQL和Java编码分开,功能边界清晰,Java代码专注业务,而SQL语句专注数据。
    • 开发效率稍逊于Hibernate,但是可以接受。

(三)MyBatis核心对象

1.JDBC核心对象

  • DriverManager:数据库驱动管理对象。
  • Connection:数据库连接对象。
  • Statement | PrepareStatement:数据库操纵SQL语句对象。
  • ResultSet:结果集对象。

2.MyBatis核心对象
MyBatis完成一次数据库操作所经历的主要步骤如下:

MyBatis基础入门教程_第1张图片

  1. 加载配置文件
  2. 获取SqlSessionFactoryBuilder对象。
  3. 通过SqlSessionFactoryBuilder对象和配置文件获取SqlSessionFactory对象。
  4. 通过SqlSessionFactory对象打开一个SqlSession对象。
  5. 通过SqlSession对象获取对应的Mapper对象(通过执行器Executor、映射对象MappedStatement完成)。
  6. 使用Mapper对象调用对应接口完成数据库操作。
类型 说明
SqlSessionFactoryBuilder SqlSessionFactoryBuilder的唯一作用就是用来创建SqlSessionFactory,创建完成之后就不会用到它了,所以SqlSessionFactoryBuiler生命周期极短。SqlSessionFactoryBuiler中提供了9个方法,返回的都是SqlSessionFactory对象。
SqlSessionFactory SqlSessionFactory是一个接口,默认的实现类,主要是用来生成SqlSession对象,而SqlSession对象是需要不断被创建的,所以SqlSessionFactory是全局都存在的,也没有必要重复创建,所以这是一个单例对象。
SqlSession SqlSession是用来操作xml文件中我们写好的sql语句,每次操作数据库我们都需要一个SqlSession对象,SqlSession是用来和数据库中的事务进行对接的,所以SqlSession里面是包含了事务隔离级别等信息的。
Mapper Mapper是一个接口,没有任何实现类。主要作用就是用来映射Sql语句的接口,映射器的接口实例从SqlSession对象中获取,所以说Mapper实例作用域是和SqlSession相同或者更小。

生命周期

  • SqlSessionFactoryBuilder:只需要在创建SqlSessionFactory对象的时候使用,创建完成之后即可被丢弃。
  • SqlSessionFactory:全局唯一,是一个单例对象,但需要全局存在。
  • SqlSession:一般一个SqlSession对应一个request。
  • Mapper:一般控制在方法内。

3.MyBatis四大内置对象

  • ParameterHandler:处理SQL的参数对象。
  • ResultSetHandler:处理SQL的返回结果集。
  • StatementHandler:数据库的处理对象,用于执行SQL语句。
  • Executor:MyBatis的执行器,用于执行增删改查操作。

(四)mapper映射文件常用标签

以下内容参考博客:https://blog.csdn.net/cwx397562/article/details/100334210
注:这里介绍的主要是(除mappers外)映射文件中的标签,而核心配置文件中的标签放在"编写全局配置文件"一栏中。

①mappers标签

  MyBatis 是基于 sql 映射配置的框架,sql 语句都写在 Mapper 配置文件中,当构建 SqlSession 类之后,就需要去读取 Mapper 配置文件中的 sql 配置。
  而 mappers 标签就是用来配置需要加载的 sql 映射配置文件路径的,其中的每一个mapper子标签都是一个独立的映射配置文件的路径。
1.通过接口所在包配置

<mappers>
	
	 <package name="org.example.Mappers"/>
mappers>

  使用package标签进行配置,属性name指定mapper接口所在包,要求对应的映射文件必须与接口位于同一路径下,并且名称相同。
2.通过相对路径配置(常用)
  这里是将核心配置文件放在src/main/resources下,而mapper映射文件放在resources下的Mappers目录当中。

<mappers>
	
   	<mapper resource="Mappers/selectMapper.xml"/>
  	<mapper resource="Mappers/insertMapper.xml"/>
  	<mapper resource="Mappers/updateMapper.xml"/>
mappers>

3.类注册引入
  mapper标签通过指定mapper接口的名称,此时对应的映射文件必须与接口位于同一路径下,并且名称相同。这里,是将selectMapper、insertMapper、updateMapper接口放在同一UserMapper目录下。

<mappers>
  		
  		<mapper class="org.example.UserMapper.selectMapper"/>
  		<mapper class="org.example.UserMapper.insertMapper"/>
  		<mapper class="org.example.UserMapper.updateMapper"/>
mappers>

4.使用URL绝对路径方式引入(一般不使用)

<mappers>
	<mapper url="D:\IntelliJ IDEA\MyBatisDemo\src\main\java\org\example\UserMapper\insertMapper.java"/>
mappers>

②基础SQL标签

1.select标签
  • id:标签的唯一标识,值为mapper接口中方法的名称。
  • paramterType:将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
  • resultType:指明返回数据类型对应的类型全类名,只有当实体类的属性名与表中字段名相同时才能自动匹配。
  • resultMap:结果集属性的映射,不能同时使用resultType和resultMap。
  • flushCache:将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,对应select语句,默认值:false。
  • useCache:将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
  • timeout:这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
  • fetchSize:这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
  • statementType:STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
  • resultSetType:FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
  • databaseId:如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
  • resultOrdered:这个设置仅针对嵌套结果 select 语句适用:如果为true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至导致内存不够用。默认值:false。
  • resultSets:这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。
2.增、删、改标签
  • id:标签的唯一标识,值为mapper接口中方法的名称。
  • parameterType:将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
  • flushCache:将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,对应增、删、查语句,默认值:false。
  • timeout:这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
  • statement:STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
  • useGeneratedKeys:(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server这样的关系数据库管理系统的自动递增字段, oracle使用序列是不支持的,通过selectKey可以返回主键),默认值:false。
  • keyProperty:(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
  • keyColumn:(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
  • databaseId:如果配置了 databaseIdProvider(数据库厂商标识),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。

③collection与association标签

  collection与association标签的属性一样,都是用于resultMap返回关联映射使用,collection关联的是集合,而association是关联单个对象。

  • property:resultMap返回实体类中字段和result标签中的property一样。
  • column:数据库的列名或者列标签别名,是关联查询往下一个语句传送值。注意: 在处理组合键时,您可以使用column=“{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套查询语句。这就会把prop1和prop2设置到目标嵌套选择语句的参数对象中。
  • javaType:一般为ArrayList或是java.util.List。
  • ofType: java的实体类,对应数据库表的列名称,即关联查询select对应返回的类。
  • select:执行一个其他映射的sql语句返回一个java实体类型。

④resultMap标签

属性

  • id:唯一标识。
  • type:返回类型。
  • autoMapping:是否开启自动映射功能,即,自动查找与字段名小写同名的属性名,并调用setter方法。默认为true,设置为false后,则需要在resultMap内明确注明映射关系才会调用对应的setter方法。
  • extends:继承别的resultMap,可选。

关联子标签

  • id:设置主键使用,使用此标签配置映射关系(可能不止一个)。
  • result:一般属性和字段的映射关系。
  • association:关联一个对象时使用。
  • collection:关联一个集合时使用。

(五)基本工作流程

MyBatis的基本工作流程

  • 向SqlSession传入SQL语句的id,以及查询参数。
  • 找到待执行的SQL信息,交给Executor对SQL语句进行组装拼接,后交给StatementHandler处理。
  • StatementHandler封装了JDBC的操作,它负责根据SQL信息,生成对应的Statement,并利用ParameterHandler进行查询参数的解析与绑定,后执行查询。
  • StatemenHandler查询完毕,将结果集交由ResultSetHandler进行结果集信息的解析与封装处理(参数解析,结果集解析,都会用TypeHandler来做类型转换,java类型与JDBC类型)

MyBatis的基本使用有以下四步

  • 编写全局配置文件。
  • 编写mapper映射文件。
  • 加载配置文件,生成SqlSessionFactory。
  • 创建SqlSession,通过SqlSession调用mapper映射文件中的SQL语句来执行数据库CRUD操作。

①编写核心配置文件

1.1 IDEA创建一个Maven项目
MyBatis基础入门教程_第2张图片
1.2 写入pom.xml文件导入jar包

    <dependencies>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.11version>
        dependency>
        
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13.2version>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.30version>
        dependency>

1.3 创建MyBatis核心配置文件
  MyBatis核心配置文件习惯上命名为mybatis-config.xml,这个文件名并非强制要求,事实上,在整合Spring之后,这个配置文件可以省略。
  核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息,核心配置文件存放的位置为:src/main/resources目录下,且,MyBatis的核心配置
文件的配置必须按照严格的顺序。
MyBatis基础入门教程_第3张图片

核心配置文件解析过程

  1. 获得配置文件的InputStream,创建Document对象。
  2. 利用Xpath语法,解析各个配置节点。
  3. 将信息封装到Configuration对象中,生成SqlSessionFactory。
SqlSessionFactoryBuilder # build 
 |- XMLConfigBuilder # parse 
   |- XMLConfigBuilder # parseConfiguration

核心配置文件的配置顺序
  配置信息都要写在configuration标签内,且顺序为:

  • properties(属性):习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件,可以用${}占位符快速获取数据源的信息。
  • settings(设置):用来开启或关闭mybatis的一些特性,比如可以用来开启延迟加载,可以用来开启二级缓存
  • typeAliases(类型别名):定义别名,简化书写。
  • typeHandlers(类型处理器):用于处理Java类型和Jdbc类型之间的转换,mybatis有许多内置的TypeHandler,比如StringTypeHandler,会处理Java类型String和Jdbc类型CHAR和VARCHAR。
  • objectFactory(对象工厂):mybatis会根据resultType或resultMap的属性来将查询得到的结果封装成对应的Java类,它有一个默认的DefaultObjectFactory,用于创建对象实例。
  • plugins(插件):MyBatis 可以使用第三方的插件来对功能进行扩展,如,分页助手 PageHelper。
  • environments(环境配置):使用default可配置默认环境名。
    • environment(环境配置):使用id可配置当前环境的唯一标识ID。
    • transactionManager(事务管理器):使用type可配置事务管理器类型。
      MyBatis基础入门教程_第4张图片
    • dataSource(数据源):有UNPOOLED(无数据连接池,每次被请求时打开和关闭连接)、POOLED(使用数据库连接池) 、JNDI(能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置 一个 JNDI 上下文的引用)。
  • databaseIdProvided(数据库厂商标识)
  • mappers(映射器):用于加载映射。
加载方式 案例
使用相对于类路径资源引用(常用)
使用Mapper接口的全限定类名,此种方法要求mapper 接口名称和mapper 映射文件名称相同,且放在同一个目录中。
注册指定包下的所有接口,此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。

注:有得用"/“表示目录结构,而有的用”."来表示。

核心配置文件(mybatis-config.xml)模板:



<configuration>
    
    <environments default="development">
        
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                
                <property name="driver" value="${driver}"/>
                
                <property name="url" value="${url}"/>
                
                <property name="username" value="${username}"/>
                
                <property name="password" value="${password}"/>
                
                

                
                
            dataSource>
        environment>
    environments>
    <mappers>
        
        <mapper resource="UserMapper.xml"/>
    mappers>
configuration>

注:可在设置->编辑器->文件和代码模板当中保存此模板,而新建时会要求输入参数,即可完成自动创建:
MyBatis基础入门教程_第5张图片

  在这里,我创建了专用的资源文件jdbc.properties在src/main/resources目录下(和mybatis-config.xml同目录),可方便后面的修改,降低耦合性:
jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatisdemo?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456

需要在核心配置文件中引入jdbc.properties资源文件:

<properties resource="jdbc.properties"/>

从而得到:
mybatis-config.xml



<configuration>
	
    <properties resource="jdbc.properties"/>
    
    <environments default="development">
        
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                
                <property name="driver" value="${jdbc.driver}"/>
                
                <property name="url" value="${jdbc.url}"/>
                
                <property name="username" value="${jdbc.username}"/>
                
                <property name="password" value="${jdbc.password}"/>
                
                

                
                
            dataSource>
        environment>
    environments>
    <mappers>
        
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    mappers>
configuration>

②编写mapper映射文件实现插入功能

2.1 创建User表

create table tb_user(
    id int not null auto_increment primary key ,
    username varchar(20),
    password varchar(20),
    age int,
    sex char,
    email varchar(20)
);

2.2 创建映射实体类

package org.example.Entity;

public class User {
    private Integer Uid;
    private String UserName;
    private String UPassword;
    private Integer UAge;
    private String USex;
    private String UEmail;

    public User() {
    }

    public User(Integer uid, String userName, String UPassword, Integer UAge, String USex, String UEmail) {
        Uid = uid;
        UserName = userName;
        this.UPassword = UPassword;
        this.UAge = UAge;
        this.USex = USex;
        this.UEmail = UEmail;
    }

    public Integer getUid() {
        return Uid;
    }

    public void setUid(Integer uid) {
        Uid = uid;
    }

    public String getUserName() {
        return UserName;
    }

    public void setUserName(String userName) {
        UserName = userName;
    }

    public String getUPassword() {
        return UPassword;
    }

    public void setUPassword(String UPassword) {
        this.UPassword = UPassword;
    }

    public Integer getUAge() {
        return UAge;
    }

    public void setUAge(Integer UAge) {
        this.UAge = UAge;
    }

    public String getUSex() {
        return USex;
    }

    public void setUSex(String USex) {
        this.USex = USex;
    }

    public String getUEmail() {
        return UEmail;
    }

    public void setUEmail(String UEmail) {
        this.UEmail = UEmail;
    }

    @Override
    public String toString() {
        return "User{" +
                "Uid=" + Uid +
                ", UserName='" + UserName + '\'' +
                ", UPassword='" + UPassword + '\'' +
                ", UAge=" + UAge +
                ", USex='" + USex + '\'' +
                ", UEmail='" + UEmail + '\'' +
                '}';
    }
}

2.3 创建接口
  MyBatis中的mapper接口相当于以前的dao层,但是区别在于mapper仅仅是接口,我们不需要提供其实现类,事实上,MyBatis有面向接口编程的功能,故而,只要调用接口中的方法,它就会自动匹配一个相映射的SQL语句并执行。
这里我放在了src/main/java/UserMapper目录下:

package org.example.UserMapper;

public interface insertMapper {
    /*
    * 添加用户信息
    * */
    int insertUser();
}

2.4 创建MyBatis的映射文件
  MyBatis映射文件用于编写SQL,用来访问以及操作表中的数据,且,MyBatis的映射文件的一般存放位置是:src/main/resources/mappers目录下。
mapper映射文件的解析过程

  1. 拥有namespace属性作为唯一标识的mapper.xml映射文件,会被注册到Configuration中的mapperRegistry中,以便后续生成mapper代理对象。
  2. 一个mapper.xml,对应一个MapperBuilderAssistant对象,这个builderAssistant对象解析并保存了该mapper.xml中的公共标签,如parameterMap,resultMap,cache,sql,这些标签可能在某个CRUD标签里被使用。
  3. 解析CRUD标签,即 select | update | insert | delete 标签,一个CRUD标签,被封装成一个MappedStatement对象,以标签的id属性作为唯一标识,MappedStatement里包含了SQL语句信息,参数映射信息,结果集映射信息。
XMLConfigBuilder # mapperElement
 |- XMLMapperBuilder # parse
   |- XMLMapperBuilder # configurationElement

映射文件的命名规则
  表所对应的实体类的类+Mapper.xml,如,表tb_user,其映射的实体类为User,则其所对应的映射文件为UserMapper.xml。因此,一个映射文件对应一个实体类,对应一张表上的操作。

MyBatis中面向接口操作数据的两个前提

  • 映射文件的namespace和mapper接口的全类名一致。
  • 映射文件中的SQL语句的id要和mapper接口中的方法名一致。

官方文档的映射文件



<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  select>
mapper>

可将其设置在IDEA的模板当中。
创建User实体类的映射文件
  在src/main/resources目录下创建UserMapper.xml,这里,我的项目结构为:
MyBatis基础入门教程_第6张图片

  • namespace:实体类所对应Mapper类/接口的全类名。
  • id:方法名和映射文件中SQL的id一致。



<mapper namespace="org.example.UserMapper.insertMapper">
    
    
    <insert id="insertUser">
        insert into tb_user values (null,'admin','123456',18,'男','[email protected]')
    insert>
mapper>

在mybatis-config.xml当中引入映射文件

    <mappers>
        
        <mapper resource="UserMapper.xml"/>
    mappers>

③编写测试类

  • 加载配置文件,生成SqlSessionFactory。
  • 创建SqlSession,通过SqlSession调用mapper映射文件中的SQL语句来执行数据库CRUD操作。
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 org.example.UserMapper.insertMapper;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        //加载核心配置文件
        InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //获取SqlSessionFactory对象,是"生产"SQLSession的"工厂".
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        //获取SqlSession对象,代表Java程序和数据库之间的会话(类似于HttpSession是Java程序和浏览器之间的会话).
        SqlSession sqlSession=sqlSessionFactory.openSession();
        //工厂模式:如果创建某一个对象,使用的过程基本固定,那么就可以把创建这个对象的相关代码封装到一个"工厂类"中,以后都使用这个工厂类"生产我们需要的对象.
        //事实上,数据库都是通过会话来与数据库交互,创建一个会话就会把该会话加载到内存中,当中有许多监听器监听会话过程.
        //获取mapper接口对象
        insertMapper mapper=sqlSession.getMapper(insertMapper.class);
        //测试功能
        int result=mapper.insertUser();
        //提交事务,因为使用的是JDBC管理方式,需要手动提交
        sqlSession.commit();
        System.out.println(result);
    }
}

运行结果:
MyBatis基础入门教程_第7张图片
MyBatis基础入门教程_第8张图片
注,若要实现自动提交功能,可在获取SqlSession对象时进行设置:

SqlSession sqlSession=sqlSessionFactory.openSession(true);

④编写工具类

  将获取SqlSession对象的操作进行封装,可大大简化代码。
SqlSessionUtils.java

package org.example.MyBatisUtils;

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;

public class SqlSessionUtils {
    public static SqlSession getSqlSession() throws IOException {
        SqlSession sqlSession=null;
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
            sqlSession=sqlSessionFactory.openSession(true);
        }catch (IOException e){
            e.printStackTrace();
        }
        return sqlSession;
    }
}

二、log4j的使用

  Log4J 是 Apache 的一个开源项目,通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、GUI 组件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。

(一)概述

  Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件等);Layout 控制日志信息的输出格式。

①日志级别

  Log4J在org.apache.log4j.Level类中定义了OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL七种日志级别:

日志级别 说明
OFF 最高日志级别,关闭左右日志。
FATAL 将会导致应用程序退出的错误。
ERROR 发生错误事件,但仍不影响系统的继续运行。
WARN 警告,即潜在的错误情形。
INFO 一般用于粗粒度级别上,强调应用程序的运行全程。
DEBUG 一般用于细粒度级别上,对调试应用程序非常有帮助
ALL 最低等级,打开所有日志记录。

注:一般只用ERROR、WARN、INFO、DEBUG四个日志级别,优先级从高到低为 ERROR > WARN > INFO > DEBUG。

②Appender

  Appender(输出端),用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。

 1.org.apache.log4j.ConsoleAppender(控制台)  
 2.org.apache.log4j.FileAppender(文件)  
 3.org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)  
 4.org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)  
 5.org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

Log4j 常用的输出目的地有以下几种:

输出端类型 作用
ConsoleAppender 将日志输出到控制台。
FileAppender 将日志输出到文件中。
DailyRollingFileAppender 将日志输出到一个日志文件,并且每天输出到一个新的文件。
RollingFileAppender 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大 小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件。
JDBCAppender 将日志信息保存到数据库中。

③Layout

  Layout(日志格式化器),用于将日志信息格式化。

 1.org.apache.log4j.HTMLLayout(以HTML表格形式布局),  
 2.org.apache.log4j.PatternLayout(可以灵活地指定布局模式),  
 3.org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),  
 4.org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
格式化器类型 作用
HTMLLayout 格式化日志输出为HTML表格形式。
SimpleLayout 简单的日志输出格式化,打印的日志格式为(info - message)。
PatternLayout 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式。

常用输出格式
  Log4J最常用的日志输出格式为:org.apache.log4j.PatternLayOut,其主要参数为:

  • %n - 换行
  • %m - 日志内容
  • %p - 日志级别(FATAL, ERROR,WARN, INFO,DEBUG or custom)
  • %r - 程序启动到现在的毫秒数
  • %t - 当前线程名
  • %d - 日期和时间, 一般使用格式
  • %d{yyyy-MM-dd HH:mm:ss, SSS}
  • %l - 输出日志事件的发生位置, 同 %F%L%C%M
  • %F - java 源文件名
  • %L - java
    源码行数
    %C - java 类名,
    %C{1} 输出最后一个元素
    %M - java 方法名

(二)MyBatis中的基本配置

1.引入依赖

        
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.17version>
        dependency>

2.配置文件
  创建配置文件log4j-config.properties。

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/lding.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3.mybatis-config.xml配置

   <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    settings>

三、MyBatis实现CRUD

  SQL所需的参数可以通过相应的接口进行传递,此时需要在映射文件中获取这些参数值,并将其拼接到相应的SQL语句当中,最后执行SQL语句。

(一)MyBatis获取参数值的两种方式

1.使用${}获取
  使用${}来获取参数值的本质是使用字符串拼接的方式拼接SQL,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号。
2.使用#{}获取
  使用#{}获取参数值的本质是占位符赋值,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号。
注:以上两种实际上就是JDBC中获取参数值的方式。
注:由于映射文件当中mapper的namespace属性必须写全类名,故,当不同查询语句在不同的接口当中时,需要为每一个接口创建映射文件,项目结构:
MyBatis基础入门教程_第9张图片

①单个字面量类型的参数

  若mapper接口的方法参数为单个的字面量类型,此时可以使用${}和#{}以任意的名称获取参数的值,注意,${}需要手动改添加单引号。
Mapper接口:

package org.example.UserMapper;

import org.example.Entity.User;
import java.util.List;

public interface selectMapper {
    /*
    * 根据用户名查询用户信息
    * */
    User getUserByUsername(String username);
}

1.使用#{}
创建接口映射文件selectMapper.xml



<mapper namespace="org.example.UserMapper.selectMapper">
    
    <select id="getUserByUsername" resultType="org.example.Entity.User">
        select * from tb_user where username=#{username}
    select>
mapper>

添加至核心配置文件mybatis-config.xml


<mapper resource="Mappers/selectMapper.xml"/>

编写测试类MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.selectMapper;
import org.junit.Test;
import java.io.IOException;

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        selectMapper mapper=sqlSession.getMapper(selectMapper.class);
        User user=mapper.getUserByUsername("admin");
        System.out.println(user);
    }
}

2.使用${}
  ${}的底层原理是使用字符串拼接,故需要手动改添加单引号(不太建议使用,因为可能有SQL注入发生)。

    
    <select id="getUserByUsername" resultType="org.example.Entity.User">
        select * from tb_user where username='${username}'
    select>

②多个字面量类型的参数

  若mapper接口的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1,…为键,以参数为值;以param1,param2,…为键,以参数为值,因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。
注:两种方式的索引并不一致。

package org.example.UserMapper;

import org.example.Entity.User;
import java.util.List;

public interface selectMapper {
    /*
    * 根据用户名、密码查询用户信息
    * */
    User getUserByUsernameAndPassword(String username,String password);
}

1.使用#{}
selectMapper.xml



<mapper namespace="org.example.UserMapper.selectMapper">
    
    <select id="getUserByUsernameAndPassword" resultType="org.example.Entity.User">
        select * from tb_user where username=#{arg0} and password=#{param2};
        
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.selectMapper;
import org.junit.Test;
import java.io.IOException;

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        selectMapper mapper=sqlSession.getMapper(selectMapper.class);
        User user=mapper.getUserByUsernameAndPassword("admin","123456");
        System.out.println(user);
    }
}

2.使用${}



<mapper namespace="org.example.UserMapper.selectMapper">
    
    <select id="getUserByUsername" resultType="org.example.Entity.User">
        select * from tb_user where username='${username}'
    select>
    
    <select id="getUserByUsernameAndPassword" resultType="org.example.Entity.User">
        select * from tb_user where username='${arg0}' and password='${param2}';
        
    select>
mapper>

③map集合类型的参数

  若mapper接口方法的参数有多个时,可以手动将这些参数放在一个集合map当中存储,而访问这些参数时通过键来进行访问。
接口方法

package org.example.UserMapper;

import org.example.Entity.User;
import java.util.List;
import java.util.Map;

public interface selectMapper {
    /*
    * 根据用户名、密码、年龄查询用户信息
    * */
    User getUserByMapOfUsernameAndPasswordAndId(Map<String,Object> map);
}

核心配置文件mybatis-config.xml

    <mappers>
        
        <mapper resource="Mappers/selectMapper.xml"/>
    mappers>

1.使用#{}



<mapper namespace="org.example.UserMapper.selectMapper">
    
    <select id="getUserByMapOfUsernameAndPasswordAndId" resultType="org.example.Entity.User">
        select * from tb_user where username=#{username} and password=#{password} and id=#{id}
    select>
mapper>

测试代码

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.selectMapper;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        selectMapper mapper=sqlSession.getMapper(selectMapper.class);
        Map<String,Object>map=new HashMap<>();
        map.put("username","admin");
        map.put("password","123456");
        map.put("id",1);
        User user=mapper.getUserByMapOfUsernameAndPasswordAndId(map);
        System.out.println(user);
    }
}

2.使用${}



<mapper namespace="org.example.UserMapper.selectMapper">
    
    <select id="getUserByMapOfUsernameAndPasswordAndId" parameterType="map" resultType="org.example.Entity.User">
        select * from tb_user where username='${username}' and password='${password}' and id='${id}'
    select>
mapper>

④接收实体类类型参数

  在接收实体类参数时,要求映射文件中SQL语句的参数名为实体类的属性名。

package org.example.UserMapper;

import org.example.Entity.User;

public interface insertMapper {
    /*
     * 添加用户信息
     * */
    int insertUser(User user);
}

1.使用#{}
insertMapper.xml



<mapper namespace="org.example.UserMapper.insertMapper">
    
    <insert id="insertUser">
        insert into tb_user values (null,#{UserName},#{UPassword},#{UAge},#{USex},#{UEmail});
    insert>
mapper>

测试代码

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.insertMapper;
import org.junit.Test;
import java.io.IOException;

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        insertMapper mapper=sqlSession.getMapper(insertMapper.class);
        int result= mapper.insertUser(new User(null,"李四","123",23,"男","[email protected]"));
        System.out.println(result);
    }
}

2.使用${}



<mapper namespace="org.example.UserMapper.insertMapper">
    
    <insert id="insertUser">
        insert into tb_user values (null,'${UserName}','${UPassword}',${UAge},'${USex}','${UEmail}');
    insert>
mapper>

⑤字面量类型和实体类类型参数

  这是在写"各种插入功能的实现"时发现的情况。
  场景:我所插入的Student实体类中包含一个Clazz对象属性(遵循"多对一"原则),而Student表包含ClazzNo字段,我需要在插入Student对象时同时设置它的ClazzNo字段(这是不能通过创建它的Clazz对象属性能实现的),故接口的方法声明为:

package org.example.StudentMapper;

import org.example.Entity.Student;

public interface insertStudentMapper {
    /*
    * 用实体类对象进行插入
    * */
    int insertByStudent(Student student,int ClazzNo);
}

insertStudentMapper.xml



<mapper namespace="org.example.StudentMapper.insertStudentMapper">
    
    <insert id="insertByStudent" useGeneratedKeys="true" keyProperty="arg0.SNo">
        insert into student VALUES (null,#{arg0.SName}, #{arg0.SAge}, #{arg0.SBirthday},#{arg1})
    insert>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        insertStudentMapper mapper=session.getMapper(insertStudentMapper.class);
        String str="2004-03-24 23:38:47";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date Birthday=simpleDateFormat.parse(str);
        Student student=new Student(null,"田七",19,Birthday,null);
        System.out.println(mapper.insertByStudent(student,301));
        System.out.println(student.getSNo());
    }
}

可见,需要使用"arg0.属性名称"的形式进行获取。

⑥使用Param注解

  当使用注解开发时,若需要传入多个参数,可以结合@Param注解,@Param标签会被mybatis处理并封装成一个Map对象,以Param中的值为键,以传递的参数为键的值(故在SQL语句当中调用参数时使用的是注解内的值而非函数参数),此时在映射文件中就可用注解中的值进行参数的调用。
insertMapper.java

package org.example.UserMapper;

import org.apache.ibatis.annotations.Param;

public interface insertMapper {
    /*
     * 添加用户信息
     * */
    int insertUser(@Param("Uid")int uid,@Param("UserName")String username,@Param("UPassword")String password,@Param("UAge")int age,@Param("USex")String sex,@Param("UEmail")String email);
}

1.使用#{}



<mapper namespace="org.example.UserMapper.insertMapper">
    
    <insert id="insertUser">
        insert into tb_user values (null,#{UserName},#{UPassword},#{UAge},#{USex},#{UEmail});
    insert>
mapper>

2.使用${}



<mapper namespace="org.example.UserMapper.insertMapper">
    
    <insert id="insertUser">
        insert into tb_user values (null,'${UserName}','${UPassword}',${UAge},'${USex}','${UEmail}');
    insert>
mapper>

(二)MyBatis各种常用功能

前置:本项目的所有代码实现基础

1.前置数据库student_course
(使用DataGrip编写)

CREATE TABLE `student` (
  `SNo` int NOT NULL AUTO_INCREMENT COMMENT '学生学号',
  `SName` varchar(20) NOT NULL COMMENT '学生姓名',
  `SAge` int NOT NULL COMMENT '学生年龄',
  `SBirthday` datetime NOT NULL COMMENT '学生出生日期',
  `ClazzNo` int NOT NULL COMMENT '班级序号',
  PRIMARY KEY (`SNo`),
  KEY `student_clazz__fk` (`ClazzNo`),
  CONSTRAINT `student_clazz__fk` FOREIGN KEY (`ClazzNo`) REFERENCES `clazz` (`ClazzNo`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';

CREATE TABLE `course` (
  `CNo` int NOT NULL COMMENT '课程号',
  `CName` varchar(20) NOT NULL COMMENT '课程名',
  PRIMARY KEY (`CNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='课程表';

CREATE TABLE `score` (
  `SNo` int NOT NULL AUTO_INCREMENT COMMENT '学生学号',
  `CNo` int NOT NULL COMMENT '课程号',
  `Score` int NOT NULL DEFAULT '0' COMMENT '学生课程分数',
  PRIMARY KEY (`SNo`,`CNo`),
  KEY `Score_fk_Course` (`CNo`),
  CONSTRAINT `Score_fk_Course` FOREIGN KEY (`CNo`) REFERENCES `course` (`CNo`),
  CONSTRAINT `Score_fk_Student` FOREIGN KEY (`SNo`) REFERENCES `student` (`SNo`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分数表';

CREATE TABLE `clazz` (
  `ClazzNo` int NOT NULL COMMENT '班级编号',
  `ClazzName` varchar(20) NOT NULL COMMENT '班级名称',
  PRIMARY KEY (`ClazzNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='班级表';

insert into student values (null,'张三',18,'2005-09-14 23:18:17',301),
                           (null,'李四',19,'2004-03-12 15:34:13',301),
                           (null,'王五',17,'2006-12-02 08:20:12',302),
                           (null,'赵六',18,'2005-02-23 16:14:20',302);

insert into course values (101,'高等数学'),
                          (102,'线性代数'),
                          (103,'数据结构'),
                          (104,'操作系统'),
                          (105,'计算机组成原理');

insert into score values (1,101,95),
                         (1,102,85),
                         (1,103,79),
                         (1,104,87),
                         (2,101,91),
                         (2,103,75),
                         (2,105,85),
                         (3,102,85),
                         (3,104,95),
                         (4,102,65),
                         (4,103,78),
                         (4,105,85);

insert into Clazz values (301,'计科一班'),
                         (302,'计科二班');

MyBatis基础入门教程_第10张图片

2.创建实体类
Student.java

package org.example.Entity;

import java.util.Date;

public class Student {
    private Integer SNo;
    private String SName;
    private Integer SAge;
    private Date SBirthday;
    private Clazz clazz;
    /*
    * 后面内容省略,是表中的JavaBean
    * */

Course.java

package org.example.Entity;

public class Course {
    private Integer CNo;
    private String CName;
    /*
    * 后面内容省略,是表中的JavaBean
    * */

Score.java

package org.example.Entity;

public class Score {
    private Integer SNo;
    private Integer CNo;
    private Integer Score;
    /*
    * 后面内容省略,是表中的JavaBean
    * */
}

Clazz.java

package org.example.Entity;

import java.util.List;

public class Clazz {
    private int ClazzNo;
    private String ClazzName;
    private List<Student> students;
    /*
    * 后面内容省略,是表中的JavaBean
    * */

3.我的项目结构
MyBatis基础入门教程_第11张图片

①高级映射思想

&emps; 为了能够很好地表现数据表之间的关联,在处理数据表时,会遵循"多对一对应对象,一对多对应集合"的原则。
1.多对一映射
  “多对一对应对象”,是指需要在"多"的实体类中创建"一"的实体类对象。
  以上述学生-课程数据库为例,多个学生可对应一个班级,故,学生实体类中应包含一个班级实体类对象。

package org.example.Entity;

import java.util.Date;

public class Student {
    private Integer SNo;
    private String SName;
    private Integer SAge;
    private Date SBirthday;
    private Clazz clazz;
    /*
    * 后面内容省略,是表中的JavaBean
    * */

2.一对多映射
  “一对多对应集合”,是指需要在"一"的实体类中创建"多"的实体类对象的集合。
  以上述学生-课程数据库为例,一个班级可对应多个学生,故,班级实体类中应包含一个学生实体类对象的集合。

package org.example.Entity;

import java.util.List;

public class Clazz {
    private int ClazzNo;
    private String ClazzName;
    private List<Student> students;
    /*
    * 后面内容省略,是表中的JavaBean
    * */

3.多对多映射
  多对多映射时,可新建一张表表示两表关系。
  以上述学生-课程数据库为例,学生和课程的分数是多对多关系,故新建一张分数表。

②自定义映射resultMap

  之前一直使用的是resultType来指定返回值类型,但前提是表的字段名要与实体类的属性名相同,而当二者不一致时,就无法将查询结果的字段封装到实体类的对应属性当中。
  事实上,可通过resultMap来自定义映射关系,进行手动封装。

1.字段和属性映射关系的处理

1.1 resultMap标签
作用:建立SQL查询结果字段与实体属性的映射关系。

属性名 说明
id resultMap标签的唯一标识。
type 返回值的全限定类名。
autoMapping 是否开启自动映射功能,即,自动查找与字段名小写同名的属性名,并调用setter方法。默认为true,设置为false后,则需要在resultMap内明确注明映射关系才会调用对应的setter方法。
extends 继承别的resultMap,可选。

1.2 id与result子标签
作用

  • id标签:id标签是resultMap的子标签,用于设置主键字段与实体类属性的映射关系。
  • result标签:result标签是resultMap的子标签,用于设置普通字段与实体类属性的映射关系。

属性配置

属性名 说明
property 需要映射到JavaBean的属性名称。
column 数据表的列名或者标签别名。
javaType 一个 Java 类的全限定名,或一个类型别名。通常不会配置,mybatis 能够根据参数信息自动识别,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType JDBC类型, JDBC类型为CUD操作时列可能为空时进行处理。
typeHandler 指定类型处理器的全限定类名或类型别名。

关于什么时候使用jdbcType和JavaType:举一个例子,如果数据库id字段是int类型,那么它的jdbc就是Integer类型。当实体类的这个映射属性id为Long类型时,如果不设置jdbcType和javaType的话,查询的结果返回给实体时就会转换错误,写了这两个mybatis就会帮我们转换成相应的类型,从来避免发生错误。

Mybatis中javaType和jdbcType对应关系

 JDBCType            JavaType
    CHAR                String
    VARCHAR             String
    LONGVARCHAR         String
    NUMERIC             java.math.BigDecimal
    DECIMAL             java.math.BigDecimal
    BIT                 boolean
    BOOLEAN             boolean
    TINYINT             byte
    SMALLINT            short
    INTEGER             int
    BIGINT              long
    REAL                float
    FLOAT               double
    DOUBLE              double
    BINARY              byte[]
    VARBINARY           byte[]
    LONGVARBINARY       byte[]
    DATE                java.sql.Date
    TIME                java.sql.Time
    TIMESTAMP           java.sql.Timestamp
    CLOB                Clob
    BLOB                Blob
    ARRAY               Array
    DISTINCT            mapping of underlying type
    STRUCT              Struct
    REF                 Ref
    DATALINK            java.net.URL[color=red][/color]


核心配置文件mybatis-config.xml

    <mappers>
        
        <mapper resource="StudentMapper/selectStudentMapper.xml"/>
    mappers>

接口selectStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;

public interface selectStudentMapper {
    /*
    * 根据学号查询学生
    * */
    Student getStudentById(Integer SNo);
}

映射文件selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentById" resultMap="StudentResultMap">
        select * from student where SNo=#{SNo}
    select>
mapper>

测试类

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        Student student=mapper.getStudentById(1);
        System.out.println(student);
    }
}

MyBatis基础入门教程_第12张图片

1.3constructor子标签
  constructor子标签,指定使用指定参数列表的构造函数来实例化领域模型。注意:其子元素顺序必须与参数列表顺序对应。
子标签配置

子标签名 说明
idArg 标记该参数为主键。
arg 标记该参数为普通字段。
2.多对一映射处理

  在之前提到过"多对一映射"的概念,此处就不再赘述。
  在本项目中,需要处理的多对一映射是Student实体类中的Clazz实体类属性,即:

package org.example.Entity;

import java.util.Date;

public class Student {
    private Integer SNo;
    private String SName;
    private Integer SAge;
    private Date SBirthday;
    private Clazz clazz;
    /*...*/
2.1 使用级联的方式进行处理

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        
        <result property="clazz.ClazzNo" column="ClazzNo"/>
        <result property="clazz.ClazzName" column="ClazzName"/>
    resultMap>
    
    <select id="getStudentById" resultMap="StudentResultMap">
        select * from student,clazz where student.SNo=#{SNo} and student.ClazzNo=clazz.ClazzNo;
    select>
mapper>

测试类:

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        Student student=mapper.getStudentById(1);
        System.out.println(student);
    }
}

MyBatis基础入门教程_第13张图片

2.2 使用association的方式进行处理

  association标签是resultMap的子标签,可专门用于处理多对一的映射关系,属性如下:

  • property:需要处理的属性名。
  • javaType:声明该属性的java类型。
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        
        <association property="clazz" javaType="org.example.Entity.Clazz">
            <id property="ClazzNo" column="ClazzNo"/>
            <result property="ClazzName" column="ClazzName"/>
        association>
    resultMap>

注:javaType属性不能遗漏,不然mybatis将不知道是哪个实体类的属性和实体类表相互对应。

2.3 使用分步查询的方式进行处理

  分步查询是建议使用的,其可避免多表连接,效率最高。
2.3.1 属性说明

  • select:指定分步查询前一步SQL所对应的接口方法名。
  • column:传递分步查询前一步SQL所需条件。

2.3.2查询步骤

  • 先通过班级编号获取班级实体类。
  • 再通过学生编号获取学生实体类。
  • 执行测试类。

2.3.2.1 先通过班级编号获取班级实体类
核心配置文件mybatis-config.xml

        
        <mapper resource="ClazzMapper/selectClazzMapper.xml"/>

接口selectClazzMapper.java

package org.example.ClazzMapper;

import org.example.Entity.Clazz;

public interface selectClazzMapper {
    /*
    * 根据ClazzNo查询班级信息
    * */
    Clazz getClazzByClazzNo(int ClazzNo);
}

映射文件selectClazzMapper.xml



<mapper namespace="org.example.ClazzMapper.selectClazzMapper">
    <resultMap id="getClazzByClazzNo" type="org.example.Entity.Clazz">
        <id property="ClazzNo" column="ClazzNo"/>
        <result property="ClazzName" column="ClazzName"/>
    resultMap>
    
    <select id="getClazzByClazzNo" resultMap="getClazzByClazzNo">
        select * from clazz where ClazzNo=#{ClazzNo}
    select>
mapper>

2.3.2.2 再通过学生编号获取学生实体类
核心配置文件mybatis-config.xml

        
        <mapper resource="StudentMapper/selectStudentMapper.xml"/>

接口selectStudentMapper.java方法

package org.example.StudentMapper;

import org.example.Entity.Student;

public interface selectStudentMapper {
    /*
    * 根据学号查询学生
    * */
    Student getStudentById(Integer SNo);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        
        <association property="clazz" select="org.example.ClazzMapper.selectClazzMapper.getClazzByClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentById" resultMap="StudentResultMap">
        select * from student where student.SNo=#{SNo};
    select>
mapper>

2.3.2.3 执行测试类
MyBatis基础入门教程_第14张图片
可见,执行了两条查询语句。

3.一对多映射处理

  在之前提到过"一对多映射"的概念,此处就不再赘述。
  在本项目中,需要处理的多对一映射是Clazz实体类中的Student实体类集合属性,即:

package org.example.Entity;

import java.util.List;

public class Clazz {
    private int ClazzNo;
    private String ClazzName;
    private List<Student> students;
    /*
    * 后面内容省略,是表中的JavaBean
    * */

接口方法

package org.example.ClazzMapper;

import org.example.Entity.Clazz;

public interface selectClazzMapper {
    /*
    * 根据ClazzNo查询班级信息
    * */
    Clazz getClazzByClazzNo(int ClazzNo);
}
3.1 使用collection的方式进行处理

  collection专门用来处理一对多的关系,由于collection已经表示集合,故后面只要给出集合的泛型即可,此处使用ofType即可。
属性说明

  • property:处理一对多关系的属性。
  • ofType:表示该属性对应的集合中存储的数据类型。

接口方法

package org.example.ClazzMapper;

import org.example.Entity.Clazz;

public interface selectClazzMapper {
    /*
    * 根据ClazzNo查询班级信息
    * */
    Clazz getClazzByClazzNo(int ClazzNo);
}

selectClazzMapper.xml



<mapper namespace="org.example.ClazzMapper.selectClazzMapper">
    <resultMap id="getClazzByClazzNo" type="org.example.Entity.Clazz">
        <id property="ClazzNo" column="ClazzNo"/>
        <result property="ClazzName" column="ClazzName"/>
        
        <collection property="students" ofType="org.example.Entity.Student">
            <id property="SNo" column="SNo"/>
            <result property="SName" column="SName"/>
            <result property="SAge" column="SAge"/>
            <result property="SBirthday" column="SBirthday"/>
        collection>
    resultMap>
    
    <select id="getClazzByClazzNo" resultMap="getClazzByClazzNo">
        select * from clazz,student where clazz.ClazzNo=#{ClazzNo} and student.ClazzNo=clazz.ClazzNo
    select>
mapper>

注:学生中的Clazz属性不应再获取,否则会栈溢出。
MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.ClazzMapper.selectClazzMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectClazzMapper mapper=session.getMapper(selectClazzMapper.class);
        System.out.println(mapper.getClazzByClazzNo(301));
    }
}

MyBatis基础入门教程_第15张图片

3.2 使用分步查询的方式进行处理

3.2.1 先写通过ClazzNo获取所有Student对象
接口方法

package org.example.StudentMapper;

import org.example.Entity.Student;

import java.util.List;

public interface selectStudentMapper {
    /*
    * 根据班级号查询学生
    * */
    List<Student> getStudentByClazzNo(Integer ClazzNo);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
    resultMap>
    
    <select id="getStudentByClazzNo" resultMap="StudentResultMap">
        select * from student where student.ClazzNo=#{ClazzNo}
    select>
mapper>

3.2.2 再通过班级序号获取Clazz对象
接口方法

package org.example.ClazzMapper;

import org.example.Entity.Clazz;

public interface selectClazzMapper {
    /*
    * 根据ClazzNo查询班级信息
    * */
    Clazz getClazzByClazzNo(int ClazzNo);
}

selectClazzMapper.xml



<mapper namespace="org.example.ClazzMapper.selectClazzMapper">
    <resultMap id="getClazzByClazzNo" type="org.example.Entity.Clazz">
        <id property="ClazzNo" column="ClazzNo"/>
        <result property="ClazzName" column="ClazzName"/>
        
        <collection property="students" select="org.example.StudentMapper.selectStudentMapper.getStudentByClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getClazzByClazzNo" resultMap="getClazzByClazzNo">
        select * from clazz where clazz.ClazzNo=#{ClazzNo}
    select>
mapper>

3.2.3 测试类

import org.apache.ibatis.session.SqlSession;
import org.example.ClazzMapper.selectClazzMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectClazzMapper mapper=session.getMapper(selectClazzMapper.class);
        System.out.println(mapper.getClazzByClazzNo(301));
    }
}

MyBatis基础入门教程_第16张图片

③别名映射的实现

  在映射文件当中,往往需要使用类的全类名,这是非常繁琐的,事实上,可定义别名来简化代码。

1.别名的定义
<typeAliases>
    
    <typeAlias  alias="Course" type="org.example.Entity.Course"/>
typeAliases>

注:别名的作用范围是全局的,需要在核心配置文件当中配置。
  我们可以为整个包下的类定义别名,别名默认为类的首字母小写之后的字符串。

<typeAliases>
    <package name="org.example.Entity"/>
typeAliases>
2.MyBatis定义的别名

  在我们的java中已经自己定义了一些常用的数据类型,比如int,long,string等,所以在Mybatis中已经自己嵌入了这些的别名,我们只需要使用的时候来查询一下就可以很好的使用了。
MyBatis基础入门教程_第17张图片

④动态SQL

  MyBatis框架的动态SQL技术是一种根据特定条件动态拼接SQL语句的功能,其存在的意义是为了解决拼接SQL语句字符串时的痛点问题,然而动态SQL有时候在执行性能 (效率)上面不如静态SQL,而且使用不恰当,往往会在安全方面存在隐患 (SQL 注入式攻击)。
作用:让我们可以在xml映射文件当中以标签的形式编写动态SQL语句,完成逻辑判断和动态拼接 sql 的功能。
原理:根据条件判断语句来动态拼接SQL字符串。
动态SQL标签

元素 作用
if 单条件分支判断
where 动态生成where
choose(when、otherwise) 多条件分支判断,相当于switch-case语句,只会选择满足条件中的一个
trim 用于处理一些SQL前后缀拼装问题
foreach 用于对集合进行遍历。
set 动态生成set
sql(include) 用于抽取保存的SQL语句
1.if标签

语法格式

SQL语句
<if test="判断条件">
	要拼接的SQL语句
</if>

注意点
  在使用标签的过程中,由于不确定查询参数是否存在,许多人会使用类似于where 1 = 1 来作为前缀,然后后面用and拼接要查询的参数,这样,就算要查询的参数为空,也能够正确执行查询。若不加上这一条件,当第一条判断不成立而后续判断成立时(第一条拼接的SQL需要加上and,而其余拼接的SQL不需要加上and),会多出一个and,如:

SELECT * FROM student where and SName=#{SName}

而当所有都不成立时会多出一个where语句:

SELECT * FROM student where

这就需要加上一个where 1=1 的条件,来避免以上两种情况的出现,此时就会查询整张表:

SELECT * FROM student where 1=1;

事实上,后续可使用标签来解决。

案例:传入Student实体类对象,在数据库中查找与之有相同属性的Student对象。
selectStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;

import java.util.List;

public interface selectStudentMapper {
    /*
    * 根据If条件筛选出符合条件的学生
    * */
    List<Student> getStudentByIfCondition(Student student);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentByIfCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
        select * from student where 1=1
            <if test="SNo!=null">
                and SNo=#{SNo}
            
            if>
            <if test="SName!=null and SName !=''">
                and SName=#{SName}
            if>
            <if test="SAge!=null">
                and SAge=#{SAge}
            if>
            <if test="SBirthday!=null">
                and SBirthday=#{SBirthday}
            if>
            <if test="ClazzNo!=null">
                and ClazzNo=#{ClazzNo}
            if>
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        String str="2004-03-12 15:34:13";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date Birthday=simpleDateFormat.parse(str);
        Student student=new Student(2,null,19,Birthday,null,null);
        //提供学生的SNo、SAge、SBirthday来查询符合条件的所有学生信息
        System.out.println(mapper.getStudentByIfCondition(student));
    }
}

但是当所有条件都为null,并将1=1的条件去掉时,会出现:

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        String str="2004-03-12 15:34:13";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date Birthday=simpleDateFormat.parse(str);
        Student student=new Student(null,null,null,null,null,null);
        System.out.println(mapper.getStudentByIfCondition(student));
    }
}

MyBatis基础入门教程_第18张图片
此时,SQL语句出错。

2.where标签

  在前面的if标签中,需要加上1=1的本质问题是and标签的冗余,且where关键字在没有任何条件时仍然存在,而标签就很好解决了这个问题。
  标签和共同使用,当失败后, 关键字只会去掉库表字段赋值前面的and,不会去掉语句后面的and关键字,即注意, 只会去掉 语句中的最开始的and关键字。
  且,当只有当中有返回值时,才会插入where关键字,且,当标签返回的SQL字段是以andor开头时,它也会自动剔除。相当于:

<trim prefix="WHERE" prefixOverrides="AND | OR">
 ...
trim>

案例:



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentById" resultMap="StudentResultMap">
        select * from student where student.SNo=#{SNo};
    select>
    
    <select id="getStudentByClazzNo" resultMap="StudentResultMap">
        select * from student where student.ClazzNo=#{ClazzNo}
    select>
    
    <select id="getStudentByIfCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
        select * from student
        <where>
            <if test="SNo!=null">
                and SNo=#{SNo}
            
            if>
            <if test="SName!=null and SName !=''">
                and SName=#{SName}
            if>
            <if test="SAge!=null">
                and SAge=#{SAge}
            if>
            <if test="SBirthday!=null">
                and SBirthday=#{SBirthday}
            if>
            <if test="ClazzNo!=null">
                and ClazzNo=#{ClazzNo}
            if>
        where>
    select>
mapper>
3.choose标签

  属于一整套标签,相当于switch-case语句,最多只会拼接其中的一条SQL语句。
语法格式

<choose>
	<when test="条件判断语句1">
		要拼接的SQL1
	when>
	<when test="条件判断语句2">
		要拼接的SQL2
	when>
	<otherwise>
		
		要拼接的SQL3
	otherwise>
choose>

案例:传入一个实体类对象,按照SNo、SName、SBirthday、SAge、ClazzNo其中一个条件查找学生,当不传入条件时,返回SNo=1的学生对象。
selectStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;
import java.util.List;

public interface selectStudentMapper {
    /*
    * 根据choose|when 按照SNo、SName、SBirthday、SAge、ClazzNo其中一个条件查找学生,当不传入条件时,返回SNo=1的学生对象.
    * */
    List<Student> getStudentByChooseCondition(Student student);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentByChooseCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
        select * from student
        <where>
            <choose>
                <when test="SNo!=null">
                    and SNo=#{SNo}
                when>
                <when test="SName!=null and SName!=''">
                    and SName=#{SName}
                when>
                <when test="SAge!=null">
                    and SAge=#{SAge}
                when>
                <when test="SBirthday!=null">
                    and SBirthday=#{SBirthday}
                when>
                <when test="ClazzNo!=null">
                    and ClazzNo=#{ClazzNo}
                when>
                <otherwise>
                    and SNo=1;
                otherwise>
            choose>
        where>
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        Student student=new Student(null,null,18,null,null,null);
        System.out.println(mapper.getStudentByChooseCondition(student));
    }
}

运行结果
MyBatis基础入门教程_第19张图片

4.trim标签

  一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 whereset以及values( 等前缀,或者添加)等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
作用:当中有返回语句时,进行字符拼接处理(无返回语句则不执行)。
常见属性

属性 说明
prefix 给SQL语句拼接的前缀。
suffix 给SQL语句拼接的后缀。
prefixOverrides 去掉SQL语句前面指定的前缀。
suffixOverrides 去掉SQL语句后面指定的后缀。

案例1:trim实现功能,解决上文中提到的存在的问题。



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentByIfCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
        select * from student
        <trim prefix="where" prefixOverrides="and">
            <if test="SNo!=null">
                and SNo=#{SNo}
            
            if>
            <if test="SName!=null and SName !=''">
                and SName=#{SName}
            if>
            <if test="SAge!=null">
                and SAge=#{SAge}
            if>
            <if test="SBirthday!=null">
                and SBirthday=#{SBirthday}
            if>
            <if test="ClazzNo!=null">
                and ClazzNo=#{ClazzNo}
            if>
        trim>
    select>
mapper>

案例2:去掉多于的逗号
若使用以下mapper映射文件:



<mapper namespace="org.example.StudentMapper.insertStudentMapper">
    
    <insert id="insertByStudent" parameterType="org.example.Entity.Student" useGeneratedKeys="true" keyProperty="SNo">
        insert into student (
                             <if test="SNo!=null">
                                 SNo,
                             if>
                             <if test="SName!=null">
                                 SName,
                             if>
                             <if test="SAge!=null">
                                 SAge,
                             if>
                             <if test="SBirthday!=null">
                                 SBirthday,
                             if>
                             <if test="ClazzNo!=null">
                                 ClazzNo
                             if>
        ) VALUES (
        <if test="SNo!=null">
            #{SNo},
        if>
        <if test="SName!=null">
            #{SName},
        if>
        <if test="SAge!=null">
            #{SAge},
        if>
        <if test="SBirthday!=null">
            #{SBirthday},
        if>
        <if test="ClazzNo!=null">
            #{ClazzNo}
        if>
        )
    insert>
mapper>

若最后的ClazzNo为空,则SQL变为:

insert into student (SNo,SName,SAge,SBirthday,) values(SNo,SName,SAge,SBirthday,)

显然有多于的逗号,故,需要改为:



<mapper namespace="org.example.StudentMapper.insertStudentMapper">
    
    <insert id="insertByStudent" parameterType="org.example.Entity.Student" useGeneratedKeys="true" keyProperty="SNo">
        insert into student
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="SNo!=null">
                    SNo,
                if>
                <if test="SName!=null">
                    SName,
                if>
                <if test="SAge!=null">
                    SAge,
                if>
                <if test="SBirthday!=null">
                    SBirthday,
                if>
                <if test="ClazzNo!=null">
                    ClazzNo
                if>
            trim>
        <trim prefix="values(" suffix=")" suffixOverrides=",">
            <if test="SNo!=null">
                #{SNo},
            if>
            <if test="SName!=null">
                #{SName},
            if>
            <if test="SAge!=null">
                #{SAge},
            if>
            <if test="SBirthday!=null">
                #{SBirthday},
            if>
            <if test="ClazzNo!=null">
                #{ClazzNo}
            if>
        trim>
    insert>
    
mapper>
5.foreach标签

  foreach通常用来对集合的遍历,事实上,你可以传递一个 list 实例、 array 数组或map实例作为参数对象传给 MyBatis。当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中,用名称作为键。

5.1属性说明
属性 说明
collection 指明需要遍历的对象(list、array、map等),list、array对象的collection值默认为"list"与"array",而Map无默认的collection属性值。事实上,可在入参时使用@Param(“keyName”)注解指定collection属性值,此时"list"与"array"会失效。
item 集合元素在被遍历时的别名。
index list、array中是元素的索引,而map中是元素的key值。
open 遍历集合时的开始符号,用于拼接SQL语句。
separator 元素之间的分隔符,用于拼接SQL语句。
close 遍历集合时的结束符号,用于拼接SQL语句。
5.2 遍历单个List对象

selectStudentMapper.java

package org.example.StudentMapper;

import org.apache.ibatis.annotations.Param;
import org.example.Entity.Student;
import java.util.List;

public interface selectStudentMapper {
    /*
    * 传入多个Student对象,根据SNo的值,查找所有Student对象
    * */
    List<Student> getStudentBySNo(@Param("studentsList") List<Student> studentsList);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentBySNo" parameterType="java.util.List" resultMap="StudentResultMap">
        select * from student
        <where>
            <if test="studentsList!=null">
                <foreach collection="studentsList" item="student" open="and SNo in(" separator="," close=")">
                    #{student.SNo}
                foreach>
            if>
        where>
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        Student student1=new Student(1,null,null,null,null,null);
        Student student2=new Student(2,null,null,null,null,null);
        List<Student>list=new ArrayList<>();
        list.add(student1);
        list.add(student2);
        System.out.println(mapper.getStudentBySNo(list));
    }
}

MyBatis基础入门教程_第20张图片

5.3 遍历单个array对象

  注意,这里传入的不是实体类数组。
selectStudentMapper.java

package org.example.StudentMapper;

import org.apache.ibatis.annotations.Param;
import org.example.Entity.Student;
import java.util.List;

public interface selectStudentMapper {
    /*
    * 传入SNo数组,查找所有Student对象
    * */
    List<Student> getStudentBySNo(@Param("studentsArray") Integer[] SNo);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentBySNo" resultMap="StudentResultMap">
        select * from student
        <where>
            <if test="studentsArray!=null">
                <foreach collection="studentsArray" item="studentSNo" open="and SNo in(" separator="," close=")">
                    #{studentSNo}
                foreach>
            if>
        where>
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        Integer[] snoArray=new Integer[2];
        snoArray[0]=1;
        snoArray[1]=2;
        System.out.println(mapper.getStudentBySNo(snoArray));
    }
}

MyBatis基础入门教程_第21张图片

5.3 遍历单个map对象

5.3.1 获取键值对
selectStudentMapper.java

package org.example.StudentMapper;

import org.apache.ibatis.annotations.Param;
import org.example.Entity.Student;
import java.util.List;
import java.util.Map;

public interface selectStudentMapper {
    /*
    * 根据学生姓名、年龄查找学生
    * */
    List<Student> getStudentBySNameAndSAge(@Param("StudentMap")Map<String,Integer>map);
}

selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentBySNameAndSAge" resultMap="StudentResultMap">
        select * from student
            where
                <if test="StudentMap!=null and StudentMap.size()>0">
                    (SName,SAge) in
                    <foreach collection="StudentMap.entrySet()" item="value" index="key" separator="," open="(" close=")">
                        (#{key},#{value})
                    foreach>
                if>
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.util.HashMap;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        HashMap<String,Integer>map=new HashMap<>();
        map.put("张三",18);
        map.put("李四",19);
        System.out.println(mapper.getStudentBySNameAndSAge(map));
    }
}

MyBatis基础入门教程_第22张图片
5.3.2 获取键
selectStudentMapper.xml



<mapper namespace="org.example.StudentMapper.selectStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <select id="getStudentBySNameAndSAge" resultMap="StudentResultMap">
        select * from student
            where
                <if test="StudentMap!=null and StudentMap.size()>0">
                    (SName) in
                    <foreach collection="StudentMap.keySet()" item="key" separator="," open="(" close=")">
                        (#{key})
                    foreach>
                if>
    select>
mapper>
6.set标签

  标签和标签一样,用于动态生成,其用在update语句当中。
作用:在至少有一个子元素返回了SQL语句时,才会向SQL语句中添加SET,并且如果SET之后是以,开头的话,会自动将其删掉,相当于:

<trim prefix="SET" prefixOverrides=",">
...
trim>
7.sql标签与include标签

  在实际开发中会遇到许多相同的SQL,比如根据某个条件筛选,这个筛选很多地方都能用到,我们可以将其抽取出来成为一个公用的部分,这样修改也方便,一旦出现了错误,只需要改这一处便能处处生效了,此时就用到了这个标签了。而标签,用于引用标签定义的常量。
例:

<sql id="selectAll">
    select  *  from  student
sql>
<select id="getStudentBySNo"  resultMap="StudentResultMap">
    <include refid="selectAll"/>
    WHERE 1=1
    <if test="SNo != null">
        AND SNo like #{SNo}
    if>
select>

⑤各种查询功能的实现

  对于MyBatis中的各种查询功能,有:

  • 若查询出的数据只有一条
    • 通过实体类对象接收。
    • 通过List集合接收。
    • 通过Map集合接收,以字段为键,字段所对应值为值。
  • 若查询出的数据有多条:此时不能通过一个实体类对象接收,否则会抛出异常TooManyResultsException。
    • 通过List集合接收。
    • 通过map类型的List集合接收。
    • 通过在mapper接口的方法上添加@MapKey注解,此时可以将每条数据转换的map集合作为值,以某个字段的值作为键,放在同一个map集合中。

  下面以Course实体类为例:

package org.example.Entity;

public class Course {
    private Integer CNo;
    private String CName;
	/*其余是标准JavaBean内容*/
}
1.获取包含所有实体类的集合

mybatis-config.xml


<mapper resource="CourseMapper/selectCourseMapper.xml"/>

selectCourseMapper.java

package org.example.CourseMapper;

import org.example.Entity.Course;
import java.util.List;
import java.util.Map;

public interface selectCourseMapper {
    /*
    * 获取包含所有实体类的集合
    * */
    List<Course> getAllCourse();
}

selectCourseMapper.xml



<mapper namespace="org.example.CourseMapper.selectCourseMapper">
    <resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
        <id property="CNo" column="CNo"/>
        <result property="CName" column="CName"/>
    resultMap>
    
    <select id="getAllCourse" resultMap="CourseResultMap">
        select * from course;
    select>
mapper>
2.主键查询

mybatis-config.xml


<mapper resource="CourseMapper/selectCourseMapper.xml"/>

selectCourseMapper.java

package org.example.CourseMapper;

import org.example.Entity.Course;
import java.util.List;

public interface selectCourseMapper {
    /*
    * 根据CNo查询课程信息
    * */
    List<Course> getCourseByCNo(Integer CNo);
}

selectCourseMapper.xml



<mapper namespace="org.example.CourseMapper.selectCourseMapper">
    <resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
        <id property="CNo" column="CNo"/>
        <result property="CName" column="CName"/>
    resultMap>
    
    <select id="getCourseByCNo" resultMap="CourseResultMap">
        select * from course where CNo=#{CNo}
    select>
mapper>
3.查询总记录数

mybatis-config.xml


<mapper resource="CourseMapper/selectCourseMapper.xml"/>

selectCourseMapper.java

package org.example.CourseMapper;

import org.example.Entity.Course;
import java.util.List;

public interface selectCourseMapper {
    /*
    * 查询课程总数
    * */
    Integer getCountOfCourse();
}

selectCourseMapper.xml



<mapper namespace="org.example.CourseMapper.selectCourseMapper">
    <resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
        <id property="CNo" column="CNo"/>
        <result property="CName" column="CName"/>
    resultMap>
    
    <select id="getCountOfCourse" resultType="java.lang.Integer">
        select count(*) from course;
    select>
mapper>
4.获取单个实体类的字段map映射集合

mybatis-config.xml


<mapper resource="CourseMapper/selectCourseMapper.xml"/>

selectCourseMapper.java

package org.example.CourseMapper;

import org.example.Entity.Course;
import java.util.List;

public interface selectCourseMapper {
    /*
    * 根据CNo获取课程信息的map集合
    * */
    Map<String,Object>getCourseByCNoToMap(Integer CNo);//使用Object作为值类型,便于类型转换.
}

selectCourseMapper.xml



<mapper namespace="org.example.CourseMapper.selectCourseMapper">
    <resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
        <id property="CNo" column="CNo"/>
        <result property="CName" column="CName"/>
    resultMap>
    
    <select id="getCourseByCNoToMap" resultType="java.util.Map">
        select * from course where CNo=#{CNo}
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.CourseMapper.selectCourseMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectCourseMapper mapper=session.getMapper(selectCourseMapper.class);
        System.out.println(mapper.getCourseByCNoToMap(101));
    }
}

MyBatis基础入门教程_第23张图片

5.获取所有实体类的字段map映射集合

mybatis-config.xml


<mapper resource="CourseMapper/selectCourseMapper.xml"/>

selectCourseMapper.java

package org.example.CourseMapper;

import org.example.Entity.Course;
import java.util.List;
import java.util.Map;

public interface selectCourseMapper {
    /*
    * 获取所有课程信息的map集合
    * */
    List<Map<String,Object>>getAllCourseToMap();
}

selectCourseMapper.xml



<mapper namespace="org.example.CourseMapper.selectCourseMapper">
    <resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
        <id property="CNo" column="CNo"/>
        <result property="CName" column="CName"/>
    resultMap>
    
    <select id="getAllCourseToMap" resultType="java.util.Map">
        select * from course
    select>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.CourseMapper.selectCourseMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectCourseMapper mapper=session.getMapper(selectCourseMapper.class);
        System.out.println(mapper.getAllCourseToMap());
    }
}

MyBatis基础入门教程_第24张图片

6.模糊查询

  注意,在模糊查询当中,不能使用#{},例:



<mapper namespace="org.example.mapper.SQLMapper">
    
    <select id="getUserByLike" resultType="User">
        select * from tb_user where username like '%#{username}%';
    select>
mapper>

MyBatis基础入门教程_第25张图片
这是因为#{}的实质是参数注入,而当其处于SQL语句的字符串当中时,?就不会被注入参数,而是被当做字符串常量。

6.1使用${}


<mapper namespace="org.example.mapper.SQLMapper">
    
    <select id="getUserByLike" resultType="User">
        select * from tb_user where username like '%${username}%';
    select>
mapper>
6.2 使用字符串拼接函数concat


<mapper namespace="org.example.mapper.SQLMapper">
    
    <select id="getUserByLike" resultType="User">
        select * from tb_user where username like concat('%',#{username},'%');
    select>
mapper>
6.3 直接拼接


<mapper namespace="org.example.mapper.SQLMapper">
    
    <select id="getUserByLike" resultType="User">
        
        
        select * from tb_user where username like '%'#{username}'%';
    select>
mapper>

⑥各种插入功能的实现

mybaits-config.xml


<mapper resource="StudentMapper/insertStudentMapper.xml"/>
1.插入一个实体类对象并获取自增主键

insertStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;

public interface insertStudentMapper {
    /*
    * 用实体类对象进行插入
    * */
    int insertByStudent(Student student);
}

insertStudentMapper.xml



<mapper namespace="org.example.StudentMapper.insertStudentMapper">
    
    <insert id="insertByStudent" >
        insert into student VALUES (#{SNo},#{SName}, #{SAge}, #{SBirthday},#{ClazzNo})
    insert>
mapper>

MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        insertStudentMapper mapper=session.getMapper(insertStudentMapper.class);
        String str="2004-03-24 23:38:47";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date Birthday=simpleDateFormat.parse(str);
        Student student=new Student(5,"田七",19,Birthday,301,null);
        System.out.println(mapper.insertByStudent(student));
        System.out.println(student.getSNo());
    }
}

这里,是在传入实体类对象时就指定了SNo的值,事实上,由于SNo是自增主键,故可不指定,而是赋值为NULL,且,可设置以下两个属性,使得插入之后,传入的实体类对象本身的SNo也会被赋值:

  • useGeneratedKeys:(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server这样的关系数据库管理系统的自动递增字段, oracle使用序列是不支持的,通过selectKey可以返回主键),默认值:false。
  • keyProperty:(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。

insertStudentMapper.xml



<mapper namespace="org.example.StudentMapper.insertStudentMapper">
    
    <insert id="insertByStudent" useGeneratedKeys="true" keyProperty="SNo">
        insert into student VALUES (null,#{SName}, #{SAge}, #{SBirthday},#{ClazzNo})
    insert>
mapper>

测试代码中的实体类SNo只需要赋值为null即可:

SqlSession session= SqlSessionUtils.getSqlSession();
insertStudentMapper mapper=session.getMapper(insertStudentMapper.class);
String str="2004-03-24 23:38:47";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
Student student=new Student(null,"田七",19,Birthday,301,null);
mapper.insertByStudent(student)
2.集合方式批量插入

insertStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;
import java.util.List;

public interface insertStudentMapper {
    /*
    * 使用集合进行批量插入
    * */
    Integer insertByStudentList(List<Student> studentList);
}

insertStudentMapper.xml



<mapper namespace="org.example.StudentMapper.insertStudentMapper">
    
    <insert id="insertByStudentList" useGeneratedKeys="true" keyProperty="SNo">
        insert into student(SNo, SName, SAge, SBirthday, ClazzNo)
            values
                <foreach collection="studentList" separator="," item="student">
                    (null,#{student.SName},#{student.SAge},#{student.SBirthday},#{student.ClazzNo})
                foreach>
    insert>
mapper>

⑦各种修改功能的实现

1.全字段修改

updateStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;

public interface updateStudentMapper {
    /*
    * 根据SNo进行全字段修改
    * */
    Integer updateStudentBySNo(Student student);
}

updateStudentMapper.xml



<mapper namespace="org.example.StudentMapper.updateStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <update id="updateStudentBySNo">
        update student
        <set>
            SName=#{SNAme},SBirthday=#{SBirthday},SAge=#{SAge},ClazzNo=#{ClazzNo}
        set>
        where SNo=#{SNo}
    update>
mapper>
1.动态修改

updateStudentMapper.java

package org.example.StudentMapper;

import org.example.Entity.Student;

public interface updateStudentMapper {
    /*
    * 根据SNo进行动态修改
    * */
    Integer dynamicUpdateStudentBySNo(Student student);
}

updateStudentMapper.xml



<mapper namespace="org.example.StudentMapper.updateStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <update id="updateStudentBySNo">
        update student
        <set>
            <if test="SName!=null and SName!=''">
                SName=#{SNAme},
            if>
            <if test="SBirthday!=null">
                SBirthday=#{SBirthday},
            if>
            <if test="SAge!=null">
                SAge=#{SAge},
            if>
            <if test="ClazzNo!=null">
                ClazzNo=#{ClazzNo}
            if>
        set>
        where SNo=#{SNo}
    update>
mapper>

⑧各种删除功能的实现

1.删除单个对象

deleteStudentMapper.java

package org.example.StudentMapper;

public interface deleteStudentMapper {
    /*
    * 根据SNo删除Student对象
    * */
    Integer deleteStudentBySNo(Integer SNo);
}

deleteStudentMapper.xml



<mapper namespace="org.example.StudentMapper.deleteStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <delete id="deleteStudentBySNo">
        delete from student where SNo=#{SNo}
    delete>
mapper>
2.批量删除

deleteStudentMapper.java

package org.example.StudentMapper;

import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface deleteStudentMapper {
    /*
    * 根据SNo列表进行批量删除
    * */
    Integer deleteStudentBySNoList(@Param("SNoList") List<Integer> SNoList);
}

deleteStudentMapper.xml



<mapper namespace="org.example.StudentMapper.deleteStudentMapper">
    <resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
        <id property="SNo" column="SNo"/>
        <result property="SName" column="SName"/>
        <result property="SBirthday" column="SBirthday"/>
        <result property="SAge" column="SAge"/>
        <result property="ClazzNo" column="ClazzNo"/>
    resultMap>
    
    <delete id="deleteStudentBySNoList">
        delete from student where SNo in (
            <foreach collection="SNoList" item="SNo" separator=",">
                #{SNo}
            foreach>
            )
    delete>
mapper>

四、MyBatis缓存

  所有的查询都要连接数据库,而连接数据库需要耗费资源,如果能将一次查询的结果暂存到一个可以直接取到的地方,使得再次查询相同数据时直接走缓存就不用走数据库了,这就是缓存(Cache)(注,缓存只针对查,而不针对增删改)。即,缓存数据,是存在内存中的临时数据,将用户经常查询的数据放在缓存(内存)中,用户去查询数据时就不用从磁盘上查询,而是从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题,且,缓存的数据一般是经常查询并且不经常改变的数据。
  MyBatis包含一个非常强大的查询缓存特性,可以非常方便地定制和配置缓存,并极大提高查询效率。其默认定义了两级缓存:一级缓存和二级缓存。
MyBatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序以及查出来的数据,可以拿来直接使用。
  • 如果二级缓存没有命中,再查询一级缓存。
  • 如果一级缓存也没有命中,则查询数据库。
  • SqlSession关闭后,一级缓存中的数据会写入二级缓存。

(一)一级缓存

  一级缓存是默认开启的,其范围是SqlSession级别,当使用SqlSession查询数据时,如果下一次再使用相同的SqlSession进行查询时,就会直接从缓存中取数据,如果缓存中没有相应数据才从数据库中取数据。
例:
MyBatisTest.java

import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        System.out.println(mapper.getStudentById(1));
        System.out.println(mapper.getStudentById(1));
    }
}

MyBatis基础入门教程_第26张图片
可见,第二次查询并未执行SQL语句,亦并未连接数据库。

①缓存失效的四种情况

使一级缓存失效的四种情况:

1.使用不同的SqlSession

 &emps;此时会建立两次连接。

import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session1= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper1=session1.getMapper(selectStudentMapper.class);
        SqlSession session2= SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper2=session1.getMapper(selectStudentMapper.class);
        System.out.println(mapper1.getStudentById(1));
        System.out.println(mapper2.getStudentById(1));
    }
}

MyBatis基础入门教程_第27张图片

2.同一个SqlSession查询条件不同

  此时会两次进入数据库查询。

import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session=SqlSessionUtils.getSqlSession();
        selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
        System.out.println(mapper.getStudentById(1));
        System.out.println(mapper.getStudentById(2));
    }
}

MyBatis基础入门教程_第28张图片

3.同一个SqlSession两次查询期间执行了任何一次增删改的操作
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBatisTest {
    @Test
    public void test() throws Exception{
        SqlSession session=SqlSessionUtils.getSqlSession();
        selectStudentMapper selectStudentMapper=session.getMapper(selectStudentMapper.class);
        insertStudentMapper insertStudentMapper=session.getMapper(org.example.StudentMapper.insertStudentMapper.class);
        String str="2004-03-24 23:38:47";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date Birthday=simpleDateFormat.parse(str);
        System.out.println(selectStudentMapper.getStudentById(1));
        insertStudentMapper.insertByStudent(new Student(null,"田七",20,Birthday,301,null));
        System.out.println(selectStudentMapper.getStudentById(2));
    }
}

MyBatis基础入门教程_第29张图片

4.同一个SqlSession两次查询期间手动清空了缓存
SqlSession session=SqlSessionUtils.getSqlSession();
session.clearCache();

②一级缓存的清空

  一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
  一级缓存时执行commitclose,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(updatedeleteinsert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。
  事实上,MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象,见下面代码。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

(二)二级缓存

  二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果是会被缓存的,而过再次执行相同的查询语句,结果就会从缓冲中获取,且,二级缓存需要手动开启。
注:二级缓存只有在一级缓存死掉才可以执行。

①二级缓存开启的三个条件

1.核心配置文件
<settings>
	<setting name="cacheEnabled" value="true"/>
settings>
2.Mapper映射文件

  在mapper.xml(sql映射文件中)的mapper标签中添加 标签。
MyBatis基础入门教程_第30张图片

3.修改标签语句

	<select id="getStudentByClazzNo" resultMap="StudentResultMap" useCache="true">
		select * from student where student.ClazzNo=#{ClazzNo}
	select>

②二级缓失效的情况与相关配置

  两次查询之间执行了任意的增删改,会使一、二级缓存同时失效。
在mapper配置文件中添加的cache标签可以设置一些属性:

  • eviction属性:缓存回收策略(默认是LRU)。
    • LRU(Least Recently Used):最近最少使用的:移除最长时间不被使用的对象。
    • FIFO(First in First out):先进先出,按对象进入缓存的顺序来移除它们。
    • SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象。
    • WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
  • flushinterval属性:刷新间隔,单位为毫秒(默认情况下是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新)。
  • size属性:引用数目,正整数(代表缓存最多可以存储多少个对象,太大容易导致内存溢出)。
  • readOnly属性:只读,true/false。
    • true:只读缓存,会给所有调用者返回缓存对象的相同实例,一次这些对象不能被修改,这提供了很重要的性能优势。
    • false:读写缓存,会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此默认是false。

五、逆向工程

正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表,Hibernate支持正向工程。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成以下资源:

  • java实体类
  • Mapper接口
  • Mapper映射文件

①配置Maven插件

<build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generatorgroupId>
                <artifactId>mybatis-generator-maven-pluginartifactId>
                <version>1.3.7version>
                <configuration>
                    
                    <verbose>trueverbose>
                    <overwrite>trueoverwrite>
                configuration>
            plugin>
        plugins>
    build>

②创建配置文件

配置文件模板:




<generatorConfiguration>
    
    <properties resource="properties/xx.properties">properties>

    
    <classPathEntry location="C:\Users\Vergi\.m2\repository\mysql\mysql-connector-java\8.0.11\mysql-connector-java-8.0.11.jar" />
    
    
    
    <context id="default" targetRuntime="MyBatis3">
        
        <commentGenerator>
            <property name="suppressDate" value="true" />
            
            <property name="suppressAllComments" value="true" />
        commentGenerator>

        
        <jdbcConnection driverClass="${db.driver}"
                        connectionURL="${db.url}"
                        userId="${db.user}"
                        password="${db.password}">
        jdbcConnection>


        
        <javaTypeResolver>
            
            
            
            <property name="forceBigDecimals" value="false" />
        javaTypeResolver>

        
        
        <javaModelGenerator targetPackage="mybatis.generator.model"
                            targetProject=".\src\main\java">
            
            <property name="enableSubPackages" value="false" />
            
            <property name="trimStrings" value="true" />
        javaModelGenerator>

        
        <sqlMapGenerator targetPackage="mybatis.generator.mappers"
                         targetProject=".\src\main\resources">
            
            <property name="enableSubPackages" value="false" />
        sqlMapGenerator>

        
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="mybatis.generator.dao" targetProject=".\src\main\java">
            
            <property name="enableSubPackages" value="false" />
        javaClientGenerator>
        
        <table tableName="student"/>
        <table tableName="product"/>
    context>
generatorConfiguration>

③使用插件

MyBatis基础入门教程_第31张图片

六、注解开发

  留个小坑,太忙了,先推进度,有空再写。

你可能感兴趣的:(后端开发,mybatis,java,数据库,intellij-idea,后端)