编程知识 cdmana.com

Mybatis框架

文章目录

一.Mybatis概述

1.1 框架

  • 在⽂献中看到的framework被翻译为框架
  • Java常⽤框架:
    • SSM三⼤框架:Spring + SpringMVC + MyBatis
    • SpringBoot
    • SpringCloud
    • 等等…
  • 框架其实就是对通⽤代码的封装,提前写好了⼀堆接⼝和类,我们可以在做项⽬的时候直接引⼊这些 接⼝和类(引⼊框架),基于这些现有的接⼝和类进⾏开发,可以⼤⼤提⾼开发效率。
  • 框架⼀般都以jar包的形式存在。(jar包中有class⽂件以及各种配置⽂件等。)

1.2 三层架构

此处可转JavaWeb查看第七点!!
链接:点击此处转移查看!

1.3 JDBC不足

  • SQL语句写死在Java程序中,不灵活。改SQL的话就要改Java代码。违背开闭原则OCP。
  • 给?传值是繁琐的。
  • 将结果集封装成Java对象是繁琐的。

1.4 了解Mybatis

  • MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
  • MyBatis在三层架构中负责持久层的,属于持久层框架。
  • ORM:对象关系映射
    • O(Object):Java虚拟机中的Java对象
    • R(Relational):关系型数据库
    • M(Mapping):将Java虚拟机中的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.1 版本

软件版本:

  • IntelliJ IDEA:2022.2
  • 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中文网

  • 写代码前准备:
    • 准备数据库表:汽⻋表t_car,字段包括:
      • id:主键(⾃增)【bigint】
      • car_num:汽⻋编号【varchar】
      • brand:品牌【varchar】
      • guide_price:⼚家指导价【decimal类型,专⻔为财务数据准备的类型】
      • produce_time:⽣产时间【char,年⽉⽇即可,10个⻓度,‘2022-10-11’】
      • car_type:汽⻋类型(燃油⻋、电⻋、氢能源)【varchar】
  • 步骤1:创建maven的java项目
  • 步骤2:引⼊依赖(mybatis依赖 + mysql驱动依赖)
    pom.xml
<!--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>
  • 步骤3:在resources根⽬录下新建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>
 <environments default="development">
 <environment id="development">
 <transactionManager type="JDBC"/>
 <dataSource type="POOLED">
 <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://localhost:3306/po wernode"/>
 <property name="username" value="root"/>
 <property name="password" value="root"/>
 </dataSource>
 </environment>
 </environments>
 <mappers>
 <!--sql映射⽂件创建好之后,需要将该⽂件路径配置到这⾥-->
 <mapper resource=""/>
 </mappers>
</configuration>

注意1:mybatis核⼼配置⽂件的⽂件名不⼀定是mybatis-config.xml,可以是其它名字。
注意2:mybatis核⼼配置⽂件存放的位置也可以随意。这⾥选择放在resources根下,相当于放到了类的 根路径下。

  • 步骤4:在resources根⽬录下新建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">
<!--namespace先随意写⼀个-->
<mapper namespace="car">
 <!--insert sql:保存⼀个汽⻋信息-->
 <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>

注意1:sql语句最后结尾可以不写“;”
注意2:CarMapper.xml⽂件的名字不是固定的。可以使⽤其它名字。
注意3:CarMapper.xml⽂件的位置也是随意的。这⾥选择放在resources根下,相当于放到了类的根路径下。
注意4:将CarMapper.xml⽂件路径配置到mybatis-config.xml:
mybatis-config.xml

<mapper resource="CarMapper.xml"/>
  • 步骤5:编写MyBatisIntroductionTest代码
    MyBatisIntroductionTest.java
package com.powernode.mybatis;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
/** * MyBatis⼊⻔程序 */
public class MyBatisIntroductionTest {
    
 public static void main(String[] args) {
    
 // 1. 创建SqlSessionFactoryBuilder对象
 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSession
FactoryBuilder();
 // 2. 创建SqlSessionFactory对象
 InputStream is = Thread.currentThread().getContextClassLoader().ge
tResourceAsStream("mybatis-config.xml");
 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.bui
ld(is);
 // 3. 创建SqlSession对象
 SqlSession sqlSession = sqlSessionFactory.openSession();
 // 4. 执⾏sql
 int count = sqlSession.insert("insertCar"); // 这个"insertCar"必须是
sql的id
 System.out.println("插⼊⼏条数据:" + count);
 // 5. 提交(mybatis默认采⽤的事务管理器是JDBC,默认是不提交的,需要⼿动提交。)
 sqlSession.commit();
 // 6. 关闭资源(只关闭是不会提交的)
 sqlSession.close();
 } }

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

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

2.3 MyBatis核心配置文件的名字和路径

  • 核⼼配置⽂件的名字是随意的,因为以下的代码:
// ⽂件名是出现在程序中的,⽂件名如果修改了,对应这⾥的java程序也改⼀下就⾏了。
InputStream is = Thread.currentThread().getContextClassLoader().getResource
AsStream("mybatis-config.xml");
  • 核⼼配置⽂件不是必须放到resources下!也可以放到D盘根⽬录下!

将mybatis-config.xml⽂件拷⻉⼀份放到D盘根下,然后修改程序:

 // 2. 创建SqlSessionFactory对象
 // 这只是⼀个输⼊流,可以⾃⼰new。
 InputStream is = new FileInputStream("D:/mybatis-config.xml");
 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.bui
ld(is);

结论:

  • mybatis核⼼配置⽂件的名字是随意的,存放路径也是随意的。
  • mybatis核⼼配置⽂件的名字不是固定的,但通常该⽂件的名字叫做:mybatis-config.xml
  • mybatis核⼼配置⽂件的路径不是固定的,但通常该⽂件会存放到类路径当中,这样让项⽬的移植更加健壮.
  • 在mybatis中提供了⼀个类:Resources【org.apache.ibatis.io.Resources】,该类可以从类路径当
    中获取资源,我们通常使⽤它来获取输⼊流InputStream,代码如下:
// 这种⽅式只能从类路径当中获取资源,也就是说mybatis-config.xml⽂件需要在类路径下。
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

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

package com.jm.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 org.junit.Test;

import java.io.IOException;

public class MyTest {
    
    @Test
    public void testInsert(){
    
        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 (IOException e) {
    
            //6.回滚
            if(sqlSession!=null){
    
                sqlSession.rollback();
            }
            e.printStackTrace();
        }finally {
    
            //7.关闭
            sqlSession.close();
        }
    }
}

2.5 引入JUnit

  • JUnit是专⻔做单元测试的组件。
    • 在实际开发中,单元测试⼀般是由我们Java程序员来完成的。
    • 我们要对我们⾃⼰写的每⼀个业务⽅法负责任,要保证每个业务⽅法在进⾏测试的时候都能通 过。
    • 测试的过程中涉及到两个概念:
      • 期望值
      • 实际值
    • 期望值和实际值相同表示测试通过,期望值和实际值不同则单元测试执⾏时会报错。
  • 这⾥引⼊JUnit是为了代替main⽅法。
  • 使⽤JUnit步骤:
    • 第⼀步:引⼊依赖

    •   <!-- junit依赖 -->
        <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.13.2</version>
         <scope>test</scope>
        </dependency>
      
    • 第⼆步:编写单元测试类【测试⽤例】,测试⽤例中每⼀个测试⽅法上使⽤@Test注解进⾏标注。

      • 测试⽤例的名字以及每个测试⽅法的定义都是有规范的:
        • 测试⽤例的名字:XxxTest
        • 测试⽅法声明格式:public void test业务⽅法名(){}
        // 测试⽤例
        public class CarMapperTest{
                  
         
         // 测试⽅法
         @Test
         public void testInsert(){
                  }
         
         @Test
         public void testUpdate(){
                  }
         
        }
        
    • 第三步:可以在类上执⾏,也可以在⽅法上执⾏

      • 在类上执⾏时,该类中所有的测试⽅法都会执⾏。
      • 在⽅法上执⾏时,只执⾏当前的测试⽅法。
  • 编写⼀个测试⽤例,来测试insertCar业务
    CarMapperTest.java
package com.jm.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 org.junit.Test;

import java.io.IOException;

public class MyTest {
    
    @Test
    public void testInsert(){
    
        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 (IOException e) {
    
            //6.回滚
            if(sqlSession!=null){
    
                sqlSession.rollback();
            }
            e.printStackTrace();
        }finally {
    
            //7.关闭
            sqlSession.close();
        }
    }
}

2.6 引入日志框架logback

(可参考:Mybatis中文网)

  • 引⼊⽇志框架的⽬的是为了看清楚mybatis执⾏的具体sql。
  • 启⽤标准⽇志组件,只需要在mybatis-config.xml⽂件中添加以下配置:
<settings>
 <setting name="logImpl" value="STDOUT_LOGGING" />
