Mybatis

自己学习Mybatis做的记录

目录

一、MyBatis概述

1.1 框架

1.2 三层架构

1.3 JDBC的不足

1.4 了解MyBatis

二、MyBatis入门

2.0 MyBatis架构

2.0.1 整体架构

2.0.2 原理分析

2.1 版本

2.2 MyBatis下载

2.3 MyBatis入门程序开发步骤

写代码前准备

编写mybatis核心配置文件

编写SQL映射文件

加载映射文件

编写Mybatis程序

2.3.1 第一个MyBatis程序的细节

2.3.2 MyBatis的事务管理机制

2.5 MyBatis第一个比较完整的代码写法

2.7 引入日志框架logback

2.8 MyBatis工具类SqlSessionUtil的封装

2.9 映射文件中Mapper标签的namespace属性

四、MyBatis的核心配置文件详解

4.1 environment

4.2 transactionManager

4.3 dataSource

4.4 .properties属性文件

4.5 mappers标签

三、使用MyBatis完成CRUD

3.1 insert(Create)

3.2 delete(Delete)

3.3 update(Update)

3.4 select(Retrieve)

查询一条记录

查询多条数据

3.5 总结

parameterType和resultType

通用总结

六、在WEB中应用MyBatis(使用MVC架构模式)

七、javassist

八、MyBatis中的接口代理机制

8.1 Mybatis接口代理机制的理解

8.2 接口代理机制的开发规范

8.3 接口代理机制实例(005-crud2)

九、总结Mybatis中DAO(Mapper)的开发方式

十、Mybatis核心配置文件的剩余标签

十一、#{}和${}的区别

使用#{}

使用${}

什么情况下必须使用${}

向SQL语句中拼接表名

批量删除

模糊查询

十二、输入参数映射和输出结果映射专题

十三、动态SQL拼接

十四、Mybatis关联查询

十五、Mybatis的延迟加载

十六、Mybatis的缓存

十七、Mybatis使用PageHelper分页插件

十八、Mybatis注解式开发


在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、navicat),来操作数据库,但我们做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库,现在主流的方式是:Mybatis。

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

一、MyBatis概述

1.1 框架

  • 在文献中看到的framework被翻译为框架

  • Java常用框架:

    • SSM三大框架:Spring + SpringMVC + MyBatis

    • SpringBoot

    • SpringCloud

    • 等。。

  • 框架其实就是对通用代码的封装,提前写好的一堆接口和类,我们可以在做项目的时候直接引入框架,基于这些现有的接口和类进行开发,可以大大提高开发效率。

  • 框架一般都以jar包的形式存在。(jar包中有class文件以及各种配置文件等。)

  • SSM三大框架的学习顺序:

    • 方式一:MyBatis、Spring、SpringMVC(建议)

1.2 三层架构

  • 表现层(UI):直接跟前端打交互(一是接收前端ajax请求,二是返回json数据给前端)

  • 业务逻辑层(BLL):一是处理表现层转发过来的前端请求(也就是具体业务),二是将从持久层获 取的数据返回到表现层

  • 数据访问层(DAL):操作数据库完成数据库表的CRUD,并将获得的数据返回到业务逻辑层 层。

  • Java持久层的框架:

    • MyBatis(其实就是对JDBC进行了封装)

    • Hibernate(实现了JPA规范)

    • jOOQ

    • Guzz

    • Spring Data(实现了JPA规范)

    • ActiveJDBC

    • ......

1.3 JDBC的不足

  • 示例代码1:

// ......
// sql语句写死在java程序中,将来可能有需求将会修改这些java程序,违背OCP原则
String sql = "insert into t_user(id,idCard,username,password,birth,gender,
email,city,street,zipcode,phone,grade) values(?,?,?,?,?,?,?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// JDBC代码繁琐的赋值:思考一下,这种有规律的代码能不能通过反射机制来做自动化。
ps.setString(1, "1");
ps.setString(2, "123456789");
ps.setString(3, "zhangsan");
ps.setString(4, "123456");
ps.setString(5, "1980-10-11");
ps.setString(6, "男");
ps.setString(7, "zhangsan@126.com");
ps.setString(8, "北京");
ps.setString(9, "大兴区凉水河二街");
ps.setString(10, "1000000");
ps.setString(11, "16398574152");
ps.setString(12, "A");
// 执行SQL
int count = ps.executeUpdate();
// ......

示例代码2:

// ......
// sql语句写死在java程序中
String sql = "select id,idCard,username,password,birth,gender,email,city,s
treet,zipcode,phone,grade from t_user";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
List<User> userList = new ArrayList<>();
// 思考以下循环中的所有代码是否可以使用反射进行自动化封装。
while(rs.next()){
// 获取数据
String id = rs.getString("id");
String idCard = rs.getString("idCard");
String username = rs.getString("username");
String password = rs.getString("password");
String birth = rs.getString("birth");
String gender = rs.getString("gender");
String email = rs.getString("email");
String city = rs.getString("city");
String street = rs.getString("street");
String zipcode = rs.getString("zipcode");
String phone = rs.getString("phone");
String grade = rs.getString("grade");
// 创建对象
User user = new User();
// 给对象属性赋值
user.setId(id);
user.setIdCard(idCard);
user.setUsername(username);
user.setPassword(password);
user.setBirth(birth);
user.setGender(gender);
user.setEmail(email);
user.setCity(city);
user.setStreet(street);
user.setZipcode(zipcode);
user.setPhone(phone);
user.setGrade(grade);
// 添加到集合
userList.add(user);
}
// ......
  • JDBC不足:

    • SQL语句写死在Java程序中,不灵活,改SQL的话就要改Java代码,违背开闭原则OCP。

    • 给?传值繁琐。

    • 将查询结果集封装成Java对象是繁琐的。

1.4 了解MyBatis

  • MyBatis本质上是对JDBC的封装,使开发者只需要关注 SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等JDBC繁杂的过程代码,通过MyBatis完成数据库表的CRUD。

  • MyBatis通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 Java 对象和statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由MyBatis框架执行sql并将结果映射成Java对象并返回。(在Mybati中的statement是要执行的SQL)

  • MyBatis在三层架构中负责持久层的,属于持久层框架。

  • MyBatis的发展历程:【引用百度百科】

    • MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁 移到了google code,并且改名为MyBatis。2013年11月迁移到Github。

    • iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的 持久层框架包括SQL Maps和Data Access Objects(DAOs)。

  • 打开mybatis代码可以看到它的包结构中包含:ibatis

  • ORM:对象关系映射(其实是一种思想)
    • O(Object):Java对象

    • R(Relational):关系型数据库

    • M(Mapping):Java对象映射成(转换成)数据库表中的一行记录,或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象。

    • ORM图示:

    • MyBatis属于半自动化的ORM框架。

    • Hibernate属于全自动化的ORM框架。

  • MyBatis框架特点:

    • 支持定制化的SQL、存储过程、基本映射以及高级映射

    • 几乎避免了所有JDBC中要手动设置参数以及获取结果集的代码

    • 支持XML开发,也支持注解式开发。【为了保证sql语句的灵活,所以mybatis大部分是采用 XML方式开发。】

    • 将接口和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的 记录

    • 体积小好学:两个jar包,两个XML配置文件。

    • 完全做到sql解耦合。

    • 提供了基本映射标签。

    • 提供了高级映射标签。

    • 提供了XML标签,支持动态SQL的编写。

    • .....

二、MyBatis入门

2.0 MyBatis架构

2.0.1 整体架构

1、MyBatis配置:

  • mybatis-config.xml,此文件作为MyBatis的全局(核心)配置文件,配置了MyBatis的运行环境等信息。

  • mapper.xml文件即SQL映射文件,该文件中配置了操作数据库的sql语句,此文件需要在mybatis-config.xml中加载。

2、通过MyBatis环境等配置信息构造SqlSessionFactory,即会话工厂。

3、由会话工厂创建SqlSession即会话,SqlSession对象中有很多操作数据库CRUD的方法,SqlSession是对Connection对象的封装。

4、MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

5、MappedStatement也是MyBatis一个底层封装对象,Mybatis将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。在mapper.xml文件中一个标签+sql语句就是一个MappedStatement对象,sql语句用到的参数将通过Executor执行器传过来,sql的id即是MappedStatement的id。

6、MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、字符串类型、实体类类型,Executor在MappedStatement执行sql前将输入的Java对象映射至sql中,输入参数映射就是 JDBC编程中对PreparedStatement设置参数。

7、MappedStatement对sql执行输出结果进行定义包括HashMap、基本类型、字符串类型、实体类类型,Executor在MappedStatement执行sql后将输出结果映射至Java对象中,输出结果映射过程 相当于JDBC编程中对结果的解析处理过程。

2.0.2 原理分析

  1. 加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加 载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配 置),存储在内存中。

  2. SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、 JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根 据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。

  3. SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。

  4. 结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者 基本数据类型,并将最终结果返回。

