mybatis笔记(老杜版本)

一、MyBatis概述

1.1框架

  • Java常⽤框架:

    ​ SSM三⼤框架:Spring + SpringMVC + MyBatis
    ​ SpringBoot
    ​ SpringCloud 等。。

  • SSM三⼤框架的学习顺序:MyBatis、Spring、SpringMVC(建议)

1.2 三层架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1uxyElgS-1670084450231)(D:\mybatis笔记\三层架构.png)]

  • 表现层(UI):直接跟前端打交互(⼀是接收前端ajax请求,⼆是返回json数据给前端)
  • 业务逻辑层(BLL):⼀是处理表现层转发过来的前端请求(也就是具体业务),⼆是将从持久层获取的数据返回到表现层。
  • 数据访问层(DAL):直接操作数据库完成CRUD,并将获得的数据返回到上⼀层(也就是业务逻辑层)。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IJbt1LlO-1670084450232)(D:\mybatis笔记\ORM对象关系映射.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ptwUXgH8-1670084450233)(D:\mybatis笔记\ORM对象关系映射2.png)]

  • MyBatis框架特点:

    • ⽀持定制化 SQL、存储过程、基本映射以及⾼级映射

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

    • ⽀持XML开发,也⽀持注解式开发。【为了保证sql语句的灵活,所以mybatis⼤部分是采⽤

    • XML⽅式开发。】

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

    • 记录

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

    • 完全做到sql解耦合。

    • 提供了基本映射标签。

    • 提供了⾼级映射标签。

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

二、MyBatis入门程序

2.1 版本