</settings>

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

  • logback是⽬前⽇志框架中性能较好的,较流⾏的,所以我们选它。
  • 引⼊logback的步骤:
    • 第⼀步:引⼊logback相关依赖
      <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
       <version>1.2.11</version>
       <scope>test</scope>
      </dependency>
      
    • 第⼆步:引⼊logback相关配置⽂件(⽂件名叫做logback.xml或logback-test.xml,放到类路径当中)
      <?xml version="1.0" encoding="UTF-8"?>
      <configuration debug="false">
       <!--定义⽇志⽂件的存储地址-->
       <property name="LOG_HOME" value="/home"/>
       <!-- 控制台输出 -->
       <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
       <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncode r">
       <!--格式化输出:%d表示⽇期,%thread表示线程名,%-5level:级别从左显示5 个字符宽度%msg:⽇志消息,%n是换⾏符-->
       <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logge
      r{50} - %msg%n</pattern>
       </encoder>
       </appender>
       <!-- 按照每天⽣成⽇志⽂件 -->
       <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAp pender">
       <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRolling Policy">
       <!--⽇志⽂件输出的⽂件名-->
       <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</F ileNamePattern>
       <!--⽇志⽂件保留天数-->
       <MaxHistory>30</MaxHistory>
       </rollingPolicy>
       <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncode r">
       <!--格式化输出:%d表示⽇期,%thread表示线程名,%-5level:级别从左显示5 个字符宽度%msg:⽇志消息,%n是换⾏符-->
       <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logge
      r{50} - %msg%n</pattern>
       </encoder>
       <!--⽇志⽂件最⼤的⼤⼩-->
       <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTrig geringPolicy">
       <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 < ER ROR -->
       <root level="DEBUG">
       <appender-ref ref="STDOUT"/>
       <appender-ref ref="FILE"/>
       </root>
      </configuration>
      
    • 第三步:再次执⾏单元测试⽅法testInsertCar,查看控制台是否有sql语句输出

2.7 MyBatis的封装工具类SqlSessionUtil

  • 每⼀次获取SqlSession对象代码太繁琐,封装⼀个⼯具类

SqlSessionUtil.java Java

package com.jm.unit;

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;

/** * mybatiss工具类 */
public class SqlSessionUtil {
    
    //防止被创建对象
    private SqlSessionUtil(){
    }
    //一个数据库对应一个SqlSessionFactory对象
    private static SqlSessionFactory sqlSessionFactory;
    //类加载时初始化sqlSessionFactory对象
    static {
    
        try {
    
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
    
            throw new RuntimeException(e);
        }
    }
    //每调⽤⼀次openSession()可获取⼀个新的会话,该会话⽀持⾃动提交。
    public static SqlSession openSession(){
    
        return sqlSessionFactory.openSession();
    }
}

  • 测试⼯具类,将testInsertCar()改造
    @Test
    public void testInsert2(){
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.insert("insertCar");
        System.out.println("插入条数="+count);
        sqlSession.close();
    }

三.使⽤MyBatis完成CRUD

  • 准备工作
    • 创建module(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)

  • 在MyBatis中可以这样做:
    • 在Java程序中,将数据放到Map集合中
    • 在sql语句中使⽤ #{map集合的key} 来完成传值,#{} 等同于JDBC中的 ? ,#{}就是占位符

Java程序

package com.powernode.mybatis;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/** * 测试MyBatis的CRUD */
public class CarMapperTest {
    