2.1 版本

  • 软件版本:

    • IntelliJ IDEA:2022.1.4

    • Navicat for MySQL:16.0.14

    • MySQL数据库:8.0.30

  • 组件版本:

    • MySQL驱动:8.0.30

    • MyBatis:3.5.10

    • JDK:Java17

    • JUnit:4.13.2

    • Logback:1.2.11

2.2 MyBatis下载

将框架以及框架的源码都下载下来,下载框架后解压,打开mybatis目录

  • 通过以上解压可以看到,框架一般都是以jar包的形式存在。我们的mybatis课程使用maven,所 以这个jar我们不需要。

  • 官方手册需要

2.3 MyBatis入门程序开发步骤

写代码前准备

  • 准备数据库表:汽车表t_car,字段包括:

    • id:主键(自增)【bigint】

    • car_num:汽车编号【varchar】

    • brand:品牌【varchar】

    • guide_price:厂家指导价【decimal类型,专门为财务数据准备的类型】

    • produce_time:生产时间【char,年月日即可,10个长度,'2022-10-11'】

    • car_type:汽车类型(燃油车、电车、氢能源)【varchar】

  • 使用navicat for mysql工具建表

  • 使用navicat for mysql工具向t_car表中插入两条数据,如下:

  • 创建Project:建议创建Empty Project,设置Java版本以及编译版本等。

  • 在IDEA中集成maven

  • 创建Module:创建普通的Maven Java模块

  • 步骤1:在pom.xml文件中设置此项目的打包方式为:jar(不需要war,因为mybatis封装的是jdbc。)
pom.xml

<!--    当前项目的坐标-->
    <groupId>org.example</groupId>
    <artifactId>mybatis-001</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
  • 步骤2:在pom.xml文件中引入需要的依赖jar包(mybatis依赖 + mysql驱动依赖)

pom.xml

<!--    引入mybatis的依赖-->
    <dependencies>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
<!--        引入mysql驱动的依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
      </dependency>

    </dependencies>

编写mybatis核心配置文件

  • 步骤3:在resources根目录下 创建mybatis的核心配置文件mybatis-config.xml,这个文件主要配置连接数据库的信息、加载SQL映射文件等(可以参考mybatis手册拷贝进来)

    • 注意1:其文件名可以自定义,但建议mybatis-config.xml

    • 注意2:mybatis核心配置文件存放的位置也是随意的。这里选择放在resources目录下,相当于放到了类的根路径下。(resources目录相当于类的根路径)

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 文件的根节点 -->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 四个property标签:配置连接数据库的信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/bjpowernode"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--在mapper标签中配置XxxMapper.xml SQL映射文件的路径-->
        <!--resource属性:指定从类的根路径下开始查找资源-->
        <mapper resource="指定SQL映射文件的路径"/>
    </mappers>
</configuration>

编写SQL映射文件

  • 步骤4:在resources根目录下创建mapper目录,在该目录下创建 编写sql语句的sql映射文件(XxxMapper.xml),一般一张数据库表对应一个,一张数据库表对应一个实体类,一张表对应一个Mapper接口

    • 注意1:sql语句最后结尾可以不写“;”

    • 注意2:CarMapper.xml文件的名字不固定,可以自定义。

    • 注意3:CarMapper.xml文件的位置也是随意的。这里选择放在resources根下,相当于放到了类的根路径下。

CarMapper.xml


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

<!--在mapper根标签中:使用对应的标签编写对应的sql语句,namespace先随意写一个-->
<mapper namespace="car">
    
    <!--用insert标签写insert语句:插入一条汽车信息,用其id属性指定包裹的这条sql语句的id值,id是唯一标识-->
    <insert id="insertCar">
        insert into t_car
            (id,car_num,brand,guide_price,produce_time,car_type) 
        values
            (null,'102','丰田mirai',40.30,'2014-10-05','氢能源')
    </insert>
    
</mapper>

加载映射文件

  • 步骤5:加载映射文件,此时Mybatis还不知道Mapper文件的存在,所以需要将 mapper映射文件的路径 配置到 mybatis-config.xml核心配置文件中(后续有新方法):

  • <!-- 加载映射文件的位置 -->
    <mappers>
        <mapper resource="mapper/CarMapper.xml"/>
    </mappers>

编写Mybatis程序

  • 步骤6:编写MyBatis程序(MyBatisIntroductionTest代码)

    • 在MyBatis中有一个专门执行sql语句的对象叫SqlSession,它是java程序和数据库之间的一次会话。

      • 要获取SqlSession对象先要获取SqlSessionFactory对象,通过它来生产SqlSession对象(一般SqlSessionFactory对象对应一个数据库)

      • 要获取SqlSessionFactory对象又要通过SqlSessionFactoryBuilder对象的build方法来获取一个SqlSessionFactory对象

      • 总结mybatis的核心对象包括:SqlSessionFactoryBuilder对象->SqlSessionFactory对象->SqlSession对象

MyBatisIntroductionTest.java


package org.example.mybatis.test;

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.InputStream;

public class MyBatisTest {
    public static void main(String[] args) throws Exception {
        //1.创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
		
        //创建读取Mybatis核心配置文件的流
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); 
        
        //2.获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);