2.2 MyBatis下载

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】

  • 先创建一个空项目,再空项目中创建一个模块

  • 步骤1:打包⽅式:jar(不需要war,因为mybatis封装的是jdbc。)

    <groupId>com.zivgroupId>
    <artifactId>mybatis-002-crudartifactId>
    <version>1.0-SNAPSHOTversion>
    <packaging>jarpackaging>
    
  • 步骤2:引⼊依赖(mybatis依赖 + mysql驱动依赖)

    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.30version>
    dependency>
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.1version>
    dependency>
    
  • 步骤3:在resources根⽬录下新建mybatis-config.xml配置⽂件(可以参考mybatis⼿册拷⻉)

    
    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/ziv_mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                dataSource>
            environment>
        environments>
        <mappers>
            
            <mapper resource="CarMapper.xml"/>
        mappers>
    configuration>
    

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

  • 步骤4:在resources根⽬录下新建CarMapper.xml配置⽂件(可以参考mybatis⼿册拷⻉)

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="bbb">
        <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>
    

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

    <mapper resource="CarMapper.xml"/>
    
  • 步骤5:编写MyBatisIntroductionTest代码

    package com.ziv.mybatis.test;
    
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.InputStream;
    
    public class MyBatisIntroductionTest {
        public static void main(String[] args) {
            // 1. 创建SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            // 2. 创建SqlSessionFactory对象
            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(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.4 关于MyBatis核心配置文件的名字和路径详解

  • 经过测试说明mybatis核⼼配置⽂件的名字是随意的,存放路径也是随意的。

  • 虽然mybatis核⼼配置⽂件的名字不是固定的,但通常该⽂件的名字叫做:mybatis-config.xml

  • 虽然mybatis核⼼配置⽂件的路径不是固定的,但通常该⽂件会存放到类路径当中,这样让项⽬的移植更加健壮。

  • maven工程中的resources目录就相当于项目的根目录

  • 在mybatis中提供了⼀个类:Resources【org.apache.ibatis.io.Resources】,该类可以从类路径当
    中获取资源,我们通常使⽤它来获取输⼊流InputStream,代码如下

    // 这种⽅式只能从类路径当中获取资源,也就是说mybatis-config.xml⽂件需要在类路径下
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    

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

package com.ziv.mybatis;

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

public class MyBatisCompleteCodeTest {
    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 {
            // 6.关闭
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

2.6 引入JUnit

  • 第⼀步:引⼊依赖
<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.13.2version>
    <scope>testscope>
dependency>
  • 第⼆步:编写单元测试类【测试⽤例】,测试⽤例中每⼀个测试⽅法上使⽤@Test注解进行标注

    • 测试⽤例的名字:XxxTest
    • 测试⽅法声明格式:public void test业务⽅法名(){}
    @Test
    public void testDeleteById(){
    }
    @Test
    public void testInsertCar(){
    }
    
  • 第三步:可以在类上执⾏,也可以在⽅法上执⾏

    • 在类上执⾏时,该类中所有的测试⽅法都会执⾏。
    • 在⽅法上执⾏时,只执⾏当前的测试⽅法。

2.7 引入日志框架logback

  • 引⼊⽇志框架的⽬的是为了看清楚mybatis执⾏的具体sql。
  • 启⽤标准⽇志组件,只需要在mybatis-config.xml⽂件中添加以下配置:【可参考mybatis⼿册】
<settings>
    
    <setting name="logImpl" value="STDOUT_LOGGING" />
settings>
  • 我们这里使用logback日志框架,使用第三方框架,可以不用在settings中配置

    • 第⼀步:引⼊logback相关依赖

      <dependency>
          <groupId>ch.qos.logbackgroupId>
          <artifactId>logback-classicartifactId>
          <version>1.2.11version>
      dependency>
      
    • 第⼆步:引⼊logback相关配置⽂件(⽂件名叫做logback.xml或logback-test.xml,放到类路径当中)

      
      <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.PatternLayoutEncoder">
                  
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
              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}.logFileNamePattern>
                  
                  <MaxHistory>30MaxHistory>
              rollingPolicy>
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
              encoder>
              
              <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                  <MaxFileSize>100MBMaxFileSize>
              triggeringPolicy>
          appender>
          
          <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"/>
          
          <root level="DEBUG">
              <appender-ref ref="STDOUT"/>
              <appender-ref ref="FILE"/>
          root>
      configuration>
      
  • 执行程序,查看打印日志:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KDckUhmc-1670084450233)(D:\mybatis笔记\日志图片.png)]

2.8 MyBatis工具类SqlSessionUtil

package com.ziv.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;

import java.io.IOException;

public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private SqlSessionUtil(){}
    public static SqlSession openSession() {
        return sqlSessionFactory.openSession();
    }
}

三、使用MyBatis完成CRUD

什么是CRUD

  • C:Create 增
  • R:Retrieve 查(检索)
  • U:Update 改
  • D:Delete 删

pojo类Car

public class Car {
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;
    ...//省略getter setter等其他方法
}

3.1 insert

<insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{},#{},#{},#{},#{})
insert>

JDBC中使用 ? 当作占位符,mybatis中使用 #{} 当作占位符,? 和 #{} 是等效的。

  • mybatis可以使用map集合给sql语句中的占位符传值:#{ 这里写map集合中的key }

    • 注意:当#{}中的key写错的时候,会传入null,底层就是调用map.get()方法
  • mybatis可以使用pojo类给sql语句中的占位符传值:#{ 这里写pojo类中的属性名 }

    • <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>
      
    • 注意:当#{}中的属性名写错的话,会报错

      org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘carNum2’ in ‘class com.ziv.mybatis.pojo.Car’

      mybatis会去找:Car类中的getXxx()方法,也就是说,#{} 中写的是将Car类中getXxx()方法的get去掉得到Xxx首字母变小写得到xxx。例如:

      getCarNum() --> #{carNum}

      getBrand() --> #{brand}

​ java代码

@Test
public void testInsertCar(){
    SqlSession sqlSession = SqlSessionUtil.openSession();
    Car car = new Car(null,"3333","比亚迪秦",30.0,"2020-10-10","新能源");
    int count = sqlSession.insert("insertCar",car);
    System.out.println("count = " + count);
    sqlSession.commit();
    sqlSession.close();
}

3.2 delete

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

当只传一个值的时候,#{}里面的内容随意,但是不能空着。

java代码

@Test
public void testDeleteById(){
    SqlSession sqlSession = SqlSessionUtil.openSession();
    int count = sqlSession.delete("deleteById", 10);
    System.out.println("count = " + count);
    sqlSession.commit();
    sqlSession.close();
}

3.3 update

<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>
@Test
public void testUpdateById() {
    SqlSession sqlSession = SqlSessionUtil.openSession();
    Car car = new Car(9L,"1004","凯美瑞",30.0,"1999-10-10","燃油车");
    sqlSession.update("updateById",car);
    sqlSession.commit();
    sqlSession.close();
}

3.4 select

  • select(查一个,返回结果是一个)

    <select id="selectById" resultType="com.ziv.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>
    
    @Test
    public void testSelectById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Car car = sqlSession.selectOne("selectById", 12);
        System.out.println("car = " + car);
        sqlSession.commit();
        sqlSession.close();
    }
    

    mybatis执行了select语句之后,会返回一个ResultSet对象,然后从中取出数据,再封装成Java对象。所以需要通过select标签中的 resultType属性指明需要封装成什么对象。

    注意:select语句中查询的列别名要和pojo类中的属性名一致

  • select(查所有)

    <select id="selectAll" resultType="com.ziv.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>
    
    @Test
    public void testSelectAll(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        List<Object> carList = sqlSession.selectList("selectAll");
        System.out.println("carList = " + carList);
        sqlSession.commit();
        sqlSession.close();
    }
    

3.5 关于SQL Mapper 的namespace

  • 在SQL Mapper配置⽂件中标签的namespace属性可以翻译为命名空间,这个命名空间主要是为了防⽌sqlId冲突的。

  • 当有两个不同的Mapper.xml文件中的sql语句id相同的时候,就需要使用命名空间加id的形式。

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

四、MyBatis核心配置文件详解

4.1 environment


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/ziv_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            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/ziv_mybatis2"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
    mappers>
configuration>
@Test
public void configTest() throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    //build方法的第二个参数用来指定使用哪个环境配置,如果没有指定使用默认配置。
    SqlSession sqlSession = new SqlSessionFactoryBuilder().build(is).openSession();
    SqlSession sqlSession2 = new SqlSessionFactoryBuilder().build(is,"production").openSession();

}

4.2 transactionManager

<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/ziv_mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    dataSource>
environment>

4.3 dataSource

<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/ziv_mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    dataSource>
environment>

4.4 properties

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

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

在mybatis核心配置文件中引入并使用:


<properties resource="jdbc.properties"/>


<environment id="production">
    <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>
  • properties两个属性:
    • resource:这个属性从类的根路径下开始加载。【常⽤的。】
    • url:从指定的url加载,假设⽂件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。

4.5 mapper

  • mapper标签⽤来指定SQL映射⽂件的路径,包含多种指定⽅式,这⾥先主要看其中两种:

    • 第⼀种:resource,从类的根路径下开始加载【⽐url常⽤】

      <mappers>
          <mapper resource="CarMapper.xml"/>
      mappers>
      
    • 第⼆种:url,从指定的url位置加载

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