 @Test
 public void testInsertCar(){
    
 // 准备数据
 Map<String, Object> map = new HashMap<>();
 map.put("k1", "103");
 map.put("k2", "奔驰E300L");
 map.put("k3", 50.3);
 map.put("k4", "2020-10-01");
 map.put("k5", "燃油⻋");
 // 获取SqlSession对象
 SqlSession sqlSession = SqlSessionUtil.openSession();
 // 执⾏SQL语句(使⽤map集合给sql语句传递数据)
 int count = sqlSession.insert("insertCar", map);
 System.out.println("插⼊了⼏条记录:" + count);
 } }

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先随便写-->
<mapper namespace="car">
 <insert id="insertCar">
 insert into t_car(car_num,brand,guide_price,produce_time,car_typ
e) values(#{k1},#{k2},#{k3},#{k4},#{k5})
 </insert>
</mapper>

注意1:#{} 的⾥⾯必须填写map集合的key,不能随便写。

  • 如果#{}⾥写的是map集合中不存在的key会有什么问题?
    • 正常执⾏。不过 #{kk} 的写法导致⽆法获取到map集合中的数据,最终 导致数据库表car_num插⼊了NULL

在以上sql语句中,可以看到#{k1} #{k2} #{k3} #{k4} #{k5}的可读性太差,为了增强可读性.

Java程序做如下修改:

Map<String, Object> map = new HashMap<>();
// 让key的可读性增强
map.put("carNum", "103");
map.put("brand", "奔驰E300L");
map.put("guidePrice", 50.3);
map.put("produceTime", "2020-10-01");
map.put("carType", "燃油⻋");

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">
<mapper namespace="car">
 <insert id="insertCar">
 insert into t_car(car_num,brand,guide_price,produce_time,car_type)
values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
 </insert>
</mapper>

使⽤Map集合可以传参,那使⽤pojo(简单普通的java对象)可以完成传参吗?测试⼀下:

  • 第⼀步:定义⼀个pojo类Car,提供相关属性。
package com.jm.pojo;

/** * 封装Car类 */
public class Car {
    
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    @Override
    public String toString() {
    
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", 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;
    }

    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 Car() {
    
    }
}

  • 第⼆步:Java程序
    @Test
    public void testInsertCar(){
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //封装数据
        Car car=new Car(null,"333","法拉利",400.0,"2008-4-15","燃油车");
        int count=sqlSession.insert("insertCar",car);
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }
  • 第三步: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>

总结:

  • 如果采⽤map集合传参,#{} ⾥写的是map集合的key,如果key不存在不会报错,数据库表中会插 ⼊NULL。
  • 如果采⽤POJO传参,#{} ⾥写的是get⽅法的⽅法名去掉get之后将剩下的单词⾸字⺟变⼩写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get⽅法不存在会报错。

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

<insert id="insertCar" parameterType="java.util.Map">
	 insert into t_car(car_num,brand,guide_price,produce_time,car_type) values
	(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
	</insert> <insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car" >
	 insert into t_car(car_num,brand,guide_price,produce_time,car_type) values
	(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>

3.2 delete(Delete)

需求:根据car_num进⾏删除。

SQL语句这样写:

<delete id="deleteByCarNum">
	 delete from t_car where car_num = #{SuiBianXie}
</delete>

Java程序这样写:

@Test
public void testDeleteByCarNum(){
    
	 // 获取SqlSession对象
	 SqlSession sqlSession = SqlSessionUtil.openSession();
	 // 执⾏SQL语句
	 int count = sqlSession.delete("deleteByCarNum", "102");
	 System.out.println("删除了⼏条记录:" + count); 
 }

注意:当占位符只有⼀个的时候,${} ⾥⾯的内容可以随便写。

3.3 update(Update)

需求:修改id=34的Car信息,car_num为102,brand为⽐亚迪汉,guide_price为30.23,produce_time 为2018-09-10,car_type为电⻋

SQL语句如下:

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

Java代码如下:

 @Test
 public void testUpdateCarByPOJO(){
    
	 // 准备数据
	 Car car = new Car();
	 car.setId(34L);
	 car.setCarNum("102");
	 car.setBrand("⽐亚迪汉");
	 car.setGuidePrice(30.23);
	 car.setProduceTime("2018-09-10");
	 car.setCarType("电⻋");
	 // 获取SqlSession对象
	 SqlSession sqlSession = SqlSessionUtil.openSession();
	 // 执⾏SQL语句
	 int count = sqlSession.update("updateCarByPOJO", car);
	 System.out.println("更新了⼏条记录:" + count);
 }

3.4 select(Retrieve)

select语句和其它语句不同的是:查询会有⼀个结果集。
查询⼀条数据
需求:查询id为1的Car信息

SQL语句如下:

<select id="selectCarById">
	 select * from t_car where id = #{id}
</select>

Java程序如下:

@Test
public void testSelectCarById(){
    
	 // 获取SqlSession对象
	 SqlSession sqlSession = SqlSessionUtil.openSession();
	 // 执⾏SQL语句
	 Object car = sqlSession.selectOne("selectCarById", 1);
	 System.out.println(car); 
 }

按以上方法将会出现异常,原因是什么呢?

### Error querying database. Cause: org.apache.ibatis.executor.ExecutorExc
eption:
 A query was run and no Result Maps were found for the Mapped Statement
'car.selectCarById'. 
【翻译】:对于⼀个查询语句来说,没有找到查询的结果映射。
 It's likely that neither a Result Type nor a Result Map was specified. 
 【翻译】:很可能既没有指定结果类型,也没有指定结果映射。

以上的异常⼤致的意思是:对于⼀个查询语句来说,你需要指定它的“结果类型”或者“结果映射”。
所以说,你想让mybatis查询之后返回⼀个Java对象的话,⾄少你要告诉mybatis返回⼀个什么类型的 Java对象,可以在标签中添加resultType属性,⽤来指定查询要转换的类型:

<select id="selectCarById" resultType="com.powernode.mybatis.pojo.Car">
    select * from t_car where id = #{id}
</select>

但是奇怪的是返回的Car对象,只有id和brand两个属性有值,其它属性的值都是null,这是为什么呢?
我们尝试在sql语句中使⽤as关键字来给查询结果列名起别名试试:

<select id="selectCarById" 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
	 id = #{id}
</select>

通过测试得知,如果当查询结果的字段名和java类的属性名对应不上的话,可以采⽤as关键字起别名, 当然还有其它解决⽅案,我们后⾯再看。

查询多条数据

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

SQL语句如下:

<!--虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。-->
<select id="selectCarAll" resultType="com.powernode.mybatis.pojo.Car">
 <!--记得使⽤as起别名,让查询结果的字段名和java类的属性名对应上。-->
	 select
	 id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
	 from
	 t_car
</select>

Java代码如下:

@Test
public void testSelectCarAll(){
    
	 // 获取SqlSession对象
	 SqlSession sqlSession = SqlSessionUtil.openSession();
	 // 执⾏SQL语句
	 List<Object> cars = sqlSession.selectList("selectCarAll");
	 // 输出结果
	 cars.forEach(car -> System.out.println(car)); 
 }

3.5 关于SQL Mapper的namespace

在SQL Mapper配置⽂件中标签的namespace属性可以翻译为命名空间,这个命名空间主要是为了防⽌sqlId冲突的。
创建CarMapper2.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="car2">
 <select id="selectCarAll" 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
 </select>
</mapper>

不难看出,CarMapper.xml和CarMapper2.xml⽂件中都有 id=“selectCarAll”
将CarMapper2.xml配置到mybatis-config.xml⽂件中。

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

编写Java代码如下:

@Test
public void testNamespace(){
    
 // 获取SqlSession对象
 SqlSession sqlSession = SqlSessionUtil.openSession();
 // 执⾏SQL语句
 List<Object> cars = sqlSession.selectList("selectCarAll");
 // 输出结果
 cars.forEach(car -> System.out.println(car)); }

运行结果:

org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException:
 selectCarAll is ambiguous in Mapped Statements collection (try using the
full name including the namespace, or rename one of the entries)
 【翻译】selectCarAll在Mapped Statements集合中不明确(请尝试使⽤包含名称空间的全 名,或重命名其中⼀个条⽬)
 【⼤致意思是】selectCarAll重名了,你要么在selectCarAll前添加⼀个名称空间,要有你改 个其它名字。

Java代码修改如下:

@Test
public void testNamespace(){
    
	 // 获取SqlSession对象
	 SqlSession sqlSession = SqlSessionUtil.openSession();
	 // 执⾏SQL语句
	 //List<Object> cars = sqlSession.selectList("car.selectCarAll");
	 List<Object> cars = sqlSession.selectList("car2.selectCarAll");
	 // 输出结果
	 cars.forEach(car -> System.out.println(car)); 
 }

四、MyBatis核心配置文件详解

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 default="development">
 <environment id="development">
 <transactionManager type="JDBC"/>
 <dataSource type="POOLED">
 <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://localhost:3306/po wernode"/>
 <property name="username" value="root"/>
 <property name="password" value="root"/>
 </dataSource>
 </environment>
 </environments>
 <mappers>
 <mapper resource="CarMapper.xml"/>
 <mapper resource="CarMapper2.xml"/>
 </mappers>
</configuration>
  • configuration:根标签,表示配置信息。
  • environments:环境(多个),以“s”结尾表示复数,也就是说mybatis的环境可以配置多个数据源
    • default属性:表示默认使⽤的是哪个环境,default后⾯填写的是environment的id。default 的值只需要和environment的id值⼀致即可。
  • environment:具体的环境配置(主要包括:事务管理器的配置 + 数据源的配置
    • id:给当前环境⼀个唯⼀标识,该标识⽤在environments的default后⾯,⽤来指定默认环境的选择
  • transactionManager:配置事务管理器
    • type属性:指定事务管理器具体使⽤什么⽅式,可选值包括两个
      • JDBC:使⽤JDBC原⽣的事务管理机制。底层原理:事务开启 conn.setAutoCommit(false); …处理业务…事务提交conn.commit();
      • MANAGED:交给其它容器来管理事务,⽐如WebLogic、JBOSS等。如果没有管理事务的 容器,则没有事务。没有事务的含义:只要执⾏⼀条DML语句,则提交⼀次。
  • dataSource:指定数据源
    • type属性:⽤来指定具体使⽤的数据库连接池的策略,可选值包括三个
      • UNPOOLED:采⽤传统的获取连接的⽅式,虽然也实现Javax.sql.DataSourc接⼝,但 是 并没有使⽤池的思想。+
        • property可以是:
          • driver 这是 JDBC 驱动的 Java 类全限定名。
          • url 这是数据库的 JDBC URL 地址。
          • username 登录数据库的⽤户名。
          • password 登录数据库的密码。
          • defaultTransactionIsolationLevel 默认的连接事务隔离级别。
          • defaultNetworkTimeout 等待数据库操作完成的默认⽹络超时时间(单位:毫秒)
      • POOLED:采⽤传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实 现。
        • property可以是(除了包含UNPOOLED中之外):
          • poolMaximumActiveConnections 在任意时间可存在的活动(正在使⽤)连接数量,默认值:10
          • poolMaximumIdleConnections 任意时间可能存在的空闲连接数。
          • 其它…
      • JNDI:采⽤服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到 DataSource是不⼀样。如果不是web或者maven的war⼯程,JNDI是不能使⽤的。
        • property可以是(最多只包含以下两个属性):
          • initial_context 这个属性⽤来在 InitialContext 中寻找上下⽂(即, initialContext.lookup(initial_context))这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
          • data_source 这是引⽤数据源实例位置的上下⽂路径。提供了 initial_context 配置时会 在其返回的上下⽂中进⾏查找,没有提供时则直接在 InitialContext 中查找。
  • mappers:在mappers标签中可以配置多个sql映射⽂件的路径。
  • mapper:配置某个sql映射⽂件的路径
    • resource属性:使⽤相对于类路径的资源引⽤⽅式
    • url属性:使⽤完全限定资源定位符(URL)⽅式

4.1 environment

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 default="dev">-->
    <!--默认使⽤⽣产环境-->
    <environments default="production">
        <!--开发环境-->
        <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/po wernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
        <!--⽣产环境-->
        <environment id="production">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/my batis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </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 SqlSession
        FactoryBuilder();
        // 使⽤默认数据库
        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);
    }
}

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">
            <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 SqlSessionFact
        oryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(R
        esources.getResourceAsStream("mybatis-config2.xml"));
        // 获取SqlSession对象
        SqlSession sqlSession=sqlSessionFactory.openSession();
        // 执⾏SQL
        int count=sqlSession.insert("insertCar",car);
        System.out.println("插⼊了⼏条记录:"+count);
	}

当事务管理器是:JDBC

  • 采⽤JDBC的原⽣事务机制:
    • 开启事务:conn.setAutoCommit(false);
    • 处理业务…
    • 提交事务:conn.commit();

当事务管理器是:MANAGED

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

4.3 dataSource

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="JNDI">
            <!--使⽤数据库 连接池。【这个连接池是mybatis⾃⼰实现的。】-->
            <dataSource type="POOLED">
            <!--不会使⽤连接池,每⼀次都会新建JDBC连接对象-->
            <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 SqlSessionFact
        oryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(R
        esources.getResourceAsStream("mybatis-config3.xml"));
        // 获取SqlSession对象
        SqlSession sqlSession=sqlSessionFactory.openSession(true);
        // 执⾏SQL
        int count=sqlSession.insert("insertCar",car);
        System.out.println("插⼊了⼏条记录:"+count);
        // 关闭会话
        sqlSession.close();
    }

结论

  • UNPOOLED不会使⽤连接池,每⼀次都会新建JDBC连接对象。
  • POOLED会使⽤数据库 连接池。【这个连接池是mybatis⾃⼰实现的。】
  • JNDI表示对接JNDI服务器中的连接池。这种⽅式给了我们可以使⽤第三⽅连接池的接⼝。如果 想使⽤dbcp、c3p0、druid(德鲁伊)等,需要使⽤这种⽅式。

type="POOLED"的时候,它的属性有哪些?

<?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"/>
                <!--最多空闲数量-->
                <property name="poolMaximumIdleConnections" value="1"/>
                <!--强⾏回归池的时间-->
                <property name="poolMaximumCheckoutTime" value="20000"/>
                <!--这是⼀个底层设置,如果获取连接花费了相当⻓的时间,连接池会打印状 态⽇志并重新尝试获取⼀个连接(避免在误配置的情况下⼀直失败且不打印⽇志),默认值:20000毫秒(即 20 秒)。-->
                <property name="poolTimeToWait" value="20000"/>
            </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个连接要空闲下来的时候,连接池会选择关闭该连接对象。来减少数据库的开销。
  • 需要根据系统的并发情况,来合理调整连接池最⼤连接数以及最多空闲数量。充分发挥数据库连接池的 性能。【可以根据实际情况进⾏测试,然后调整⼀个合理的数量。】