        //3.获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //4.通过SqlSession对象调用对应的方法执行 指定的sql语句
        //方法传入要执行的sql语句的id值,返回值是影响数据库表中的记录数
        //(4.后续:通过Mapper接口的代理类实例调用其方法执行 指定的 sql语句)
        int count = sqlSession.insert("insertCar");  
        System.out.println("插入了几条记录" + count);

        //5.手动提交事务
        sqlSession.commit();
        
        // 6.关闭资源(只关闭是不会提交的)
        sqlSession.close();   //底层分析:如果使用的事务管理器是JDBC,那么底层实际上还是会执行connection.commit()

    }
}

注意1:MyBatis默认采用的事务管理器是:JDBC。JDBC事务默认是不提交的,需要手动提交。

  • 步骤6:运行程序,查看运行结果,以及数据库表中的数据

2.3.1 第一个MyBatis程序的细节

小技巧:凡是遇到resources这个单词,大部分情况下,加载资源的方式都是从类的根路径下开始加载资源

优点:从类路径下开始加载资源的项目 移植性很强,比如从windows移植到Linux下,代码不用修改,因为这个资源一直在类路径下

2.3.2 MyBatis的事务管理机制

为true,表示没有开启事务

2.5 MyBatis第一个比较完整的代码写法

MyBatisCompleteTest.java

package org.example.mybatis.test;

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;

/**
 * 一个完整的MyBatis程序
 */