mapper还有其他的指定⽅式,后⾯再看!!!

汇总


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="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/ziv_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
        
        <environment id="production">
            <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>

五、手写MyBatis框架(掌握原理)

5.1 dom4j解析XML文件

5.2 GodBatis

5.3 GodBatis使用Maven打包

5.4 使用GodBatis

5.5 总结MyBatis框架的重要实现原理

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

6.1 需求描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eOhSumBB-1670084450234)(D:\mybatis笔记\bank需求分析.png)]

6.2 数据库表的设计和准备数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JOVqei1G-1670084450234)(D:\mybatis笔记\bank数据库表.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kSoneTTY-1670084450234)(D:\mybatis笔记\bank数据准备.png)]

6.3 实现步骤

第一步 创建模块,配置tomcat,引入依赖



<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.0modelVersion>

  <groupId>org.zivgroupId>
  <artifactId>mybatis-004-webartifactId>
  <version>1.0version>
  <packaging>warpackaging>

  <name>mybatis-004-web Maven Webappname>
  
  <url>http://www.example.comurl>

  <properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <maven.compiler.source>1.7maven.compiler.source>
    <maven.compiler.target>1.7maven.compiler.target>
  properties>

  <dependencies>
    <dependency>
      <groupId>jakarta.servletgroupId>
      <artifactId>jakarta.servlet-apiartifactId>
      <version>5.0.0version>
    dependency>
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-classicartifactId>
      <version>1.2.11version>
    dependency>
    <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <version>8.0.30version>
    dependency>
    <dependency>
      <groupId>org.mybatisgroupId>
      <artifactId>mybatisartifactId>
      <version>3.5.1version>
    dependency>
  dependencies>

  <build>
    <finalName>mybatis-004-webfinalName>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-clean-pluginartifactId>
          <version>3.1.0version>
        plugin>
        
        <plugin>
          <artifactId>maven-resources-pluginartifactId>
          <version>3.0.2version>
        plugin>
        <plugin>
          <artifactId>maven-compiler-pluginartifactId>
          <version>3.8.0version>
        plugin>
        <plugin>
          <artifactId>maven-surefire-pluginartifactId>
          <version>2.22.1version>
        plugin>
        <plugin>
          <artifactId>maven-war-pluginartifactId>
          <version>3.2.2version>
        plugin>
        <plugin>
          <artifactId>maven-install-pluginartifactId>
          <version>2.5.2version>
        plugin>
        <plugin>
          <artifactId>maven-deploy-pluginartifactId>
          <version>2.8.2version>
        plugin>
      plugins>
    pluginManagement>
  build>
project>

引⼊相关配置⽂件,放到resources⽬录下(全部放到类的根路径下)

  • mybatis-config.xml

    
    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="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                dataSource>
            environment>
        environments>
    <mappers>
        <mapper resource="AccountMapper.xml"/>
    mappers>
    configuration>
    
  • AccountMapper.xml

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="bank">
        <select id="selectByActno" resultType="com.ziv.bank.pojo.Account">
            select id,actno,balance from t_act where #{actno}
        select>
        <update id="updateByActno">
            update t_act set(balance=#{balance}) where actno = #{actno}
        update>
    mapper>
    
  • logback.xml

    
    
    <configuration debug="false">
        
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="UTF-8">
                
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
            encoder>
        appender>
        
        <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"/>
        
        <root level="DEBUG">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="FILE"/>
        root>
    configuration>
    
  • jdbc.properties

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

第二步 前端页面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="fromAct"><br>
    转入账户:<input type="text" name="toAct"><br>
    转账金额:<input type="text" name="money"><br>
    <input type="submit" value="转账">
form>
body>
html>

注意:web.xml中的web-app标签属性metadata-complete=“false”,其值为false表示启用注解编程,其值为true表示不开启注解。

第三步 创建包

com.ziv.bank.pojo
com.ziv.bank.service
com.ziv.bank.service.impl
com.ziv.bank.dao
com.ziv.bank.dao.impl
com.ziv.bank.web.controller
com.ziv.bank.exception
com.ziv.bank.utils

第四步 定义pojo类:Account 和工具类

package com.ziv.bank.pojo;
/**
 * 账户类,封装数据
 * @author ziv
 * @version 1.0
 * @since 1.0
 */
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() {
    }
}
package com.ziv.bank.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;

public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	//ThreadLocal解决事务问题
    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

    private SqlSessionUtil(){}

    /**
     * 获取会话对象
     * @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();
        }
    }
}

第五步 编写AccountDao接口,已经AccountDaoImpl实现类

package com.ziv.bank.dao;
import com.ziv.bank.pojo.Account;
/**
 * 账户的dao对象,负责t_act的CRUD
 *
 * @author ziv
 * @version 1.0
 * @since 1.0
 */
public interface AccountDao {
    /**
     * 通过账号查询账户信息
     * @param actno 账号
     * @return 账户信息
     */
    Account selectByActno(String actno);
    /**
     * 更新账户信息
     * @param act 需要更新的账户
     * @return 1 表示更新成功,其他表示更新失败
     */
    int updateByActno(Account act);
}
package com.ziv.bank.dao.impl;

import com.ziv.bank.dao.AccountDao;
import com.ziv.bank.pojo.Account;
import com.ziv.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

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

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