在这里插入图片描述

4.4 properties

mybatis提供了更加灵活的配置,连接数据库的信息可以单独写到⼀个属性资源⽂件中,假设在类的根路 径下创建jdbc.properties⽂件,配置如下:
jdbc.properties Properties

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

在mybatis核⼼配置⽂件中引⼊并使⽤:
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">
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="root"/>
    </properties>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--${key}使⽤-->
                <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>

编写Java程序进⾏测试:
ConfigurationTest.testProperties

    @Test
    public void testProperties()throws Exception{
    
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFacto
        ryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(Re
        sources.getResourceAsStream("mybatis-config4.xml"));
        SqlSession sqlSession=sqlSessionFactory.openSession();
        Object car=sqlSession.selectOne("selectCarByCarNum");
        System.out.println(car);
    }

properties两个属性:

  • resource:这个属性从类的根路径下开始加载。【常⽤的。】
  • url:从指定的url加载,假设⽂件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。

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

  • 第⼀种⽅式:查看dtd约束⽂件。
  • 第⼆种⽅式:通过idea的报错提示信息。【⼀般采⽤这种⽅式】

4.5 mapper

mapper标签⽤来指定SQL映射⽂件的路径,包含多种指定⽅式,这⾥先主要看其中两种:
第⼀种:resource,从类的根路径下开始加载【⽐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,从指定的url位置加载
假设CarMapper.xml⽂件放在d盘的根下,这个配置就需要这样写:
mybatis-config4.xml

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

五.在WEB中应用MyBatis(使用MVC架构模式)

5.1 准备数据库表t_act

drop table if exists t_act;
create table t_act(
    id int primary key auto_increment,
    actno varchar(255),
    balance decimal
);
INSERT INTO `t_act` (`id`, `actno`, `balance`) VALUES (1, 'act001', 50000.0);
INSERT INTO `t_act` (`id`, `actno`, `balance`) VALUES (2, 'act002', 0);

5.2 实现步骤

第一步:搭建环境,导入依赖

第二步:前端页面index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>银行账户转账</title>
</head>
<body>
<form action="/bank/transfer" method="post">
    转出账号:<input type="text" name="fromActno"><br>
    转入账号:<input type="text" name="toActno"><br>
    转账金额:<input type="text" name="money"><br>
    <input type="submit" value="转账">
</form>
</body>
</html>

第三步:创建包

  • com.powernode.bank.pojo
  • com.powernode.bank.dao
  • com.powernode.bank.dao.impl
  • com.powernode.bank.service
  • com.powernode.bank.service.impl
  • com.powernode.bank.web
  • com.powernode.bank.exception
  • com.powernode.bank.utils:将之前编写的SqlSessionUtil⼯具类拷⻉到该包下。

第四步:定义pojo类:Account

package com.jm.bank.pojo;

/** * 账户类,封装数据 */
public class Account {
    
    private Long id;
    private String actno;
    private Double balance;

    @Override
    public String toString() {
    
        return "Account{" +
                "id=" + id +
                ", actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }

    public Long getId() {
    
        return id;
    }

    public void setId(Long id) {
    
        this.id = id;
    }

    public String getActno() {
    
        return actno;
    }

    public void setActno(String actno) {
    
        this.actno = actno;
    }

    public Double getBalance() {
    
        return balance;
    }

    public void setBalance(Double balance) {
    
        this.balance = balance;
    }

    public Account(Long id, String actno, Double balance) {
    
        this.id = id;
        this.actno = actno;
        this.balance = balance;
    }

    public Account() {
    
    }
}

第五步:编写AccountDao接口,以及AccountDaoImpl实现类

AccountDao(接口)

package com.jm.bank.dao;

import com.jm.bank.pojo.Account;

public interface AccountDao {
    
    Account selectByActno(String actno);
    int updateByActno(Account account);
}

TransferDaoImpl(实现类)

package com.jm.bank.dao.impl;

import com.jm.bank.dao.AccountDao;
import com.jm.bank.pojo.Account;
import com.jm.bank.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class TransferDaoImpl implements AccountDao {
    
    @Override
    public Account selectByActno(String actno) {
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Account account=sqlSession.selectOne("selectByActno",actno);
        return account;
    }

    @Override
    public int updateByActno(Account account) {
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count=sqlSession.update("updateByActno",account);
        return count;
    }
}

第六步:编写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">

<mapper namespace="account">

    <select id="selectByActno" resultType="com.jm.bank.pojo.Account">
        select *
        from t_act
        where actno=#{actno};
    </select>

    <update id="updateByActno">
        update t_act
        set balance=#{balance}
        where actno=#{actno};
    </update>
</mapper>

第七步:编写AccountService接口以及AccountServiceImpl

AccountService(接口)

package com.jm.bank.service;

import com.jm.bank.exception.MoneyNotEnoughException;
import com.jm.bank.exception.TransferException;

public interface AccountService {
    
    void transfer(String fromActno,String toActno,double money) throws MoneyNotEnoughException, TransferException;
}

TransferServiceImpl(实现类)

package com.jm.bank.service.impl;

import com.jm.bank.dao.AccountDao;
import com.jm.bank.dao.impl.TransferDaoImpl;
import com.jm.bank.exception.MoneyNotEnoughException;
import com.jm.bank.exception.TransferException;
import com.jm.bank.pojo.Account;
import com.jm.bank.service.AccountService;

public class TransferServiceImpl implements AccountService {
    
    AccountDao accountDao=new TransferDaoImpl();
    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
    

        Account fromAct = accountDao.selectByActno(fromActno);
        Account toAct = accountDao.selectByActno(toActno);
        //1.判断转账金额是某足够,不够抛异常
        if(fromAct.getBalance()<money){
    
            throw new MoneyNotEnoughException("对不起,余额不足,无法转账!");
        }
        //2.更新转账后用户的金额
        //先在内存中更新数据
        fromAct.setBalance(fromAct.getBalance()-money);
        toAct.setBalance(toAct.getBalance()+money);
        //再更新数据库中的数据
        int count=accountDao.updateByActno(fromAct);
        count+=accountDao.updateByActno(toAct);
        if(count!=2){
    
            throw new TransferException("转账异常,未知原因!");
        }
    }
}

第⼋步:编写AccountServlet

package com.jm.bank.web;

import com.jm.bank.exception.MoneyNotEnoughException;
import com.jm.bank.exception.TransferException;
import com.jm.bank.service.AccountService;
import com.jm.bank.service.impl.TransferServiceImpl;
import com.jm.bank.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/transfer")
public class TransferServlet extends HttpServlet {
    
    AccountService accountService=new TransferServiceImpl();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
        //添加事务代码
        SqlSession sqlSession = SqlSessionUtil.openSession();

        //获取表单数据
        String fromActno = request.getParameter("fromActno");
        String toActno = request.getParameter("toActno");
        double money = Double.parseDouble(request.getParameter("money"));
        try {
    
            //调用业务逻辑层方法,进行操作
            accountService.transfer(fromActno,toActno,money);
            //到这里表示转账成功
            //调用view展示结果
            response.sendRedirect(request.getContextPath()+"/success.html");
        } catch (MoneyNotEnoughException e) {
    
            response.sendRedirect(request.getContextPath()+"/error1.html");
        } catch (TransferException e) {
    
            response.sendRedirect(request.getContextPath()+"/error2.html");
        }

        //提交事务
        sqlSession.commit();
        //关闭事务
        SqlSessionUtil.close(sqlSession);
    }
}

5.3 MyBatis对象作用域

5.3.1 SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

5.3.2 SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

5.3.3 SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

5.4 事务问题

为了保证service和dao中使⽤的SqlSession对象是同⼀个,可以将SqlSession对象存放到ThreadLocal当中。修改SqlSessionUtil⼯具类:

package com.jm.bank.util;


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;

/** * mybatiss工具类 */
public class SqlSessionUtil {
    

    private SqlSessionUtil(){
    }
    private static SqlSessionFactory sqlSessionFactory;

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

    //全局的,一个服务器当中定义一个即可
    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();
        }
    }

}


5.5 分析当前程序存在的问题

package com.jm.bank.dao.impl;

import com.jm.bank.dao.AccountDao;
import com.jm.bank.pojo.Account;
import com.jm.bank.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class TransferDaoImpl implements AccountDao {
    
    @Override
    public Account selectByActno(String actno) {
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Account account=sqlSession.selectOne("selectByActno",actno);
        return account;
    }

    @Override
    public int updateByActno(Account account) {
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count=sqlSession.update("updateByActno",account);
        return count;
    }
}