public class MyBatisCompleteTest {
    public static void main(String[] args) {
        SqlSession sqlSession = null;
        try {
            //1.获取SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

            //2.获取SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));

            //3.开启会话,获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession();

            //4.执行SQL语句
            int count = sqlSession.insert("insertCar");
            System.out.println(count);

            //5.提交事务
            sqlSession.commit();
        } catch (Exception e) {
            //如果有异常就回滚事务
            if (sqlSession != null){
                sqlSession.rollback();
            }
            e.printStackTrace();
        }finally{
            //关闭会话
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

2.7 引入日志框架logback

下图了解即可,不用纠结。

  • 引入日志框架的目的:看清楚mybatis执行的sql语句。

  • 启用标准日志组件:在mybatis-config.xml文件中添加以下配置:【可参考mybatis手册】

  • 标准日志也可以用,但是配置不够灵活,可以集成其他的日志组件,例如:log4j,logback等。

  • logback是目前日志框架中性能较好的,较流行的,这个是用集成的方式。

  • 使用logback日志组件的步骤:

    • 第一步:引入logback相关依赖

    • 第二步:引入logback所必须的xml配置文件

      • 文件名必须叫做logback.xml或logback-test.xml

      • 必须放到类路径中

      • 主要配置日志输出相关的级别以及日志具体的格式

    • logback.xml
      
      
      <?xml version="1.0" encoding="UTF-8"?>
      
      <configuration debug="false">
          <!-- 控制台输出 -->
          <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
              </encoder>
          </appender>
          <!-- 按照每天生成日志文件 -->
          <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
              <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <!--日志文件输出的文件名-->
                  <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
                  <!--日志文件保留天数-->
                  <MaxHistory>30</MaxHistory>
              </rollingPolicy>
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
              </encoder>
              <!--日志文件最大的大小-->
              <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                  <MaxFileSize>100MB</MaxFileSize>
              </triggeringPolicy>
          </appender>
      
          <!--mybatis log configure-->
          <logger name="com.apache.ibatis" level="TRACE"/>
          <logger name="java.sql.Connection" level="DEBUG"/>
          <logger name="java.sql.Statement" level="DEBUG"/>
          <logger name="java.sql.PreparedStatement" level="DEBUG"/>
      
          <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
          <root level="DEBUG">
              <appender-ref ref="STDOUT"/>
              <appender-ref ref="FILE"/>
          </root>
      
      </configuration>
    • 再次执行单元测试方法testInsertCar,查看控制台是否有sql语句输出

2.8 MyBatis工具类SqlSessionUtil的封装

  • 目的:(使用后续的)每一次获取SqlSession对象代码太繁琐,封装一个工具类。

  • SqlSessionUtil.java
    
    package utils;
    
    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;
    
    /**
     * 获取SqlSession对象的工具类
     * 工具类的构造方法一般都私有化,因为工具类中的方法都是静态的,不用创建对象,直接用类名调用
     */
    public class SqlSessionUtil {
        private static SqlSessionFactory sqlSessionFactory = null;
    
        private SqlSessionUtil(){}
    
        /**
         * 在静态代码块中获取SqlSessionFactory对象;类加载的时候执行获取SqlSessionFactory对象,并且只执行一次
         */
        static {
            try {
                SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
                sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取执行SQL语句的SqlSession对象
         * @return SqlSession对象
         */
        public static SqlSession openSession(){
            return sqlSessionFactory.openSession();
        }
    }
  • 测试工具类,将testInsertCar()改造

2.9 映射文件中Mapper标签的namespace属性

SQL Mapper文件中<mapper>标签的namespace属性,它主要是为了防止sql语句id冲突,其实就是SQL语句id值的前缀。(在接口代理机制中会对namespace有约束,这里随意)

创建CarMapper2.xml文件,代码如下:

不难看出,CarMapper.xml和CarMapper2.xml文件中都有 id="selectCarAll"

将CarMapper2.xml配置到mybatis-config.xml文件中。

编写Java代码如下:

运行结果如下:

Java代码修改如下:

运行结果如下:

四、MyBatis的核心配置文件详解

mybatis-config.xml

<!-- 第一行是任何一个xml文件的固定写法 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!-- !DOCTYPE后面这个单词是这个xml文档的根标签名 -->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">  //.dtd:对当前XML文件的约束

<configuration>

<!-- 在environments标签中可以配置多个环境,一个environment环境对应一个数据库,通过其default属性指定:默认使用哪个环境-->
    <environments default="development">
<!-- 一个environment环境对应一个SqlSessionFactory对象-->
<!-- 将来在调用sqlSessionFactoryBuilder.build(InputStream inputStream,String environment)方法的时候可以通过环境标签的id属性指定使用的是哪个数据库-->
<!--调用这个sqlSessionFactoryBuilder.build(InputStream inputStream)方法只能获取默认的环境,获取默认的sqlSessionFactory对象-->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 这四个property标签:配置连接数据库的信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/bjpowernode"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 这四个property标签是配置连接数据库的信息的 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--XxxMapper.xml SQL映射文件创建好之后,将该文件的路径配置到这里-->
        <!--resource属性自动从类的根路径下开始查找资源-->
        <mapper resource="CarMapper.xml"/>
    </mappers>

</configuration>
  • configuration标签:MyBatis核心配置文件的根标签,表示配置信息。

  • mappers标签:在mappers标签中可以配置多个sql映射文件的路径。

  • mapper:配置sql映射文件的路径

    • resource属性:从类路径下开始加载资源

    • url属性:使用完全限定资源定位符(URL)方式

4.1 environment

mybatis-003-configuration

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>
<!-- 在environments标签中可以配置多个环境,一个environment环境对应一个数据库,通过其default属性指定:默认使用哪个环境-->
    <environments default="development">
<!-- 一个environment环境对应一个SqlSessionFactory对象-->
<!-- 将来在调用sqlSessionFactoryBuilder.build(InputStream inputStream,String environment)方法的时候可以通过环境标签的id属性指定使用的是哪个数据库-->
<!--调用这个sqlSessionFactoryBuilder.build(InputStream inputStream)方法只能获取默认的环境,获取默认的sqlSessionFactory对象-->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 这四个property标签:配置连接数据库的信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/bjpowernode"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 这四个property标签是配置连接数据库的信息的 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>
CarMapper.xml

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

<mapper namespace="car">
    <insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
</mapper>
ConfigurationTest.testEnvironment

package com.powernode.mybatis;

import com.powernode.mybatis.pojo.Car;
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.junit.Test;

public class ConfigurationTest {

    @Test
    public void testEnvironment() throws Exception{
        // 准备数据
        Car car = new Car();
        car.setCarNum("133");
        car.setBrand("丰田霸道");
        car.setGuidePrice(50.3);
        car.setProduceTime("2020-01-10");
        car.setCarType("燃油车");

        // 一个数据库对应一个SqlSessionFactory对象
        // 两个数据库对应两个SqlSessionFactory对象,以此类推
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

        // 使用默认数据库
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        int count = sqlSession.insert("insertCar", car);
        System.out.println("插入了几条记录:" + count);

        // 使用指定数据库
        SqlSessionFactory sqlSessionFactory1 = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "dev");
        SqlSession sqlSession1 = sqlSessionFactory1.openSession(true);
        int count1 = sqlSession1.insert("insertCar", car);
        System.out.println("插入了几条记录:" + count1);
    }
}

执行结果:

  • environments标签:环境(多个),以“s”结尾表示复数,也就是说mybatis的环境可以配置多个数据源。

    • default属性:通过环境environment的id指定默认使用的环境是哪个。

  • environment标签:具体的环境配置(主要包括:事务管理器的配置 + 数据源的配置

    • id属性:设置环境的唯一标识,该标识用在environments的default后面,用来指定默认使用哪个环境。

4.2 transactionManager

mybatis-config2.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="dev">
        <environment id="dev">
        <!-- 通过其type属性指定mybatis使用什么方式管理事务(配置事务管理器) -->
            <transactionManager type="MANAGED"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>
ConfigurationTest.testTransactionManager

@Test
public void testTransactionManager() throws Exception{
    // 准备数据
    Car car = new Car();
    car.setCarNum("133");
    car.setBrand("丰田霸道");
    car.setGuidePrice(50.3);
    car.setProduceTime("2020-01-10");
    car.setCarType("燃油车");
    // 获取SqlSessionFactory对象
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config2.xml"));
    // 获取SqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 执行SQL
    int count = sqlSession.insert("insertCar", car);
    System.out.println("插入了几条记录:" + count);
}

transactionManager标签:通过其type属性指定mybatis使用什么方式管理事务(配置事务管理器)

  • type属性:可选值包括两个

    • JDBC:使用JDBC原生的事务管理机制。底层原理:事务开启conn.setAutoCommit(false); ...处理业务...事务提交conn.commit();

    • MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。

当事务管理器是:JDBC

  • 采用JDBC的原生事务机制:

    • 开启事务:conn.setAutoCommit(false);
    • 处理业务......

    • 提交事务:conn.commit();

当事务管理器是:MANAGED

  • 交给容器去管理事务,但目前使用的是本地程序,没有容器的支持,当mybatis找不到容器的支持时:没有事务。也就是说只要执行一条DML语句,则提交一次。

4.3 dataSource

  • dataSource标签:用其type属性来指定数据源的类型

    • type属性:可选值包括三个:

      • UNPOOLED:虽然也实现Javax.sql.DataSource接口,但是没有使用数据库连接池技术,每次请求过来之后,都会创建新的Connection对象
        • property标签的name可以是:

          • driver:JDBC 驱动的 Java 类全限定名。

          • url :数据库的 JDBC URL 地址。

          • username :登录数据库的用户名。

          • password :登录数据库的密码。

          • defaultTransactionIsolationLevel 默认的连接事务隔离级别。

          • defaultNetworkTimeout 等待数据库操作完成的默认网络超时时间(单位:毫秒)

      • POOLED使用mybatis自己实现的数据库连接池,每次获取数据库连接对象都是从数据库连接池中拿

        • property标签的name可以是(除了包含UNPOOLED中之外):

          • poolMaximumActiveConnections 在任意时间可存在的活动(正在使用)连接数量,默认值:10

          • poolMaximumIdleConnections 任意时间可能存在的空闲连接数。

          • 其它....

      • JNDI:集成第三方的数据库连接池

        • property可以是(最多只包含以下两个属性):

          • initial_context 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。

          • data_source 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

mybatis-config3.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
		<!--dataSource标签用type属性来指定数据源的类型,datasource数据源是为程序提供Connection连接对象的-->
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>
ConfigurationTest.testDataSource

@Test
public void testDataSource() throws Exception{
    // 准备数据
    Car car = new Car();
    car.setCarNum("133");
    car.setBrand("丰田霸道");
    car.setGuidePrice(50.3);
    car.setProduceTime("2020-01-10");
    car.setCarType("燃油车");
    // 获取SqlSessionFactory对象
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config3.xml"));
    // 获取SqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    // 执行SQL
    int count = sqlSession.insert("insertCar", car);
    System.out.println("插入了几条记录:" + count);
    // 关闭会话
    sqlSession.close();
}

当type是UNPOOLED,控制台输出:

修改配置文件mybatis-config3.xml中的配置:

mybatis-config3.xml

<dataSource type="POOLED">

Java测试程序不需要修改,直接执行,看控制台输出:

通过测试得出:UNPOOLED不会使用连接池,每一次都会新建JDBC连接对象。POOLED会使用数据库连接池。【这个连接池是mybatis自己实现的。】

mybatis-config3.xml

<dataSource type="JNDI">

JNDI的方式:表示对接JNDI服务器中的连接池。这种方式给了我们可以使用第三方连接池的接口。如果想使用dbcp、c3p0、druid(德鲁伊)等,需要使用这种方式。

这里再重点说一下type="POOLED"的时候,它的属性有哪些?

mybatis-config3.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
                <!--最大连接对象活动的数量-->
                <property name="poolMaximumActiveConnections" value="3"/>
                <!--这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。-->
                <property name="poolTimeToWait" value="20000"/>
                <!--JDBC连接对象强行回归数据库连接池的时间,默认值为20秒-->
                <property name="poolMaximumCheckoutTime" value="20000"/>
                <!--最多空闲时连接对象的数量-->
                <property name="poolMaximumIdleConnections" value="1"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>

poolMaximumActiveConnections:最大连接对象活动的数量。默认值10

poolMaximumIdleConnections:最多空闲时连接对象的数量。默认值5

poolMaximumCheckoutTime:连接对象强行回归数据库连接池的时间。默认值20秒。

poolTimeToWait:当无法获取到空闲连接时,每隔20秒打印一次日志,避免因代码配置有误,导致傻等。(时长是可以配置的)

当然,还有其他属性。对于连接池来说,以上几个属性比较重要。

最大的活动的连接数量就是连接池中连接对象数量的上限。默认值10,如果有10个请求正在使用这10个连接,第11个请求只能等待空闲连接。

最大的空闲连接数量。默认值5,如何已经有了5个空闲连接,当第6个连接要空闲下来的时候,连接池会选择关闭该连接对象。来减少数据库的开销。

需要根据系统的并发情况,来合理调整连接池最大连接数以及最多空闲数量。充分发挥数据库连接池的性能。【可以根据实际情况进行测试,然后调整一个合理的数量。】

下图是默认配置:

在以上配置的基础之上,可以编写java程序测试:

ConfigurationTest.testPool

@Test
public void testPool() throws Exception{
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config3.xml"));
    for (int i = 0; i < 4; i++) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Object selectCarByCarNum = sqlSession.selectOne("selectCarByCarNum");
    }
}
CarMapper.xml

<select id="selectCarByCarNum" resultType="com.powernode.mybatis.pojo.Car">
  select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where car_num = '100'
</select>

4.4 .properties属性文件

mybatis提供了更加灵活的配置为:①先配置 连接数据库的信息 到一个属性文件中,假设在类的根路径下创建jdbc.properties文件,配置如下:

jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=123456
将来用${}通过key把对应的值从这个文件中取出来

②再在mybatis核心配置文件中用properties单标签引入属性文件,③再通过${}把属性文件中key对应的值取出来:

mybatis-config4.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"/>

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--使用${key}从xxx.properties文件中取值-->
                <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="CarMapper.xml"/>
    </mappers>
</configuration>

properties标签的两个属性,在mapper标签中也有:

resource:这个属性是从类的根路径下开始加载资源。【常用的。】

url:从指定的url加载资源,假设文件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。

注意:如果不知道mybatis-config.xml文件中标签的编写顺序的话,可以有两种方式知道它的顺序:

  • 第一种方式:查看dtd约束文件。

  • 第二种方式:通过idea的报错提示信息。【一般采用这种方式】

4.5 mappers标签

mappers标签中可以写多个mapper标签

mapper标签:加载SQL映射文件,包含多种配置方式,这里先主要看其中两种:

第一种:通过mapper单标签的resource属性:配置SQL映射文件的路径,表示从类的根路径下开始查找资源【比url常用】

mybatis-config4.xml


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

如果是这样写的话,必须保证类的根下有CarMapper.xml文件。

如果类的根路径下有一个包叫做test,CarMapper.xml如果放在test包下的话,这个配置应该是这样写:

mybatis-config4.xml

<mappers>
  <mapper resource="test/CarMapper.xml"/>
</mappers>

第二种:用url属性指定SQL映射文件的路径:从指定的url位置加载

假设CarMapper.xml文件放在d盘的根下,这个配置就需要这样写:

mybatis-config4.xml

<mappers>
  <mapper url="file:///d:/CarMapper.xml"/>
</mappers>

后面几种:9.3

三、使用MyBatis完成CRUD

  • 准备工作

    • 创建Maven的普通Java模块:mybatis-002-crud

    • pom.xml

      • 打包方式jar

      • 引入依赖:

        • mybatis依赖

        • mysql驱动依赖

        • junit依赖

        • logback依赖

    • mybatis-config.xml放在类的根路径下

    • CarMapper.xml放在类的根路径下

    • logback.xml放在类的根路径下

    • 提供自定义的com.powernode.mybatis.utils.SqlSessionUtil工具类

    • 创建测试用例:com.powernode.mybatis.CarMapperTest

3.1 insert(Create)

  • 以前SQL的映射文件中的SQL语句存在的问题

    • SQL语句中的值被写死

  • SQL语句中的值不能写死,值由用户提供,动态传值。之前的JDBC代码是这样写的:
  • 在MyBatis中这样写SQL语句中的值:
    • 在Java程序中,先将数据封装到Map集合中

    • 然后在sql语句中使用 #{} 来动态传值给SQL语句,#{} 等同于JDBC中的占位符 ? 。

    • Java程序:

  •         
    package org.example.mybatis.test;
    
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import utils.SqlSessionUtil;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class TestCarMapping {
        @Test
        public void testInsertCar(){
            //获取数据库执行对象
            SqlSession sqlSession = SqlSessionUtil.openSession();
    
            //创建Map集合,用于封装一条数据
            Map<String,Object> map = new HashMap<>();
            map.put("carNum",1111);
            map.put("brand","老汉推车");
            map.put("guidePrice",10.0);
            map.put("produceTime","2020-12-12");
            map.put("carType","电车");
    
            //用SqlSession对象调用相应的方法执行指定的SQL语句,insert的第一个参数为sql语句的id值,第二个参数是封装数据的对象
            int count = sqlSession.insert("insertCar",map);
            System.out.println(count);
    
            sqlSession.commit();
            sqlSession.close();
    
        }
    }
  •  
    • mapper文件:在#{} 的里面写map集合的key。
  • CarMapper.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">
    
    <!--namespace:可以配置这些SQL语句的命名空间-->
    <mapper namespace="car">
    
        <!--插入
            增删改操作的标签没有resultType属性,只有查询操作才有,
            因为增删改操作都是int类型的返回值,所以不需要指明
        -->
        <insert id="insertCar">
            insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
        </insert>
    
    </mapper>
  • 运行测试程序,查看数据库:

如果#{}里写map集合中不存在的key会有什么问题?

        如果key不存在不会报错,会在数据库表中插入NULL。

  • 使用pojo对象完成传参:
    • 第一步:定义一个pojo类Car,提供相关属性。

    • Car.java
      
      package org.example.mybatis.test.utils.pojo;
      
      public class Car {
          private Long id;
          private String carNum;
          private String brand;
          private Double guidePrice;
          private String produceTime;
          private String carType;
      
          public Car() {
          }
      
          public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
              this.id = id;
              this.carNum = carNum;
              this.brand = brand;
              this.guidePrice = guidePrice;
              this.produceTime = produceTime;
              this.carType = carType;
          }
      
          public Long getId() {
              return id;
          }
      
          public void setId(Long id) {
              this.id = id;
          }
      
          public String getCarNum() {
              return carNum;
          }
      
          public void setCarNum(String carNum) {
              this.carNum = carNum;
          }
      
          public String getBrand() {
              return brand;
          }
      
          public void setBrand(String brand) {
              this.brand = brand;
          }
      
          public Double getGuidePrice() {
              return guidePrice;
          }
      
          public void setGuidePrice(Double guidePrice) {
              this.guidePrice = guidePrice;
          }
      
          public String getProduceTime() {
              return produceTime;
          }
      
          public void setProduceTime(String produceTime) {
              this.produceTime = produceTime;
          }
      
          public String getCarType() {
              return carType;
          }
      
          public void setCarType(String carType) {
              this.carType = carType;
          }
      }
    • 第二步:Java程序

    • TestCarMapping.java
         
      
          @Test
          public void testInsertCarPoJo(){
              //获取数据库执行对象
              SqlSession sqlSession = SqlSessionUtil.openSession();
      
              //创建pojo对象,用于封装一条数据
              Car car = new Car();
              car.setCarNum("2000");
              car.setBrand("大力");
              car.setGuidePrice(30.0);
              car.setProduceTime("2000--3-3");
              car.setCarType("电力车");
      
              //执行SQL语句,insert的第一个参数是sql语句的id值,第二个参数是封装数据的对象
              int count = sqlSession.insert("insertCar",car);
              System.out.println(count);
      
              sqlSession.commit();
              sqlSession.close();
      
          }
    • 第三步:SQL语句,#{}里面写pojo类中的属性名,其实准确来说是get方法的方法名去掉get后,将剩下的单词首字母变小写

    • CarMapping.xml
      
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <!--namespace先随意写一个-->
      <mapper namespace="car">
      
          <!--写insert语句:保存一个汽车信息。id属性可以指定这条sql语句的唯一标识-->
          <insert id="insertCar">
              insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
          </insert>
      
      </mapper>
    • 运行程序,查看数据库表:

#{} 里写的是POJO的属性名,如果写成其他的会有问题吗?

错误信息中描述:在Car类中没有找到a属性的getter方法。

修改POJO类Car的代码,只将getCarNum()方法名修改为getA(),其他代码不变,如下:

再运行程序,查看数据库表中数据:

如果采用POJO对象传参,#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get方法不存在会报错。

注意:其实传参数的时候有一个属性parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的

3.2 delete(Delete)

需求:根据car_num进行删除。

mapper文件:

CarMapper.xml


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

Java程序:

TestCarMapping.java

    @Test
    public void testDeleteById(){
        //获取数据库执行对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //执行sql语句
        sqlSession.delete("deleteById",3);
        sqlSession.commit();
        sqlSession.close();
    }

运行结果:

注意:当占位符只有一个时,#{} 里面的内容可以随便写,最好见名知意。

3.3 update(Update)

更新表中的数据:

SQL语句如下:

CarMapper.xml

    <update id="updateById">
        update t_car set car_num = #{carNum},brand = #{brand},guide_price = #{guidePrice},produce_time = #{produceTime},car_type = #{carType} where id = #{id}
    </update>

Java代码如下:

TestCarMapping.java


    @Test
    public void testUpdateByid(){
        //获取数据库执行对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //封装数据
        Car car = new Car(11L,"3333","路虎",20.0D,"2003-11-30","燃料车");
        //执行sql
        sqlSession.update("updateById",car);
        sqlSession.commit();
        sqlSession.close();
    }

运行结果:

当然了,如果使用map传数据也是可以的。

3.4 select(Retrieve)

select语句和其它语句的不同:它会返回一个查询结果集,要用普通对象或集合对象来接收。

查询数据时有两个点:

  • 使用<select>标签的 resultType属性:指定将查询结果集 要封装成 什么类型的对象并返回,然后在Mybatis程序中用对应类型的对象接收查询结果(类名写全限定类名
  • 查询结果集的字段名需和java类的属性名一致,因为Mybatis要把查询到的数据 赋值(映射)到 java对象的属性中,所以要通过as关键字对查询出的列名起别名,当然还有其它解决方案,我们后面再看。

查询一条记录

需求:查询id为1的Car信息

SQL语句如下:

CarMapper.xml


    <select id="selectById" resultType="org.example.mybatis.test.utils.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where id = #{id}
    </select>

Java程序如下:

   
   @Test
    public void testSelectById(){
        //获取数据库执行对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //执行sql语句
        //方法的返回值:就是映射文件中配置的resultType的类型
        /*
        方法的执行流程:
            Mybatis的底层执行器会通过sql语句的id找到对应的Statement,并把7369给占位符,执行SQL语句,得到查询结果集
            Mybatis底层通过反射机制, 创建resultType属性 类型的对象 ,然后把查询得到的结果集赋值(映射)给该对象
         */
        Car car = sqlSession.selectOne("selectById",1);
        sqlSession.commit();
        sqlSession.close();
        System.out.println(car);
    }

查询多条数据

需求:查询所有的Car信息。

SQL语句如下:

CarMapping.xml

    <!--
    查询到数据返回多条记录,每一条封装在一个实体类对象中,然后将所有的实体类对象添加到List集合中
    resultType:指定的并不是集合的类型,而是单条数据所对应实体类类型
    resultType="java.util.List" 错误的配置方式
    -->
    <select id="selectByAll" resultType="org.example.mybatis.test.utils.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
    </select>

Java代码如下:

TestCarMapping.java


	@Test
    public void testSelectByAll(){
        //获取数据库执行对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //执行sql语句
        List<Object> carList = sqlSession.selectList("selectByAll");
        sqlSession.commit();
        sqlSession.close();
        for(Object obj:carList){
            System.out.println(obj);
        }
    }

运行结果如下:

3.5 总结

parameterType和resultType

parameterType:指定输入参数的类型,传递什么类型的参数给SQL语句,MyBatis会从输入对象中 取值 设置 在SQL语句中。
resultType:指定输出结果的类型,将查询结果集 映射成  此类型的Java对象,并返回。

通用总结

  • 在SQL映射文件中对应的标签中编写对应的SQL语句

  • MyBatis中都是调用SqlSession对象的对应方法来执行对应的SQL语句

  • 在mybatis中,在sql语句中使用 #{} 来动态传值给SQL语句,#{} 等同于JDBC中的占位符 ? 

    • 如果一条数据是用Map集合封装的,在#{}里面 填写map集合的key取出对应的value传给SQL语句

    • 如果一条数据是用pojp对象封装的,那么在#{}里面 根据pojo对象的属性名取出其属性值设置给SQL语句(相当于用这个符号取出传过来的pojo对象中的属性)

  • 使用SqlSession对象调用执行sql语句的相应方法

    • 方法的第一个参数是:映射文件中sql语句的id值,通过id值指定要执行哪条SQL语句

    • 方法的第二个参数:向sql语句中传入的数据,传入数据的数据类型必须与映射文件中配置的parameterType保持一致

      • parameterType:输入参数的类型

  • 执行查询语句后,会返回一个查询结果集。

    • 所以必须在<select>标签中用 resultType属性:指定指定输出结果的类型(类名写全限定类名)

六、在WEB中应用MyBatis(使用MVC架构模式)

由于文章内容太多,此节请点这里查看

七、javassist

Mybatis接口代理机制的原理,初学不用管

八、MyBatis中的接口代理机制

8.1 Mybatis接口代理机制的理解

  • 为什么用接口代理机制?:因为dao实现类中的方法很固定,基本上就是一行代码:通过SqlSession对象调用insert、delete、update、select等方法,这个类中的方法没有任何业务逻辑,所以使用接口代理机制动态生成dao接口的实现类
  • MyBatis中的接口代理机制:动态生成DAO接口的实现类(代理类)并创建其实例对象,以后获取到其代理实现类对象直接用即可。
  • mybatis实际上使用了代理模式,在内存中生成dao接口的代理类,并创建代理类的实例。

8.2 接口代理机制的开发规范

编写Mapper接口需要遵循一些规范,MyBatis就可以自动生成Mapper接口的实现类代理对象。

使用接口代理机制的前提:

  1. SQL映射文件中mapper标签的namespace属性需指定为:Mapper接口的全限定名
    1. 将mapper文件和Mapper接口关联起来,将来生成的Mapper接口实现类对象调用方法时,才知道执行的是哪个映射文件中的哪个sql语句
  2. SQL映射文件,sql语句的id值要和Mapper接口中的方法名一致
  3. Mapper.xml中定义的每个sql的parameterType的类型与Mapper接口方法的参数类型相同。
  4. Mapper.xml中定义的每个sql的resultType的类型与Mapper接口方法返回值类型相同。 
  5. 注:Mapper.xml映射文件最好和Mapper接口名称一致。

如何获取Mapper接口的代理类对象:通过SqlSession对象调用其getMapper()方法,传入Mapper接口的字节码文件对象,指定获取哪个Mapper接口的代理类对象

CarMapper mapper = sqlSession.getMapper(CarMapper.class);

8.3 接口代理机制实例(005-crud2)

面向接口进行crud

和以前的不同点:只有Mapper文件变了,和不用写Mapper接口的实现类了。

和以前的区别:

  • 一个是在Mapper接口的实现类中编写SqlSession对象来执行SQL语句;
  • 一个是直接在测试程序中用Mapper接口的代理类对象来执行SQL语句
  • 后者在调用相应方法执行时,不用指定SQL语句的id,因为SQL语句的id已经规范为方法名了,Mybatis的接口代理机制会自动替你完成指定,你调用哪个方法就是在指定执行哪条SQL语句
CarMapper接口
CarMapper.java


package org.example.mapper;

import org.example.pojo.Car;

import java.util.List;

public interface CarMapper {
    /**
     * 插入一条car记录
     * @param car
     * @return
     */
    int insert(Car car);

    /**
     * 根据id删除car记录
     * @param id
     * @return
     */
    int deleteById(Long id);

    /**
     * 更新汽车信息
     * @param car
     * @return
     */
    int update(Car car);

    /**
     * 根据id查询
     * @param id
     * @return
     */
    Car selectById(Long id);

    /**
     * 查询所有car的记录
     * @return
     */
    List<Car> selectAll();

}
SQL映射文件
CarMapper.xml


<?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映射文件中mapper标签的namespace必须是Mapper接口的全限定名,sql语句的id值也必须是dao接口的方法名-->
<mapper namespace="org.example.mapper.CarMapper">
    <insert id="insert">
        insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>

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

    <update id="update">
        update t_car set car_num=#{carNum},brand = #{brand},guide_price = #{guidePrice},produce_time = #{produceTime},car_type = #{carType}
        where id = #{id}
    </update>

    <select id="selectById" resultType="org.example.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where id = #{id}
    </select>

    <select id="selectAll" resultType="org.example.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
    </select>
</mapper>
TestCarMapper.java


package test;

import org.apache.ibatis.session.SqlSession;
import org.example.mapper.CarMapper;
import org.example.pojo.Car;
import org.example.utils.SqlSessionUtil;
import org.junit.Test;

import java.util.List;

public class TestCarMapper {
    @Test
    public void testInsert(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //获取Mapper接口的代理类对象,传入Mapper接口的字节码文件对象,告诉mybatis生成哪个Mapper接口的代理类对象
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        //通过CarMapper接口的代理类对象调用其中的方法执行SQL语句
        int count = mapper.insert(new Car(null,"7987","小车车",32.0,"2003-11-04","新能源"));
        System.out.println(count);
        sqlSession.commit();
        SqlSessionUtil.close(sqlSession);
    }

    @Test
    public void testdeleteById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //获取DAO接口的代理类对象
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        int count = mapper.deleteById(17L);
        sqlSession.commit();
        SqlSessionUtil.close(sqlSession);
    }

    @Test
    public void testupdate(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //获取DAO接口的代理类对象
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        int count = mapper.update(new Car(18L,"456","大车车",36.0,"2003-11-04","新能源"));
        System.out.println(count);
        sqlSession.commit();
        SqlSessionUtil.close(sqlSession);
    }

    @Test
    public void selectById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //获取DAO接口的代理类对象
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById(1L);
        System.out.println(car);
        SqlSessionUtil.close(sqlSession);
    }

    @Test
    public void testselectAll(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //获取DAO接口的代理类对象
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        List<Car> cars = mapper.selectAll();
        System.out.println(cars);
        SqlSessionUtil.close(sqlSession);
    }

}

九、总结Mybatis中DAO(Mapper)的开发方式

请点这里

十、Mybatis核心配置文件的剩余标签

请点这里

十一、#{}和${}的区别

  • #{}:底层使用的是PreparedStatement对象。它是先编译sql语句,再给占位符传值。可以防止sql注入,比较常用。

  • ${}:底层使用的是Statement对象。它先拼接sql语句,再编译sql语句。存在sql注入的风险。只有在需要进行sql语句关键字拼接的情况下才会用到。

  • 表面区别:#{}传进去的值有单引号,${}传进去的值没有加单引号

  • 优先使用#{},避免SQL注入

需求:根据car_type查询汽车

模块名:mybatis-005-antic

使用#{}

导入依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.powernode</groupId>
    <artifactId>mybatis-005-antic</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
        <!--mysql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!--logback依赖-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>

jdbc.properties放在类的根路径下

jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root

logback.xml,可以拷贝之前的,放到类的根路径下

utils:

SqlSessionUtil.java

package com.powernode.mybatis.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * MyBatis工具类
 *
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

    /**
     * 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
     *
     * @return 新的会话对象
     */
    public static SqlSession openSession() {
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭SqlSession对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
        }
        local.remove();
    }
}