第六步 编写SQL映射文件 AccountMapper.xml


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="bank">
    <select id="selectByActno" resultType="com.ziv.bank.pojo.Account">
        select id,actno,balance from t_act where #{actno}
    select>
    <update id="updateByActno">
        update t_act set(balance=#{balance}) where actno = #{actno}
    update>
mapper>

第七步:编写AccountService接⼝和AccountServiceImpl,以及异常类

public class MoneyNotEnoughException extends Exception {
    public MoneyNotEnoughException(){}
    public MoneyNotEnoughException(String msg){super(msg);}
}
public class TransferException extends Exception {
    public TransferException(){}
    public TransferException(String msg){super(msg);}
}
package com.ziv.bank.service;

import com.ziv.bank.exceptions.MoneyNotEnoughException;
import com.ziv.bank.exceptions.TransferException;

/**
 * 处理账户业务的接口
 *
 * @author ziv
 * @version 1.0
 * @since 1.0
 */
public interface AccountService {
    /**
     * 账户转账业务
     *
     * @param fromAct 转出账户
     * @param toAct   转入账户
     * @param money   转账金额
     */
    void transfer(String fromAct, String toAct, Double money) throws MoneyNotEnoughException, TransferException;
}
package com.ziv.bank.service.impl;

import com.ziv.bank.dao.AccountDao;
import com.ziv.bank.dao.impl.AccountDaoImpl;
import com.ziv.bank.exceptions.MoneyNotEnoughException;
import com.ziv.bank.exceptions.TransferException;
import com.ziv.bank.pojo.Account;
import com.ziv.bank.service.AccountService;
import com.ziv.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountServiceImpl implements AccountService {

    AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromAct, String toAct, Double money) throws MoneyNotEnoughException, TransferException {
        //添加事务代码
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //判断转出账户余额是否充足
        Account fromAccount = accountDao.selectByActno(fromAct);
        Account toAccount = accountDao.selectByActno(toAct);
        //余额不足,抛出异常
        if (fromAccount.getBalance() < money) {
            throw new MoneyNotEnoughException("余额不足");
        }
        //余额充足,继续转账
        fromAccount.setBalance(fromAccount.getBalance() - money);
        toAccount.setBalance(toAccount.getBalance() + money);
        int count = accountDao.updateByActno(fromAccount);
        //int i = 12/0;
        count += accountDao.updateByActno(toAccount);
        if (count != 2) {
            throw new TransferException("转账失败");
        }
        //提交事务
        sqlSession.commit();
        //关闭事务
        sqlSession.close();
    }
}

第⼋步:编写AccountController

package com.ziv.bank.web;

import com.ziv.bank.exceptions.MoneyNotEnoughException;
import com.ziv.bank.exceptions.TransferException;
import com.ziv.bank.service.AccountService;
import com.ziv.bank.service.impl.AccountServiceImpl;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

//@WebServlet("/transfer")
public class AccountController extends HttpServlet {

    AccountService accountService = new AccountServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String fromAct = request.getParameter("fromAct");
        String toAct = request.getParameter("toAct");
        double money = Double.parseDouble(request.getParameter("money"));
        try {
            accountService.transfer(fromAct, toAct, money);
            response.sendRedirect(request.getContextPath()+"/success.html");
        } catch (MoneyNotEnoughException e) {
            e.printStackTrace();
            response.sendRedirect(request.getContextPath()+"/error1.html");
        } catch (TransferException e) {
            response.sendRedirect(request.getContextPath()+"/error2.html");
            e.printStackTrace();
        }
    }
}

6.4 MyBatis对象作用域以及事务问题

SqlSessionFactoryBuilder

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

SqlSessionFactory

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

SqlSession

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

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

使用Threadlocal解决事务问题

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

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

七、使用javassist生成类

7.1 javassist的使用

7.2 使用javassist生成DaoImpl类

package com.ziv.bank.utils;

import org.apache.ibatis.javassist.CannotCompileException;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 工具类:可以动态的生成DAO的实现类。(或者说可以动态生成DAO的代理类)
 */
public class GenerateDaoProxy {

    public static Object generate(SqlSession sqlSession, Class daoInterface) {
        //类池
        ClassPool pool = ClassPool.getDefault();
        //制造类(com.ziv.bank.dao.AccountDao --> com.ziv.bank.dao.AccountDaoProxy)
        CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
        //制造借口
        CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        //实现接口
        ctClass.addInterface(ctInterface);
        //实现接口中的所有方法
        Method[] methods = daoInterface.getDeclaredMethods();
        Arrays.stream(methods).forEach(method -> {
            //method是接口中的抽象方法
            //实现method这个抽象方法
            // Account selectByActno(String actno);
            // public Account selectByActno(String actno){代码;}
            StringBuilder methodCode = new StringBuilder();
            methodCode.append("public ");
            methodCode.append(method.getReturnType().getName());
            methodCode.append(" ");
            methodCode.append(method.getName());
            methodCode.append("(");
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                methodCode.append(parameterType.getName());
                methodCode.append(" ");
                methodCode.append("arg" + i);
                if (i != parameterTypes.length - 1) {
                    methodCode.append(",");
                }
            }
            methodCode.append(")");
            methodCode.append("{");
            //
            methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.ziv.bank.utils.SqlSessionUtil.openSession();");
            String sqlId = daoInterface.getName() + "." + method.getName();
            SqlCommandType commandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
            if (commandType == SqlCommandType.DELETE) {

            }
            if (commandType == SqlCommandType.INSERT) {

            }
            if (commandType == SqlCommandType.UPDATE) {
                methodCode.append("return sqlSession.update(\"" + sqlId + "\", arg0);");
            }
            if (commandType == SqlCommandType.SELECT) {
                methodCode.append("return (" + method.getReturnType().getName() + ")sqlSession.selectOne(\"" + sqlId + "\", arg0);");
            }
            methodCode.append("}");
            try {
                CtMethod ctMethod = CtMethod.make(String.valueOf(methodCode), ctClass);
                ctClass.addMethod(ctMethod);
            } catch (CannotCompileException e) {
                e.printStackTrace();
            }
        });
        //创建对象
        Object obj = null;
        try {
            Class<?> clazz = ctClass.toClass();
            obj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}

八、MyBatis中接口代理机制及使用

private AccountDao accountDao = SqlSessionUtil.openSession().getMapper(AccountDao.class);

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

九、MyBatis小技巧

9.1 #{ }和${ }