不难发现,这个dao实现类中的⽅法代码很固定,基本上就是⼀⾏代码,通过SqlSession对象调⽤ insert、delete、update、select等⽅法,这个类中的⽅法没有任何业务逻辑,既然是这样,这个类我们能不能动态的⽣成,以后可以不写这个类吗?
答案:可以。

六.MyBatis中接口代理机制及使用

直接调⽤以下代码即可获取dao接⼝的代理类,mybatis内部已经实现了。

AccountDao accountDao = (AccountDao)sqlSession.getMapper(AccountDao.class);

使⽤以上代码的前提是:AccountMapper.xml⽂件中的namespace必须和dao接⼝的全限定名称⼀致,id必须和dao接⼝中⽅法名⼀致。

七.MyBatis小技巧

7.1 #{}和${}

#{}:先编译sql语句,再给占位符传值,底层是PreparedStatement实现。可以防⽌sql注⼊,⽐较常⽤。

${}:先进⾏sql语句拼接,然后再编译sql语句,底层是Statement实现。存在sql注⼊现象。只有在需要 进⾏sql语句关键字拼接的情况下才会⽤到。

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

当需要进⾏sql语句关键字拼接的时候。必须使⽤${}
需求:通过向sql语句中注⼊asc或desc关键字,来完成数据的升序或降序排列。
order by carNum #{key}

7.1.2 拼接表名

使⽤#{}会是这样:select * from ‘t_car’
使⽤${}会是这样:select * from t_car

7.1.3 批量删除

使⽤#{} :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)

7.1.4 模糊查询

使⽤${}
brand like ‘%${brand}%’

使⽤#{}
第⼀种:concat函数
brand like concat(‘%’,#{brand},‘%’)
第⼆种:双引号⽅式
brand like “%”#{brand}“%”

7.2 typeAliases 起别名

在mybatis-config.xml⽂件中使⽤typeAliases标签来起别名,包括两种⽅式:
第⼀种⽅式:typeAlias

<typeAliases>
 <typeAlias type="com.powernode.mybatis.pojo.Car" alias="Car"/>
</typeAliases>
  • ⾸先要注意typeAliases标签的放置位置,如果不清楚的话,可以看看错误提示信息。
  • typeAliases标签中的typeAlias可以写多个。
  • typeAlias:
    • type属性:指定给哪个类起别名
    • alias属性:别名。
      • alias属性不是必须的,如果缺省的话,type属性指定的类型名的简类名作为别名。
      • alias是⼤⼩写不敏感的。也就是说假设alias=“Car”,再⽤的时候,可以CAR,也可以car, 也可以Car,都⾏。

第⼆种⽅式:package
如果⼀个包下的类太多,每个类都要起别名,会导致typeAlias标签配置较多,所以mybatis⽤提供 package的配置⽅式,只需要指定包名,该包下的所有类都⾃动起别名,别名就是简类名。并且别名不 区分⼤⼩写。

<typeAliases>
 <package name="com.powernode.mybatis.pojo"/>
</typeAliases> 

package也可以配置多个的。

在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">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
 <select id="selectAll" resultType="CAR">
 select
 id,car_num as carNum,brand,guide_price as guidePrice,produce_t
ime as produceTime,car_type as carType
 from
 t_car
 order by carNum ${key}
 </select>
 <select id="selectByCarType" resultType="car">
 select
 id,car_num as carNum,brand,guide_price as guidePrice,produce_t
ime as produceTime,car_type as carType
 from
 t_car
 where
 car_type = '${carType}'
 </select>
</mapper>

7.3 mappers

SQL映射⽂件的配置⽅式包括四种:

  • resource:从类路径中加载
  • url:从指定的全限定资源路径中加载
  • class:使⽤映射器接⼝实现类的完全限定类名
  • package:将包内的映射器接⼝实现全部注册为映射器

7.3.1 resource

这种⽅式是从类路径中加载配置⽂件,所以这种⽅式要求SQL映射⽂件必须放在resources⽬录下或其子⽬录下。

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

7.3.2 url

这种⽅式显然使⽤了绝对路径的⽅式,这种配置对SQL映射⽂件存放的位置没有要求,随意。

<mappers>
 <mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>

7.3.3 class

如果使⽤这种⽅式必须满⾜以下条件:

  • SQL映射⽂件和mapper接⼝放在同⼀个⽬录下。
  • SQL映射⽂件的名字也必须和mapper接⼝名⼀致。
<!-- 使⽤映射器接⼝实现类的完全限定类名 -->
<mappers>
 <mapper class="com.powernode.mybatis.mapper.CarMapper"/>
</mappers>

7.3.4 package

如果class较多,可以使⽤这种package的⽅式,但前提条件和上⼀种⽅式⼀样。

<!-- 将包内的映射器接⼝实现全部注册为映射器 -->
<mappers>
 <package name="com.jm.mapper"/>
</mappers>

7.4 idea配置文件模板

mybatis-config.xml和SqlMapper.xml⽂件可以在IDEA中提前创建好模板,以后通过模板创建配置⽂件。
在这里插入图片描述

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=""></properties>

    <!--起别名-->
    <typeAliases>
        <package name=""/>
    </typeAliases>

    <!--环境配置-->
    <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>
        <package name=""/>
    </mappers>
    
</configuration>

SqlMapper.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="">
    
</mapper>

7.5 插入数据时获取自动生成的主键

前提是:主键是⾃动⽣成的。

插⼊⼀条新的记录之后,⾃动⽣成了主键,⽽这个主键需要在其他表中使⽤时。
插⼊⼀个⽤户数据的同时需要给该⽤户分配⻆⾊:需要将⽣成的⽤户的id插⼊到⻆⾊表的user_id字段上。

第⼀种⽅式:可以先插⼊⽤户数据,再写⼀条查询语句获取id,然后再插⼊user_id字段。【⽐较麻烦】
第⼆种⽅式:mybatis提供了⼀种⽅式更加便捷。

 <!-- useGeneratedKeys="true" 使用自动生成的主键值 keyProperty="id" 指定主键值赋值给对象的哪个属性,这个就表示将主键赋值给Car的id属性 -->
<insert id="insertUseGeneratedKeys" useGeneratedKeys="true" keyProperty="id">
 insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) 
 values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert> 

八、MyBatis参数处理

8.1 单个简单类型参数

简单类型包括:

  • byte short int long float double char
  • Byte Short Integer Long Float Double Character
  • String
  • java.util.Date
  • java.sql.Date
    <select id="selectById" resultType="Student" parameterType="java.lang.Long">
        select * from t_student where id=#{id};
    </select>
    <select id="selectByName" resultType="Student">
        select *
        from t_student where name=#{name};
    </select>
    <select id="selectByBirth" resultType="Student">
        select *
        from t_student where birth=#{birth};
    </select>
    <select id="selectBySex" resultType="Student">
        select *
        from t_student where sex=#{sex};
    </select>

补充:sql语句中的javaType,jdbcType,以及select标签中的parameterType属性,都是⽤来帮助 mybatis进⾏类型确定的。

  • javaType:可以省略
  • jdbcType:可以省略
  • parameterType:可以省略

如果参数只有⼀个的话,#{} ⾥⾯的内容就随便写了。对于 ${} 来说,注意加单引号。

8.2 Map参数

准备map

	 // 准备Map
	 Map<String,Object> paramMap = new HashMap<>();
	 paramMap.put("nameKey", "张三");
	 paramMap.put("ageKey", 20);

编写sql语句

	<select id="selectByParamMap" resultType="student">
	 	select * from t_student where name = #{nameKey} and age = #{ageKey}
	</select>

这种⽅式是⼿动封装Map集合,将每个条件以key和value的形式存放到集合中。然后在使⽤的时候通过# {map集合的key}来取值。

8.3 实体类参数

准备数据

	Student student = new Student(null,"王二麻子",24,1.68,new Date(),'男');