pojo类

Car.java

package com.powernode.mybatis.pojo;
/**
 * 普通实体类:汽车
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class Car {
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;
    // 构造方法
    // set get方法
    // toString方法
}

mapper接口(DAO接口)

CarMapper.java

package com.powernode.mybatis.mapper;

import com.powernode.mybatis.pojo.Car;

import java.util.List;

/**
 * Car的sql映射对象
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public interface CarMapper {

    /**
     * 根据car_num获取Car
     * @param carType
     * @return
     */
    List<Car> selectByCarType(String carType);

}

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"/>
    <environments default="dev">
        <environment id="dev">
            <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="CarMapper.xml"/>
    </mappers>
</configuration>

CarMapper.xml,放在类的根路径下:注意namespace必须和接口名一致。id必须和接口中方法名一致

CarMapper.xml

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

<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
    <select id="selectByCarType" resultType="com.powernode.mybatis.pojo.Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from
            t_car
        where
            car_type = #{carType}
    </select>
</mapper>

测试程序

CarMapperTest

package com.powernode.mybatis.test;

import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.junit.Test;

import java.util.List;

/**
 * CarMapper测试类
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class CarMapperTest {

    @Test
    public void testSelectByCarType(){
        CarMapper mapper = (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);
        List<Car> cars = mapper.selectByCarType("燃油车");
        cars.forEach(car -> System.out.println(car));
    }
}

执行结果:

通过执行可以清楚的看到,sql语句中是带有 ? 的,这个 ? 就是大家在JDBC中所学的占位符,专门用来接收值的。

把“燃油车”以String类型的值,传递给 ?

结论:这就是 #{},它会先进行sql语句的预编译,然后再给占位符传值

使用${}

同样的需求,我们使用${}来完成

CarMapper.xml文件修改如下:

CarMapper.xml

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

<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
    <select id="selectByCarType" resultType="com.powernode.mybatis.pojo.Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from
            t_car
        where
            <!--car_type = #{carType}-->
            car_type = ${carType}
    </select>
</mapper>

再次运行测试程序:

出现异常了,这是为什么呢?看看生成的sql语句:

结论:很显然,${} 是先进行sql语句的拼接,然后再编译,出现语法错误是正常的,因为 燃油车 是一个字符串,在sql语句中应该添加单引号

修改:

CarMapper.xml

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

<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
    <select id="selectByCarType" resultType="com.powernode.mybatis.pojo.Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from
            t_car
        where
            <!--car_type = #{carType}-->
            <!--car_type = ${carType}-->
            car_type = '${carType}'
    </select>
</mapper>

再执行测试程序:

通过以上测试,可以看出,对于以上这种需求来说,还是建议使用 #{} 的方式。

原则:能用 #{} 就不用 ${}

什么情况下必须使用${}

在向sql语句传SQL语句关键字的时候需要使用${},因为#{}是以值的形式放到SQL语句中的。必须使用${}

需求:通过向sql语句中注入asc或desc关键字,来完成数据的升序或降序排列。

  • 先使用#{}尝试:

CarMapper接口:

CarMapper

/**
 * 查询所有的Car
 * @param ascOrDesc asc或desc
 * @return
 */