  • #{}:先编译sql语句,再给占位符传值,底层是PreparedStatement实现。可以防⽌sql注⼊,⽐较常⽤。
  • ${}:先进⾏sql语句拼接,然后再编译sql语句,底层是Statement实现。存在sql注⼊现象。只有在需要进⾏sql语句关键字拼接的情况下才会⽤到。

使用${}的时机:

  • 当需要进⾏sql语句关键字拼接的时候。必须使⽤${}

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

  • 拼接表名

    例如:在前端传参数决定查询哪个表,t_log20221201,t_log20221202等等。

  • 模糊查询

    有三种解决方法

    name like '%${name}%'
    name like concat('%',#{name},'%')
    name like "%"#{name}"%"
    

9.2 typeAliases

在mybatis-config.xml中指定:


<typeAliases>
    
    <typeAlias type="com.ziv.mybatis.pojo.Car" alias="car"/>
    <typeAlias type="com.ziv.mybatis.pojo.Car"/>
    
    <package name="com.ziv.mybatis.pojo"/>
typeAliases>

在CarMapper.xml标签resultType属性中使用

<select id="selectAll" resultType="car">
    select id,
    car_num      as carNum,
    brand,
    guide_price  as guidePrice,
    produce_time as produceTime,
    car_type     as carType
    from t_car
select>

9.3 mappers


<mapper resource="" class="" url=""/>

<package name="com.ziv.mybatis.mapper"/>

9.4 idea配置文件模板

File->Settings->Editor->File and Code Templates


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource=""/>
    <typeAliases>
        <package name=""/>
    typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            dataSource>
        environment>
    environments>
    <mappers>
        <package name=""/>
    mappers>
configuration>

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">

mapper>

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


<insert id="insertCarUseGeneratedKey" 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>
@Test
public void testInsertCarUseGeneratedKey() {
    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    Car car = new Car(null, "9000", "Kaimeirui", 35.0, "2000-10-10", "燃油车");
    mapper.insertCarUseGeneratedKey(car);
    sqlSession.commit();
    sqlSession.close();
    System.out.println(car);
}

输出car之后发现id被赋上了自动生成的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQOnRj0C-1670084450235)(D:\mybatis笔记\输出结果.png)]

十、MyBatis参数处理

准备数据库表和数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAcndPdZ-1670084450235)(D:\mybatis笔记\学生表.png)]

10.1 单个简单类型参数

简单类型包括:

  • byte short int long float double char
  • Byte Short Integer Long Float Double Character
  • String
  • java.util.Date
  • java.sql.Date

mybatis可以自动识别简单类型,底层可以自动推断使用哪一个preparedStatement.setXxx()方法。比如传String的值,就会调用preparedStatement.setString()方法。

SQL映射文件中的标签比较完整的写法是:

<select id="selectStudentByName" resultType="student" parameterType="string">
	select * from t_student where name = #{name, javaType=String, jdbcType=VARCHAR}
select>

resultType和parameterType里面本来应该填写全限定类名,但是mybatis默认提供了别名(详细查看mybatis文档),所以这里直接填写类简名。另外,sql语句中的javaType,jdbcType,以及select标签中的parameterType属性,都是⽤来帮助mybatis进⾏类型确定的。不过这些配置多数是可以省略的。因为mybatis它有强⼤的⾃动类型推断机制。

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

10.2 map参数

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

10.3 实体类参数

这⾥需要注意的是:#{} ⾥⾯写的是属性名字。这个属性名其本质上是:set/get⽅法名去掉set/get之后的名字。

10.4 多参数

对于多个参数,mybatis底层会创建⼀个map集合,以arg0/param1为key,传入的参数为 value
例如:

List<Student> students = mapper.selectByNameAndSex("张三", '⼥')
//底层封装成了这样的map集合,其中arg从0开始,param从1开始
Map<String,Object> map = new HashMap<>();
map.put("arg0", name);
map.put("arg1", sex);
map.put("param1", name);
map.put("param2", sex);

<select id="selectByNameAndSex">
	select * from t_student where name = #{arg0} and sex = #{arg1}
select>

10.5 @Param注解(命名参数)

mybatis提供的key可读性太差,可以使用@Param注解自己指定key的名字

List<Student> selectByNameAndAge(@Param(value="name") String name, @Param("age") int age);
<select id="selectByNameAndAge">
	select * from t_student where name = #{name} and sex = #{age}
select>

注意:@Param替换的是map集合中arg0,arg1… 另外的param1,param2…还是可以继续使用的。