编写sql语句

	<insert id="insertByStudent">
	    insert into t_student (id, name, age, height, birth, sex)
	    values (null,#{name},#{age},#{height},#{birth},#{sex});
	</insert>

8.4 多参数

java程序

	SqlSession sqlSession = SqlSessionUtil.openSession();
	StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
	List<Student> students = mapper.selectByNameAndAge("李四",18);

sql语句

	<select id="selectByNameAndAge" resultType="com.jm.pojo.Student">
	    select *
	    from t_student where name=#{name} and age=#{age};
	</select>

实现原理:实际上在mybatis底层会创建⼀个map集合,以arg0/param1为key,以⽅法上的参数为 value
参数为

  • arg0/arg1/agr2/arg3…
  • 或者param1/param2/param3…
  • 添加Param(“”)注解后,取代arg某的位置

8.5 @Param注解(命名参数)

使⽤ @Param注解即可。这样可以增强可读性。

	List<Student> selectByNameAndAge(@Param("name") String name,@Param("age") Integer age);
    <select id="selectByNameAndAge" resultType="com.jm.pojo.Student">
        select *
        from t_student where name=#{name} and age=#{age};
    </select>
    List<Student> students = mapper.selectByNameAndAge("李四",18);
    students.forEach(student -> System.out.println(student));

核⼼:@Param(“这⾥填写的其实就是map集合的key”)

九、MyBatis查询语句

9.1 返回Car

当查询的结果,有对应的实体类,并且查询结果只有⼀条时:

 Car selectById(Long id);

9.2 返回List

当查询的记录条数是多条的时候,必须使⽤集合接收。如果使⽤单个实体类接收会出现异常。

List<Car> selectAll();

查询结果是⼀条的话可以使⽤List集合接收吗?当然可以。
如果查询结果为多条,用单个Car实体类接收,会报异常:TooManyResultsException

9.3 返回Map

当返回的数据,没有合适的实体类对应的话,可以采⽤Map集合接收。字段名做key,字段值做value。 查询如果可以保证只有⼀条数据,则返回⼀个Map集合即可。

Map<String, Object> selectByIdRetMap(Long id);
<select id="selectByIdRetMap" resultType="map">
 select id,car_num carNum,brand,guide_price guidePrice,produce_time produc
eTime,car_type carType from t_car where id = #{id}
</select>

resultMap=“map”,这是因为mybatis内置了很多别名。

9.4 返回List

查询结果条数⼤于等于1条数据,则可以返回⼀个存储Map集合的List集合。

List<Map<String,Object>> selectAllRetListMap();

9.5 返回Map<String,Map>

拿Car的id做key,以后取出对应的Map集合时更⽅便。

//添加注解,表示大Map
@MapKey("id")
Map<Long,Map<String,Object>> selectAllRetMap();

9.6 resultMap结果映射

查询结果的列名和java对象的属性名对应不上怎么办?

  • 第⼀种⽅式:as 给列起别名
  • 第⼆种⽅式:使⽤resultMap进⾏结果映射
  • 第三种⽅式:是否开启驼峰命名⾃动映射(配置settings)

使⽤resultMap进⾏结果映射

    <!-- resultMap: id:这个结果映射的标识,作为select标签的resultMap属性的值。 type:结果集要映射的类。可以使⽤别名。 -->
    <resultMap id="carResultMap" type="car">
        <!--对象的唯⼀标识,官⽅解释是:为了提⾼mybatis的性能。建议写上。-->
        <id property="id" column="id"/>
        <result property="carNum" column="car_num"/>
        <!--当属性名和数据库列名⼀致时,可以省略。但建议都写上。-->
        <!--javaType⽤来指定属性类型。jdbcType⽤来指定列类型。⼀般可以省略。-->
        <result property="brand" column="brand" javaType="string" jdbcType="VARCHAR"/>
        <result property="guidePrice" column="guide_price"/>
        <result property="produceTime" column="produce_time"/>
        <result property="carType" column="car_type"/>
    </resultMap>
    <!--resultMap属性的值必须和resultMap标签中id属性值⼀致。-->
    <select id="selectAllByResultMap" resultMap="carResultMap">
        select *
        from t_car
    </select>

是否开启驼峰命名⾃动映射
使⽤这种⽅式的前提是:属性名遵循Java的命名规范,数据库表的列名遵循SQL的命名规范。
Java命名规范:⾸字⺟⼩写,后⾯每个单词⾸字⺟⼤写,遵循驼峰命名⽅式。
SQL命名规范:全部⼩写,单词之间采⽤下划线分割。

⽐如以下的对应关系:
在这里插入图片描述

如何启⽤该功能,在mybatis-config.xml⽂件中进⾏配置:

<!--放在properties标签后⾯-->
<settings>
 <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

9.7 返回总记录条数

<!--long是别名,可参考mybatis中文网。-->
<select id="selectTotal" resultType="long">
 select count(*) from t_car
</select>

十、动态SQL

10.1 if标签

<?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="selectByMultiCondition" resultType="car">
        select * from t_car where
        <if test="brand != null and brand != ''">
            brand like #{brand}"%"
        </if>
        <if test="guidePrice != null and guidePrice != ''">
            and guide_price >= #{guidePrice}
        </if>
        <if test="carType != null and carType != ''">
            and car_type = #{carType}
        </if>
    </select>
</mapper>
  • 如果第⼀个条件为空,剩下两个条件不为空,会是怎样呢?
    • 报错了,SQL语法有问题,where后⾯出现了and。
    • 解决:可以where后⾯添加⼀个恒成⽴的条件。
  • 如果三个条件都是空,有影响吗?
    • 需要在第一个条件前面加and

10.2 where标签

where标签的作⽤:让where⼦句更加动态智能。

  • 所有条件都为空时,where标签保证不会生成where⼦句。
  • 自动去除某些条件前面多余的and或or。
<select id="selectByMultiConditionWithWhere" resultType="car">
    select * from t_car
    <where>
        <if test="brand != null and brand != ''">
            and brand like #{brand}"%"
        </if>
        <if test="guidePrice != null and guidePrice != ''">
            and guide_price >= #{guidePrice}
        </if>
        <if test="carType != null and carType != ''">
            and car_type = #{carType}
        </if>
    </where>
</select>

10.3 trim标签

trim标签的属性:

  • prefix:在trim标签中的语句前添加内容
  • suffix:在trim标签中的语句后添加内容
  • prefixOverrides:前缀覆盖掉(去掉)
  • suffixOverrides:后缀覆盖掉(去掉)
<select id="selectByMultiConditionWithTrim" resultType="car">
    select * from t_car
    <trim prefix="where" suffixOverrides="and|or">
        <if test="brand != null and brand != ''">
            brand like #{brand}"%" and
        </if>
        <if test="guidePrice != null and guidePrice != ''">
            guide_price >= #{guidePrice} and
        </if>
        <if test="carType != null and carType != ''">
            car_type = #{carType}
        </if>
    </trim>
</select>

10.4 set标签

  • 主要使⽤在update语句当中
  • 作用:⽤来⽣成set关键字,同时去掉最后多余的“,”
  • 比如我们只更新提交的不为空的字段,如果提交的数据是空或者"",那么这个字段我们将不更新。
<update id="updateWithSet">
    update t_car
    <set>
        <if test="carNum != null and carNum != ''">car_num = #{carNum},</if>
        <if test="brand != null and brand != ''">brand = #{brand},</if>
        <if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},</if>
        <if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},</if>
        <if test="carType != null and carType != ''">car_type = #{carType},</if>
    </set>
    where id = #{id}
</update>

10.5 choose when otherwise

这三个标签是在⼀起使⽤的:
语法格式:

<choose>
    <when></when>
    <when></when>
    <when></when>
    <otherwise></otherwise>
</choose>

等同于:

if(){
    
 
}else if(){
    
 
}else if(){
    
 
}else if(){
    
 
}else{
     

}

sql语句

<select id="selectWithChoose" resultType="car">
    select * from t_car
    <where>
        <choose>
            <when test="brand != null and brand != ''">
                brand like #{brand}"%"
            </when>
            <when test="guidePrice != null and guidePrice != ''">
                guide_price >= #{guidePrice}
            </when>
            <otherwise>
                produce_time >= #{produceTime}
            </otherwise>
        </choose>
    </where>
</select>

10.6 foreach标签

循环数组或集合,动态⽣成sql,⽐如这样的SQL:

批量删除

  • 用in来删除
<!-- collection:集合或数组 item:集合或数组中的元素 separator:分隔符 open:foreach标签中所有内容的开始 close:foreach标签中所有内容的结束 -->
<delete id="deleteBatchByForeach">
    delete from t_car where id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>
  • ⽤or来删除
<delete id="deleteBatchByForeach2">
    delete from t_car where
    <foreach collection="ids" item="id" separator="or">
        id = #{id}
    </foreach>
</delete>

批量添加

<insert id="insertBatchByForeach">
    insert into t_car values
    <foreach collection="cars" item="car" separator=",">
        (null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
    </foreach>
</insert>

10.7 sql标签与include标签

  • sql标签⽤来声明sql⽚段
  • include标签⽤来将声明的sql⽚段包含到某个sql语句当中
  • 作⽤:代码复⽤。易维护
<sql id="carCols">id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType</sql>

<select id="selectAllRetMap" resultType="map">
    select <include refid="carCols"/> from t_car
</select>

<select id="selectAllRetListMap" resultType="map">
    select <include refid="carCols"/> carType from t_car
</select>

<select id="selectByIdRetMap" resultType="map">
    select <include refid="carCols"/> from t_car where id = #{id}
</select>

十一、MyBatis的高级映射及延迟加载

11.1 多对一

在这里插入图片描述

多种⽅式,常⻅的包括三种:

  • 第⼀种⽅式:⼀条SQL语句,级联属性映射。
  • 第⼆种⽅式:⼀条SQL语句,association。
  • 第三种⽅式:两条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">
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper">
    <resultMap id="studentResultMap" type="Student">
        <id property="sid" column="sid"/>
        <result property="sname" column="sname"/>
        <result property="clazz.cid" column="cid"/>
        <result property="clazz.cname" column="cname"/>
    </resultMap>
    <select id="selectBySid" resultMap="studentResultMap">
        select s.*, c.* from t_student s join t_clazz c on s.cid = c.cid where sid = #{sid}
    </select>
</mapper>

第⼆种方式:association

<resultMap id="studentResultMap" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <association property="clazz" javaType="Clazz">
        <id property="cid" column="cid"/>
        <result property="cname" column="cname"/>
    </association>
</resultMap>

第三种方式:分步查询

分步优点:

  • 第⼀个优点:代码复⽤性增强。
  • 第⼆个优点:⽀持延迟加载。【暂时访问不到的数据可以先不查询。提⾼程序的执⾏效率。】

主(多的一方)

<resultMap id="studentResultMap" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <association property="clazz" select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid" column="cid"/>
</resultMap> 
<select id="selectBySid" resultMap="studentResultMap">
    select s.* from t_student s where sid = #{sid}
</select>

副(一的一方)

<?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.ClazzMapper">
    <select id="selectByCid" resultType="Clazz">
        select * from t_clazz where cid = #{cid}
    </select>
</mapper>

11.2 多对⼀延迟加载

1)要想⽀持延迟加载,⾮常简单,只需要在association标签中添加fetchType=“lazy”即可。