List<Car> selectAll(String ascOrDesc);

CarMapper.xml文件:

CarMapper.xml

<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
  select
  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
  from
  t_car
  order by carNum #{key}
</select>

测试程序

CarMapperTest.testSelectAll

@Test
public void testSelectAll(){
    CarMapper mapper = (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);
    List<Car> cars = mapper.selectAll("desc");
    cars.forEach(car -> System.out.println(car));
}

运行:

报错的原因是sql语句不合法,因为采用这种方式传值,最终sql语句会是这样:

select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType from t_car order by carNum 'desc'

desc是一个关键字,不能带单引号的,所以在进行sql语句关键字拼接的时候,必须使用${}

  • 使用${} 改造

CarMapper.xml

<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
  select
  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
  from
  t_car
  <!--order by carNum #{key}-->
  order by carNum ${key}
</select>

再次执行测试程序:

向SQL语句中拼接表名

现实业务中,可能存在分表存储数据的情况。因为表存的话,数据量太大,查询效率比较低。可以将这些数据有规律的分表存储,这样查询效率就比较高,因为扫描的数据量就变少了

业务背景:实际开发中,有的表数据量非常庞大,可能会采用分表方式进行存储,比如每天生成一张表,表的名字与日期挂钩,例如:2022年8月1日生成的表叫:t_user20220108。2000年1月1日生成的表叫:t_user20000101。此时前端在进行查询的时候会提交一个日期,比如前端提交的日期为:2000年1月1日,那么后端就会把这个日期和表名拼接为:t_user20000101有了这个表名之后,将表名传到sql语句当中,返回查询结果。那么大家思考一下,拼接表名到sql语句当中应该使用#{} 还是 ${} 呢?