10.6 @Param源码分析

十一、MyBatis查询语句专题

11.1 返回Car

当查询的结果,有对应的实体类,并且查询结果只有⼀条时,可以直接用实体类接收,也可以用List集合接收。

11.2 返回List

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

11.3 返回Map

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RXI7LR0s-1670084450236)(D:\mybatis笔记\返回map.png)]

11.4 返回List

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

11.5 返回Map

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

/**
  * 获取所有的Car,返回⼀个Map集合。 
  * Map集合的key是Car的id。 
  * Map集合的value是对应Car。 
  * 需要在接口上添加@MapKey注解,指定id作为map的key
  * @return
  */
@MapKey("id")
Map<Long,Map<String,Object>> selectAllRetMap();
    <select id="selectAllRetMap" resultType="map">
        select id,
               car_num      carNum,
               brand,
               guide_price  guidePrice,
               produce_time produceTime,
               car_type     carType
        from t_car
    select>

11.6 resultMap结果映射

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

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

使⽤resultMap进⾏结果映射


<resultMap id="carResultMap" type="car">
    
    <id property="id" column="id"/>
    <result property="carNum" column="car_num"/>
    
    
    <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>


<select id="selectAllByResultMap" resultMap="carResultMap">
    select * from t_car
select>

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

在mybatis-config.xml⽂件中进⾏配置:

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

11.7 返回总记录条数

<select id="selectAll" resultType="long">
    select count(*) from t_car
select>
@Test
public void testSelectTotal() {
    CarMapper carMapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
    Long total = carMapper.selectTotal();
    System.out.println(total);
}

十二、动态SQL

12.1 if标签



<select id="selectByMultiCondition" resultType="car">
    select * from t_car where 1=1
    <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>
select>

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

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

12.4 set标签

主要使⽤在update语句当中,⽤来⽣成set关键字,同时去掉最后多余的“,”
⽐如我们只更新提交的不为空的字段,如果提交的数据是null或者"",那么这个字段我们将不更新。

<update id="updateBySet">
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>

12.5 choose when otherwise

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

就等同于

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

有且仅有⼀个分⽀会被选择!!!!

<select id="selectByChoose" 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>
            <when test="carType != null and carType != ''">
                car_type = #{carType}
            when>
        choose>
    where>
select>

12.6 foreach标签

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

delete from t_car where id in(1,2,3);
delete from t_car where id = 1 or id = 2 or id = 3;

insert into t_car values
    (null,'1001','凯美瑞',35.0,'2010-10-11','燃油⻋'),
    (null,'1002','⽐亚迪唐',31.0,'2020-11-11','新能源'),
    (null,'1003','⽐亚迪宋',32.0,'2020-10-11','新能源')

批量删除

定义接口

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

sql语句


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

批量插入

定义接口

int insertBatch(@Param("cars") List<Car> cars);

sql语句

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

12.7 sql标签与include标签

sql标签⽤来声明sql⽚段
include标签⽤来将声明的sql⽚段包含到某个sql语句当中
作⽤:代码复⽤。易维护。

<sql id="carColumnName">
    id,
    car_num      as carNum,
    brand,
    guide_price  as guidePrice,
    produce_time as produceTime,
    car_type     as carType
sql>

<select id="selectCarById" resultType="com.ziv.mybatis.pojo.Car">
    select 
    <include refid="carColumnName"/>
    from t_car
    where id = #{id}
select>

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

准备数据库表和数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9PV72KF6-1670084450236)(D:\mybatis笔记\高级映射数据表1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lqE6FX55-1670084450236)(D:\mybatis笔记\高级映射数据表2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kkpEPLt6-1670084450237)(D:\mybatis笔记\高级映射数据表数据.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jakb7yVy-1670084450246)(D:\mybatis笔记\高级映射数据表数据2.png)]

依赖:mybatis依赖、mysql驱动依赖、junit依赖、logback依赖

创建相关的类和文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6WRDMO9-1670084450246)(D:\mybatis笔记\高级映射类和文件.png)]

13.1 多对一

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

  • 第⼀种⽅式:⼀条SQL语句,级联属性映射。
  • 第⼆种⽅式:⼀条SQL语句,association。
  • 第三种⽅式:两条SQL语句,分步查询。(这种⽅式常⽤:优点⼀是可复⽤。优点⼆是⽀持懒加载。)

第⼀种⽅式:级联属性映射

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Dnl6xhe-1670084450247)(D:\mybatis笔记\级联映射.png)]

pojo类Student中添加⼀个属性:Clazz clazz; 表示学⽣关联的班级对象。

编写sql语句:


<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="selectById" resultMap="studentResultMap">
    select s.sid, s.sname, c.cid, c.cname
    from t_stu s left join t_clazz c on s.cid = c.cid
    where s.sid = #{sid}
select>

测试程序:

@Test
public void testSelectById(){
    SqlSession sqlSession = SqlSessionUtil.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student student = mapper.selectById(1);
    System.out.println(student);
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fgSNZVjM-1670084450247)(D:\mybatis笔记\image-20221203173344950.png)]

第⼆种⽅式:association

其他位置都不需要修改,只需要修改resultMap中的配置:association即可。

<resultMap id="studentResultMapAssociation" 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>


<select id="selectByIdAssociation" resultMap="studentResultMapAssociation">
    select s.sid, s.sname, c.cid, c.cname
    from t_stu s left join t_clazz c on s.cid = c.cid
    where s.sid = #{sid}