<resultMap id="studentResultMap" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <association property="clazz" select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid" column="cid" fetchType="lazy"/>
</resultMap>

2)在mybatis中如何开启全局的延迟加载呢?需要setting配置,如下:

<settings>
 	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

把SQL映射文件的fetchType="lazy"去掉。

3)开启全局延迟加载之后,所有的sql都会⽀持延迟加载,如果某个sql你不希望它⽀持延迟加载怎么办呢?
将fetchType设置为eager:

<resultMap id="studentResultMap" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <association property="clazz" select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid" column="cid" fetchType="eager"/>
</resultMap>

这样的话,针对某个特定的sql,就关闭了延迟加载机制。

11.3 ⼀对多

在这里插入图片描述

⼀对多的实现,通常是在⼀的⼀⽅中有List集合属性。

⼀对多的实现通常包括两种实现⽅式:

  • 第⼀种⽅式:collection
  • 第⼆种⽅式:分步查询

第⼀种方式:collection

<resultMap id="clazzResultMap" type="Clazz">
    <id property="cid" column="cid"/>
    <result property="cname" column="cname"/>
    <collection property="stus" ofType="Student">
        <id property="sid" column="sid"/>
        <result property="sname" column="sname"/>
    </collection>
</resultMap> 
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">
    select * from t_clazz c join t_student s on c.cid = s.cid where c.cid = #{cid}
</select>

注意是ofType,表示“集合中的类型”。

第⼆种方式:分步查询

主(一的一方)

<resultMap id="clazzResultMap" type="Clazz">
    <id property="cid" column="cid"/>
    <result property="cname" column="cname"/>
    <!--主要看这⾥-->
    <collection property="stus" select="com.powernode.mybatis.mapper.StudentMapper.selectByCid" column="cid"/>
</resultMap>
 <!--sql语句也变化了-->
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">
    select * from t_clazz c where c.cid = #{cid}
</select>

副(多的一方)

<select id="selectByCid" resultType="Student">
 	select * from t_student where cid = #{cid}
</select>

11.4 ⼀对多延迟加载

⼀对多延迟加载机制和多对⼀是⼀样的。同样是通过两种⽅式:

  • 第⼀种:fetchType=“lazy”
  • 第⼆种:修改全局的配置setting,lazyLoadingEnabled=true,如果开启全局延迟加载,想让某个 sql不使⽤延迟加载:fetchType=“eager”

十二、MyBatis的缓存

缓存:cache

缓存的作⽤:通过减少IO的⽅式,来提⾼程序的执⾏效率。

mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直 接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO。另⼀⽅⾯不再执⾏繁琐的查找算法。效率大大提升。

mybatis缓存包括:

  • ⼀级缓存:将查询到的数据存储到SqlSession中。
  • ⼆级缓存:将查询到的数据存储到SqlSessionFactory中。
  • 或者集成其它第三⽅的缓存:⽐如EhCache【Java语⾔开发的】、Memcache【C语⾔开发的】等。

缓存只针对于DQL语句,也就是说缓存机制只对应select语句。

12.1 ⼀级缓存

12.1.1 ⼀级缓存默认是开启的,范围是sqlSession

不需要做任何配置。

原理:只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存。

    @Test
    public void testSelectById2() throws Exception {
    
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(165L);
        System.out.println(car1);
        Car car2 = mapper1.selectById(165L);
        System.out.println(car2);
    }

在这里插入图片描述

12.1.2 不走缓存的两种情况?

  • 第⼀种:不同的SqlSession对象。
  • 第⼆种:查询条件变化了。
    @Test
    public void testSelectById() throws Exception {
    
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(166L);
        System.out.println(car1);
        //查询条件变了,不走缓存
        Car car2 = mapper1.selectById(165L);
        System.out.println(car2);

        //不同的sqlSession对象,不走缓存
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
        Car car3 = mapper2.selectById(165L);
        System.out.println(car3);
        Car car4 = mapper2.selectById(165L);
        System.out.println(car4);
    }

在这里插入图片描述

12.1.3 ⼀级缓存失效情况的两种情况?

  • 第⼀种:第⼀次查询和第⼆次查询之间,⼿动清空了⼀级缓存
  • 第⼆种:第⼀次查询和第⼆次查询之间,执⾏了增删改操作。(无论哪张表)
    @Test
    public void testSelectById2() throws Exception {
    
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(165L);
        System.out.println(car1);
        //手动清除一级缓存
        //sqlSession1.clearCache();

        //第⼀次查询和第⼆次查询之间执行update、insert、delete操作,一级缓存失效
        mapper1.insert(new Car(null,"8888","兰德酷路泽",67.0,"2018-12-05","新能源"));

        Car car2 = mapper1.selectById(165L);
        System.out.println(car2);
    }

12.2 二级缓存

12.2.1 二级缓存的范围是SqlSessionFactory

12.2.2 使⽤二级缓存需要具备以下几个条件:

1.<setting name=“cacheEnabled” value=“true”> 全局性地开启或关闭所有映射器配置⽂件中已配置 的任何缓存。默认就是true⽆需设置。
2. 在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加配置:<cache />
3. 使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝
4. SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中。此时⼆级缓存才可⽤。

测试二级缓存:

    @Test
    public void testSelectById3() throws Exception{
    
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));

        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(165L);
        System.out.println(car1);

        //关键一步
        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
        Car car2 = mapper2.selectById(165L);
        System.out.println(car2);
    }

在这里插入图片描述

12.2.3 二级缓存的失效条件?

只要两次查询之间出现了增删改操作。⼆级缓存就会失效。【⼀级缓存也会失效】

12.2.4 二级缓存的相关配置<cache />

在这里插入图片描述

  1. eviction:指定从缓存中移除某个对象的淘汰算法。默认采⽤LRU策略。
    a. LRU:Least Recently Used。最近最少使⽤。优先淘汰在间隔时间内使⽤频率最低的对象。(其 实还有⼀种淘汰算法LFU,最不常⽤。)
    b. FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊⼆级缓存的对象最先被淘汰。
    c. SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
    d. WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
  2. flushInterval:
    ⼆级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存⾜够⼤,⼀ 直会向⼆级缓存中缓存数据。除⾮执⾏了增删改。
  3. readOnly:
    a. true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好。但是多线程并发可能 会存在安全问题。
    b. false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般。但安全。
  4. size:
    设置⼆级缓存中最多可存储的java对象数量。默认值1024。

12.3 MyBatis集成EhCache

12.3.1 集成EhCache是为了代替mybatis自带的⼆级缓存。

⼀级缓存是⽆法替代的。
mybatis对外提供了接⼝,也可以集成第三⽅的缓存组件。⽐如EhCache、Memcache等。都可以。 EhCache是Java写的。Memcache是C语⾔写的。所以mybatis集成EhCache较为常⻅。

12.3.2 操作步骤

第⼀步:引⼊mybatis整合ehcache的依赖

<!--mybatis集成ehcache的组件-->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.2</version>
</dependency>
<!--ehcache需要slf4j的⽇志组件,log4j不好使-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.11</version>
    <scope>test</scope>
</dependency>

第⼆步:在类的根路径下新建echcache.xml⽂件,并提供以下配置信息。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
    <!--磁盘存储:将缓存中暂时不使⽤的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
    <diskStore path="e:/ehcache"/>

    <!--defaultCache:默认的管理策略-->
    <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有 效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
    <!--maxElementsInMemory:在内存中缓存的element的最⼤数⽬-->
    <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
    <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-->
    <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多⻓时间没有被访问就会失 效。只对eternal为false的有效。默认值0,表示⼀直可以访问-->
    <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。 只对eternal为false的有效。默认值0,表示⼀直可以访问-->
    <!--memoryStoreEvictionPolicy:缓存的3 种清空策略-->
    <!--FIFO:first in first out (先进先出)-->
    <!--LFU:Less Frequently Used (最少使⽤).意思是⼀直以来最少被使⽤的。缓存的元 素有⼀个hit 属性,hit 值最⼩的将会被清出缓存-->
    <!--LRU:Least Recently Used(最近最少使⽤). (ehcache 默认值).缓存的元素有⼀ 个时间戳,当缓存容量满了,⽽⼜需要腾出地⽅来缓存新的元素的时候,那么现有缓存元素中时间戳 离当前时间最远的元素将被清出缓存-->
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
</ehcache>