使用#{}会是这样:select * from 't_car'

使用${}会是这样:select * from t_car

LogMapper.xml

<select id="selectAllByTableName" resultType="car">
  select
 	 id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
  from
    t_log_
  
</select>
LogMapper.java
    
public interface LogMapper{    

	/**
	 * 根据日期查询不同的表,获取表中所有的日志
	 * @param tableName
	 * @return
 	*/
	List<Log> selectAllByTable(String date);
}
public class TestLogMapper{
	@Test
	public void testSelectAllByTable(){
        SqlSession sqlSession = SqlSessionUtil.openSqlSession();
        LogMapper mapper = sqlSession.getMapper(LogMapper.class);
        List<Log> logs = mapper.selectAllByTable("20220901");
        logs.forEach(log -> System.out.println(log));
	}
}

执行结果:

批量删除

批量删除:一次删除多条记录。

批量删除的SQL语句有两种写法:

  • 第一种or:delete from t_user where id = 1 or id = 2 or id = 3;

  • 第二种in:delete from t_user where id in(1, 2, 3);

现在使用第二种in的方式处理,前端传过来的字符串:"1, 2, 3"

如果使用mybatis处理,应该使用#{} 还是 ${}

使用#{} :delete from t_user where id in('1,2,3') 执行错误:1292 - Truncated incorrect DOUBLE value: '1,2,3'