select>

测试结果和第一种方法一样

第三种⽅式:分步查询

其他位置不需要修改,只需要修改以及添加以下三处:
第⼀处:association中select位置填写sqlId。sqlId=namespace+id。其中column属性作为这条⼦sql语句的条件。



<resultMap id="selectResultMapByStep" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    
    
    
    <association property="clazz"
                 select="com.ziv.mybatis.mapper.ClazzMapper.selectByIdStepTwo"
                 column="cid"/>
resultMap>


<select id="selectByIdStepOne" resultMap="selectResultMapByStep">
    select sid,sname,cid from t_stu where sid = #{sid}
select>

第⼆处:在ClazzMapper接⼝中添加⽅法

package com.ziv.mybatis.mapper;

import com.ziv.mybatis.pojo.Clazz;

/**
 * @author ziv
 */
public interface ClazzMapper {

    /**
     * 根据id查询班级的信息
     * @param cid
     * @return
     */
    Clazz selectByIdStepTwo(Integer cid);
}

第三处:在ClazzMapper.xml⽂件中进⾏配置


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ziv.mybatis.mapper.ClazzMapper">

    
    
    <select id="selectByIdStepTwo" resultType="Clazz">
        select cid,cname from t_clazz where cid = #{cid}
    select>
mapper>

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NtiSpKFU-1670084450248)(C:\Users\95155\AppData\Roaming\Typora\typora-user-images\image-20221203191839504.png)]

分步优点:

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

13.2 多对一延迟加载

要想⽀持延迟加载,⾮常简单,只需要在association标签中添加fetchType="lazy"即可。
修改StudentMapper.xml⽂件:

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

测试结果,只执行了一条sql语句

@Test
public void testSelectByIdStepOne(){
    SqlSession sqlSession = SqlSessionUtil.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student student = mapper.selectByIdStepOne(1);
    //System.out.println(student);
    //只需要看学生的名字
    System.out.println(student.getSname());
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CHAMPOKe-1670084450248)(C:\Users\95155\AppData\Roaming\Typora\typora-user-images\image-20221203192651945.png)]

在association标签中设置fetchType=“lazy”,只在当前association关联的sql语句中生效,如果需要添加全局设置,则需要在mybatis-config.xml中配置一下:

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

如果开启了全局懒加载配置,但是在某个association中不希望使用懒加载,则在association中设置属性:fetchType=“eager”

13.3 一对多

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aStfFQ0t-1670084450248)(D:\mybatis笔记\高级映射一对多.png)]

⼀对多的实现,通常是在⼀的⼀⽅中有List集合属性。
在Clazz类中添加List stus; 属性。

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

  • 第⼀种⽅式: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="selectByCollection" resultMap="clazzResultMap">
    select c.cid, c.cname, s.sid, s.sname
    from t_clazz c left join t_stu s on c.cid = s.cid
    where c.cid = #{cid}
select>

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-smMWydO6-1670084450249)(C:\Users\95155\AppData\Roaming\Typora\typora-user-images\image-20221203195816354.png)]

第⼆种⽅式:分步查询

与多对一的分步查询类似

<resultMap id="clazzResultMapByStep" type="Clazz">
    <id property="cid" column="cid"/>
    <result property="cname" column="cname"/>
    
    
    
    <collection property="stus"
                select="com.ziv.mybatis.mapper.StudentMapper.selectByCidStepTwo" 
                column="cid"/>
resultMap>


<select id="selectByStepOne" resultMap="clazzResultMapByStep">
    select cid,cname from t_clazz where cid = #{cid}
select>

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ps2Lu0Jx-1670084450249)(C:\Users\95155\AppData\Roaming\Typora\typora-user-images\image-20221203201346330.png)]

13.4 一对多延迟加载

与多对一相同

十四、MyBatis的缓存

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7fj7Nvc-1670084450249)(D:\mybatis笔记\mybatis缓存.png)]

14.1 一级缓存

⼀级缓存默认是开启的。不需要做任何配置。
原理:只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存。

测试

@Test
public void testSelectByCollection(){
    SqlSession sqlSession = SqlSessionUtil.openSession();
    ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
    Clazz clazz1 = mapper.selectByCollection(1001);
    System.out.println(clazz1);
    Clazz clazz2 = mapper.selectByCollection(1001);
    System.out.println(clazz2);
    sqlSession.close();
}

执行结果,只执行了一次sql语句,第二次是直接从缓存中拿到数据输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAHDYe2P-1670084450250)(C:\Users\95155\AppData\Roaming\Typora\typora-user-images\image-20221203223726884.png)]

什么情况下不会⾛缓存?

  • 第⼀种:不同的SqlSession对象。
  • 第⼆种:查询条件变化了。

⼀级缓存失效情况包括两种:

  • 第⼀种:第⼀次查询和第⼆次查询之间,⼿动清空了⼀级缓存。

    sqlSession.clearCache();
    
  • 第⼆种:第⼀次查询和第⼆次查询之间,执⾏了增删改操作。【这个增删改和哪张表没有关系,只要有insert delete update操作,⼀级缓存就失效。】

14.2 二级缓存

⼆级缓存的范围是SqlSessionFactory。
使⽤⼆级缓存需要具备以下⼏个条件:

  1. 全局性地开启或关闭所有映射器配置⽂件中已配置的任何缓存。默认就是true,⽆需设置。
<setting name="cacheEnabled" value="true">
  1. 在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加配置:

    
    <cache/>
    
  2. 使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝

  3. SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中。此时⼆级缓存才可⽤。

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