第三步:修改SqlMapper.xml⽂件中的<cache/>标签,添加type属性。

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

第四步:编写测试程序使⽤。

十三、MyBatis的逆向工程

所谓的逆向⼯程是:根据数据库表逆向⽣成Java的pojo类SqlMapper.xml⽂件,以及Mapper接⼝类等。

要完成这个⼯作,需要借助别⼈写好的逆向⼯程插件。

思考:使⽤这个插件的话,需要给这个插件配置哪些信息?

  • pojo类名、包名以及⽣成位置。
  • SqlMapper.xml⽂件名以及⽣成位置。
  • Mapper接⼝名以及⽣成位置。
  • 连接数据库的信息。
  • 指定哪些表参与逆向⼯程。

13.1 操作步骤

第⼀步:基础环境准备
创建模块,打包⽅式:jar
第⼆步:在pom中添加逆向⼯程插件

  <!--定制构建过程-->
  <build>
    <!--可配置多个插件-->
    <plugins>
      <!--其中的⼀个插件:mybatis逆向⼯程插件-->
      <plugin>
        <!--插件的GAV坐标-->
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.4.1</version>
        <!--允许覆盖-->
        <configuration>
          <overwrite>true</overwrite>
        </configuration>
        <!--插件的依赖-->
        <dependencies>
          <!--mysql驱动依赖-->
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>

第三步:配置generatorConfig.xml
该⽂件名必须叫做:generatorConfig.xml
该⽂件必须放在类的根路径下。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- targetRuntime有两个值: MyBatis3Simple:⽣成的是基础版,只有基本的增删改查。 MyBatis3:⽣成的是增强版,除了基本的增删改查之外还有复杂的增删改查。 -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!--防⽌⽣成重复代码-->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>

        <commentGenerator>
            <!--是否去掉⽣成⽇期-->
            <property name="suppressDate" value="true"/>
            <!--是否去除注释-->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--连接数据库信息-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/jmpower" userId="root" password="021107">
        </jdbcConnection>

        <!-- ⽣成pojo包名和位置 -->
        <javaModelGenerator targetPackage="com.jm.pojo" targetProject="src/main/java">
            <!--是否开启⼦包-->
            <property name="enableSubPackages" value="true"/>
            <!--是否去除字段名的前后空⽩-->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- ⽣成SQL映射⽂件的包名和位置 -->
        <sqlMapGenerator targetPackage="com.jm.mapper" targetProject="src/main/resources">
            <!--是否开启⼦包-->
        <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- ⽣成Mapper接⼝的包名和位置 -->
        <javaClientGenerator type="xmlMapper" targetPackage="com.jm.mapper" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 表名和对应的实体类名-->
        <table tableName="t_car" domainObjectName="Car"/>

    </context>
</generatorConfiguration>

第四步:运⾏插件
在这里插入图片描述

13.2 测试逆向工程生成的是否好用

第⼀步:环境准备

  • 依赖:mybatis依赖、mysql驱动依赖、junit依赖、logback依赖
  • jdbc.properties
  • mybatis-config.xml
  • logback.xml

第⼆步:编写测试程序

package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.pojo.CarExample;
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;
import java.math.BigDecimal;
import java.util.List;
public class GeneratorTest {
    
    @Test
    public void testGenerator() throws Exception{
    
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder
                ().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        // 增
         /*Car car = new Car(); car.setCarNum("1111"); car.setBrand("⽐亚迪唐"); car.setGuidePrice(new BigDecimal(30.0)); car.setProduceTime("2010-10-12"); car.setCarType("燃油⻋"); int count = mapper.insert(car); System.out.println("插⼊了⼏条记录:" + count);*/
        
        // 删
         /*int count = mapper.deleteByPrimaryKey(83L); System.out.println("删除了⼏条记录:" + count);*/
        
        // 改
        // 根据主键修改
         /*Car car = new Car(); car.setId(89L); car.setGuidePrice(new BigDecimal(20.0)); car.setCarType("新能源"); int count = mapper.updateByPrimaryKey(car); System.out.println("更新了⼏条记录:" + count);*/
        // 根据主键选择性修改
         /*car = new Car(); car.setId(89L); car.setCarNum("3333"); car.setBrand("宝⻢520Li"); car.setProduceTime("1999-01-10"); count = mapper.updateByPrimaryKeySelective(car); System.out.println("更新了⼏条记录:" + count);*/
        
        // 查⼀个
        Car car = mapper.selectByPrimaryKey(89L);
        System.out.println(car);
        // 查所有
        List<Car> cars = mapper.selectByExample(null);
        cars.forEach(c -> System.out.println(c));
        // 多条件查询
        // QBC ⻛格:Query By Criteria ⼀种查询⽅式,⽐较⾯向对象,看不到sql语句。
        CarExample carExample = new CarExample();
        carExample.createCriteria()
                .andBrandEqualTo("丰⽥霸道")
                .andGuidePriceGreaterThan(new BigDecimal(60.0));
        carExample.or().andProduceTimeBetween("2000-10-11", "2022-10-11");
        mapper.selectByExample(carExample);
        sqlSession.commit();
    }
}

十四、MyBatis使用PageHelper

14.1 limit分页

mysql的limit后⾯两个数字:

  • 第⼀个数字:startIndex(起始下标。下标从0开始。)
  • 第⼆个数字:pageSize(每页显示的记录条数)

假设已知⻚码pageNum,还有每⻚显示的记录条数pageSize,第⼀个数字可以动态的获取吗?

  • startIndex = (pageNum - 1) * pageSize

14.2 PageHelper插件

使⽤PageHelper插件进⾏分⻚,更加的便捷。

操作步骤

第⼀步:引⼊依赖

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.3.1</version>
</dependency>

第⼆步:在mybatis-config.xml⽂件中配置插件

<plugins>
 	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

第三步:编写Java代码

   @Test
   public void testSelectAll(){
    
       SqlSession sqlSession = SqlSessionUtil.openSession();
       CarMapper mapper = sqlSession.getMapper(CarMapper.class);

       //一定要DQL之前,开启分页功能
       int pageNum=2;
       int pageSize=3;
       PageHelper.startPage(pageNum,pageSize);

       //执行查询语句
       List<Car> cars = mapper.selectAll();

       //封装分页信息对象new PageInfo()
       //PageInfo对象是PageHelper插件提供的,用来封装相关的信息的对象
       PageInfo<Car> carPageInfo = new PageInfo<>(cars, 3);
       
       System.out.println(carPageInfo);
       SqlSessionUtil.close(sqlSession);
   }

执行结果:
PageInfo{
pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3,
list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=
6, pages=3, reasonable=false, pageSizeZero=false}
[Car{id=86, carNum=‘1234’, brand=‘丰⽥霸道’, guidePrice=50.5, produceTime
=‘2020-10-11’, carType=‘燃油⻋’},
Car{id=87, carNum=‘1234’, brand=‘丰⽥霸道’, guidePrice=50.5, produceTime
=‘2020-10-11’, carType=‘燃油⻋’}],
prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPa
ge=true, hasNextPage=true,
navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNum
s=[1, 2, 3]
}

十五、MyBatis的注解式开发

mybatis中也提供了注解式开发⽅式,采⽤注解可以减少Sql映射⽂件的配置。 当然,使⽤注解式开发的话,sql语句是写在java程序中的,这种⽅式也会给sql语句的维护带来成本

官⽅是这么说的:

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

使⽤注解编写复杂的SQL是这样的:

在这里插入图片描述

原则:简单sql可以注解。复杂sql使⽤xml。

15.1 @Insert

    @Insert("insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})")
    int insert(Car car);

15.2 @Delete

    @Delete("delete from t_car where id=#{id};")
    int deleteById(int id);

15.3 @Update

    @Update("update t_car " +
            "set " +
            "car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} " +
            "where id=#{id};")
    int update(Car car);

15.4 @Select

    @Select("select * from t_car where id=#{id};")
    @Results({
    
            @Result(property = "id",column = "id"),
            @Result(property = "carNum",column = "car_num"),
            @Result(property = "brand",column = "brand"),
            @Result(property = "guidePrice",column = "guide_price"),
            @Result(property = "produceTime",column = "produce_time"),
            @Result(property = "carType",column = "car_type")
    })
    Car selectById(int id);

版权声明
本文为[Jm呀]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_66689823/article/details/126899576

Scroll to Top