使用${} :delete from t_user where id in(1, 2, 3)应该采用${}来取值

CarMapper接口

	/**
     * 根据id批量删除
     * @param ids
     * @return
     */
int deleteBatch(String ids);
CarMapper.xml

<delete id="deleteBatch">
  delete from t_car where id in(${ids})
</delete>
CarMapperTest.testDeleteBatch

@Test
public void testDeleteBatch(){
    CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
    int count = mapper.deleteBatch("1,2,3");
    System.out.println("删除了几条记录:" + count);
    SqlSessionUtil.openSession().commit();
}

执行结果:

模糊查询


模糊查询的三种方式
1、#{}占位符,防止sql注入
需要在Java中将传入数据的前后拼接%符号
where ename like #{ename}
2、使用字符串拼接函数
where ename like concat('%',#{ename},'%')
3、${}拼接符号,实现sql的拼接
where ename like '%${value}%'
注意:${}不是占位符,如果输入参数为简单类型,${}中的内容必须为value


第一种方式:

mapper文件:


<select id="selectByEname1" parameterType="java.lang.String" 
resultType="com.gs.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
    where ename like #{ename}
</select>

Java代码:

@Test
public void testSelectByEname() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
String ename = "S";
List<Emp> list = session.selectList("emp.selectByEname1", "%"+ename+"%");
for (Emp emp : list) {
System.out.println(emp);
}
session.close();
}

第二种方式:

mapper文件:

<select id="selectByEname2" parameterType="java.lang.String"
resultType="com.gs.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
    where ename like concat('%',#{ename},'%')
</select>

Java代码:

@Test
public void testSelectByEname2() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();
    String ename = "S";
    List<Emp> list = session.selectList("emp.selectByEname2", ename);
    for (Emp emp : list) {
        System.out.println(emp);
    }
    session.close();
}

第三种方式:

mapper文件:

<select id="selectByEname3" parameterType="java.lang.String"
resultType="com.gs.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
    where ename like '%${value}%'
</select>

Java代码:

@Test
public void testSelectByEname3() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();
    String ename = "S";
    List<Emp> list = session.selectList("emp.selectByEname3", ename);
    for (Emp emp : list) {
        System.out.println(emp);
    }
    session.close();
}

十二、输入参数映射和输出结果映射专题

点击这里

十三、动态SQL拼接

动态SQL拼接

十四、Mybatis关联查询

点这里

十五、Mybatis的延迟加载

Mybatis的延迟加载

十六、Mybatis的缓存

Mybatis的缓存

十七、Mybatis使用PageHelper分页插件

Mybatis的分页插件

十八、Mybatis注解式开发

mybatis注解开发

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值