⼆级缓存的相关配置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8A1ddhOY-1670084450250)(D:\mybatis笔记\cache标签的属性.png)]

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

14.3 MyBatis集成EhCache

集成EhCache是为了代替mybatis⾃带的⼆级缓存。⼀级缓存是⽆法替代的。
mybatis对外提供了接⼝,也可以集成第三⽅的缓存组件。⽐如EhCache、Memcache等。都可以。
EhCache是Java写的。Memcache是C语⾔写的。所以mybatis集成EhCache较为常⻅,按照以下步骤操作,就可以完成集成:

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

    
    <dependency>
        <groupId>org.mybatis.cachesgroupId>
        <artifactId>mybatis-ehcacheartifactId>
        <version>1.2.2version>
    dependency>
    
    <dependency>
        <groupId>ch.qos.logbackgroupId>
        <artifactId>logback-classicartifactId>
        <version>1.2.11version>
        <scope>testscope>
    dependency>
    
  • 第⼆步:在类的根路径下新建echcache.xml⽂件,并提供以下配置信息。

  • 
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
             updateCheck="false">
        
        <diskStore path="e:/ehcache"/>
    
        
        
        
        
        
        
        
        
        
        
        
        <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                      timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
    
    ehcache>
    
  • 第三步:修改SqlMapper.xml⽂件中的标签,添加type属性。

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
  • 第四步:编写测试程序使⽤。

十五、MyBatis的逆向工程

所谓的逆向⼯程是:根据数据库表逆向⽣成Java的pojo类,SqlMapper.xml⽂件,以及Mapper接⼝类等。
要完成这个⼯作,需要借助别⼈写好的逆向⼯程插件。

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

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

15.1 逆向工程配置与生产

第一步:pom中添加逆向工程依赖



<build>
    
    <plugins>
        
        <plugin>
            
            <groupId>org.mybatis.generatorgroupId>
            <artifactId>mybatis-generator-maven-pluginartifactId>
            <version>1.4.1version>
            
            <configuration>
                <overwrite>trueoverwrite>
            configuration>
            
            <dependencies>
                
                <dependency>
                    <groupId>mysqlgroupId>
                    <artifactId>mysql-connector-javaartifactId>
                    <version>8.0.30version>
                dependency>
            dependencies>
        plugin>
    plugins>
build>

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


DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    
    <context id="DB2Tables" targetRuntime="MyBatis3">
        
        <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/ziv_mybatis"
                        userId="root"
                        password="root">
        jdbcConnection>
        
        <javaModelGenerator targetPackage="com.ziv.mybatis.pojo" targetProject="src/main/java">
            
            <property name="enableSubPackages" value="true"/>
            
            <property name="trimStrings" value="true"/>
        javaModelGenerator>
        
        <sqlMapGenerator targetPackage="com.ziv.mybatis.mapper" targetProject="src/main/resources">
            
            <property name="enableSubPackages" value="true"/>
        sqlMapGenerator>
        
        <javaClientGenerator
                type="xmlMapper"
                targetPackage="com.ziv.mybatis.mapper"
                targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        javaClientGenerator>
        
        <table tableName="t_car" domainObjectName="Car"/>
    context>
generatorConfiguration>

第四步:运行插件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AnyhzyBk-1670084450250)(D:\mybatis笔记\image-20221203233414173.png)]

15.2 测试逆向工程生产的是否好用

@Test
public void test() throws Exception {
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder
        ().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    // 查⼀个
    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

16.1 limit分页

mysql的limit后⾯两个数字:

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

假设已知⻚码pageNum,还有每⻚显示的记录条数pageSize,第⼀个数字可以动态的获取吗?
startIndex = (pageNum - 1) * pageSize
所以,标准通⽤的mysql分⻚SQL:

select * from tableName ...
limit (pageNum - 1) * pageSize, pageSize

16.2 使⽤mybatis分页

CarMapper接口

List<Car> selectAllByPage(@Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize);

CarMapper.xml

<select id="selectAllByPage" resultType="Car">
    select * from t_car limit #{startIndex},#{pageSize}
select>

16.3 PageHelper插件

第一步:引入依赖

<dependency>
    <groupId>com.github.pagehelpergroupId>
    <artifactId>pagehelperartifactId>
    <version>5.3.2version>
dependency>

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

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

第三步:编写Java代码

@Test
public void testPageHelper() throws Exception {
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);

    // 开启分⻚
    PageHelper.startPage(2, 2);

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

    // 获取分⻚信息对象
    PageInfo<Car> pageInfo = new PageInfo<>(cars, 5);

    System.out.println(pageInfo);
    
    /*
    PageInfo{pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=15, pages=8,
    list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=15, pages=8, reasonable=false, pageSizeZero=false}
    [Car{id=4, carNum='1003', brand='丰田霸道', guidePrice=32.00, produceTime='2001-10-11', carType='燃油车'},
    Car{id=5, carNum='1003', brand='丰田霸道', guidePrice=33.00, produceTime='2002-10-11', carType='燃油车'}],
    prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true,
    navigatePages=5, navigateFirstPage=1, navigateLastPage=5, navigatepageNums=[1, 2, 3, 4, 5]}
    */
}

十七、MyBatis的注解式开发

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

17.1 @Insert

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

17.2 @Delete

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

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

17.4 @Select

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

你可能感兴趣的:(学习笔记,mybatis,java,开发语言)