小黑子—MyBatis:第一章

一 小黑子对mybatis进行概述

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

  • Java常用框架:

    • SSM三大框架: Spring + SpringMVC + MyBatis
    • SpringBoot
    • SpringCloud等。。。
  • 框架其实就是对通用代码的苗装,提前写好了一堆接口和类,我们可以在做项目的时候直接引入这些接口和类(引入框架),基于这些现有的接口和类进行开发,可以大大提高开发效率.

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

  • SSM三大框架的学习顺序:MyBatis、Spring、SpringMVC

MyBatis是一个持久层框架,负责项目开发中的数据访问层(DAO层)

具体来说,MyBatis提供以下功能和特点,用于实现数据访问层的开发:

  1. SQL映射:MyBatis通过XML配置文件或注解的方式,将SQL语句与Java方法进行映射,实现了对象关系映射(ORM),将数据库操作封装成Java方法调用。

  2. 参数绑定:MyBatis支持将Java对象、基本类型和Map等作为参数传递给SQL语句,实现了方便灵活的参数绑定。

  3. 结果映射:MyBatis可以将查询结果映射为Java对象或基本类型,并支持关联查询和复杂对象图的映射。

  4. 缓存机制:MyBatis提供了一级缓存和二级缓存的支持,能够减少对数据库的频繁访问,提高系统性能。

  5. 插件扩展:MyBatis提供了插件机制,允许开发者编写自定义插件,拦截并修改MyBatis的核心行为,实现功能的扩展和定制。

总的来说,MyBatis作为一个优秀的持久层框架,主要负责数据访问层的开发,通过SQL映射、参数绑定和结果映射等功能,简化了与数据库的交互,提高了开发效率和代码的可维护性。

1.1 mybatis三层架构

在这里插入图片描述

  • 表现层(UI)︰直接跟前端打交互(一是接收前端ajax请求,二是返回json数据给前端)
  • 业务逻辑层(BLL)︰一是处理表现层转发过来的前端请求(也就是具体业务),二是将从持久层获取的数据返回到表现层。
  • 数据访问层(DAL)︰直接操作数据库完成CRUD,并将获得的数据返回到上一层(也就是业务逻辑层)。
  • Java持久层框架
    • Mybatis
    • Hibernate(实现了JPA规范)
    • jOOQ
    • Guzz
    • Spring Data(实现了JPA规范)
    • ActiveJDBC…

1.1 了解MyBatis

  • MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
  • MyBatis在三层架构中负责持久层的,属于持久层框架。
  • MyBatis的发展历程:
    • MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。.2013年11月迁移到Gitub,
    • iBATIS一词来源于“internet”和"abatis”的组合,是一个基于Java的持久层框架。iBATS提供的持久层框架包括SQL Maps和Data Access Obijects(DAOs),
  • 打开mybatis代码可以看到它的包结构中包含: ibatis

MyBatis框架、Spring框架和Spring MVC框架在项目开发中有一些区别,下面是它们的主要区别:

  1. 定位和职责:
    • MyBatis框架:主要用于数据访问层的开发,提供了强大的SQL映射功能,可以将数据库操作封装成Java方法调用。
    • Spring框架:是一个综合性的企业级开发框架,提供了多种功能和特性,包括IoC容器、AOP、事务管理、Web开发等。
    • Spring MVC框架:是Spring框架的一部分,用于开发Web应用程序的MVC模式,负责接收用户请求、处理逻辑和渲染视图。
  2. 使用场景:
    • MyBatis框架:适用于需要编写复杂SQL语句或对数据库操作有较高灵活性要求的项目,如需要进行复杂数据查询、批量操作等。
    • Spring框架:适用于各种企业级应用项目,提供了综合的解决方案,包括依赖注入、面向切面编程、事务管理等。
    • Spring MVC框架:适用于开发基于Web的应用程序,将请求分发给不同的控制器处理,并将处理结果渲染为视图返回给用户。
  3. 功能和特性:
    • MyBatis框架:提供了强大的SQL映射、参数绑定和结果映射功能,可以直接编写和调优SQL语句,对数据库操作的细粒度控制较高。
    • Spring框架:提供了IoC容器、AOP、事务管理、面向切面编程等功能,支持各种应用层面的开发需求,并简化了开发流程。
    • Spring MVC框架:提供了基于MVC的Web开发模式,包括请求处理、URL映射、视图渲染等功能,支持Restful风格的开发。
  4. 整合方式:
    • MyBatis与Spring的整合:通过Spring提供的MyBatis-Spring模块将二者整合在一起,实现配置的统一和依赖注入的管理。
    • Spring与Spring MVC的整合:Spring MVC是Spring框架的一部分,可以直接在Spring框架中使用Spring MVC的功能,无需额外的整合。
    • MyBatis与Spring MVC的整合:可以将MyBatis作为Spring MVC的持久化框架,结合@RequestMapping注解进行数据库操作。

总的来说,MyBatis框架主要关注数据访问层开发,Spring框架提供了更多的企业级特性和全面的解决方案,而Spring
MVC框架则专注于Web应用程序的开发。在实际项目中,可以根据需求选择合适的框架及其整合方式,以实现高效、灵活和可维护的应用程序开发

1.3 ORM思想

  • ORM:对象关系映射
    • O(Object):Java虚拟机中的Java对象。
    • R(Relational):关系型数据库
    • M(Mapping):将Java虚拟机中的Java对象映射到数据库表中一行记录,或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象。

ORM图示:
在这里插入图片描述
MyBatis框架特点:

  • 支持定制化 SQL、存储过程、基本映射以及高级映射
  • 避免了几乎所有的 JDBC 代码中手动设置参数以及获取结果集
  • 支持XML开发,也支持注解式开发。【为了保证sql语句的灵活,所以mybatis大部分是采用XML方式开发。】
  • 将接口和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的记录
  • 体积小好学:两个jar包,两个XML配置文件。
  • 完全做到sql解耦合。
  • 提供了基本映射标签。
  • 提供了高级映射标签。
  • 提供了XML标签,支持动态SQL的编写。

二 小黑子的MyBatis入门程序

会JDBC,MyBatis就可以学
在这里插入图片描述

2.1 Mybatis程序的编写

resources目录:
放在这个目录当中的,一般都是资源文件,配置文件。直接放到resources目录下的资源,等同于放到了类的根路径下。

  • 步骤1:打包方式:jar(不需要war,因为mybatis封装的是jdbc。)
<groupId>com.powernode</groupId>
<artifactId>mybatis-001-introduction</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
  • 步骤2:引入依赖(mybatis依赖 + mysql驱动依赖)
<!--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:编写mybatis核心配置文件:mybatis-config.xml,在resources根目录下新建mybatis-config.xml配置文件

    • 从XNL中构建SqlSessionFactory
      通过官方的这句话,能想到什么呢?
      • 第一:在MyBatis中一定是有一个很重要的对象,这个对象是:SqlsessionFactory对象。
      • 第二:SqlSessionFactory对象的创建需要XML。
XNL是什么?
它一定是一个配置文件。
<?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/powernode"/> <!--自己的路径-->
                <property name="username" value="root"/><!--自己的用户名-->
                <property name="password" value="root"/><!--自己设置的密码-->
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
        <mapper resource=""/>
    </mappers>
</configuration>

注意:

  • 第一:这个文件名不是必须叫做mybatis-config.xml,可以用其他的名字。只是大家都采用这个名字。
  • 第二,这个文件存放的位置也不是固定的,可以随意,但一般情况下,会放到类的根路径下。
    mybatis-config.xml文件中的配置信息不理解没关系,先把连接数据库的信息修改以下即可。其他的别动。
  • 步骤4:编写XxxxMapper . xml文件在这个配置文件当中编写SQL语句
    在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语句:id是这条SQL语句的唯一标识。这个id就代表了这条SQL语句          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>
  • 步骤5:在mybatis-config.xml文件中指定XxxxMapper.xml文件的路径:
<mapper resource="CarMapper.xml"/>

注意:resource属性会自动从类的根路径下开始查找资源。

  • 注意1:sql语句最后结尾可以不写“;”
  • 注意2:CarMapper.xml文件的名字不是固定的。可以使用其它名字。
  • 注意3:CarMapper.xml文件的位置也是随意的。这里选择放在resources根下,相当于放到了类的根路径下。
  • 注意4:将CarMapper.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/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>

  • 步骤6:编写MyBatisIntroductionTest代码(使用mybatis的类库,编写mybatis程序,连接数据库,做增删改查就行了。)
    在MyBatis当中,负责执行SQL语句的那个对象叫什么?
    • SqlSession
  • SqlSession是专门用来执行SQL语句的,是一个Java程序和数据库之间的一次会话
  • 要想获取SqlSession对象,需要先获取SqlSeesionFactory对象,通过SqlSessionFactory工厂来生产SqlSession对象
  • 怎么获取SqlSessionFactory对象呢?
    • 需要首先获取SqlSessionFactoryBuilder对象
    • 通过SqlSessionFactoryBuilder对象的build方法,来获取一个SqlSessionFactory对象

mybatis的核心对象包括:

sqlSessionFactoryBuilder
sqlSessionFactory
sqlsession

sqlSessionFactoryBuilder --> sqlsessionFactory --> sqlSession

package com.powernode.mybatis.test;

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

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyBatisIntroductionTest {
    public static void main(String[] args) throws IOException {
        //1.获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

        //2.获取SqlSessionFactory对象,通过传入输入流的文件路径在工厂builder.build方法来进行创建
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");//填写mybatis-config.xml文件的路径
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);

        //3.获取SqlSession对象,通过工厂的openSession方法进行获取
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //4.执行SQL语句
        int count = sqlSession.insert("car");//放入设置的mapper文件里sql语句的id
        //返回值是影响数控表当中的记录条数

        System.out.println("插入了几条记录:" + count);

        //5.然后手动提交,因为SqlSession不支持自动提交
        sqlSession.commit();

        //6.关闭资源,(只关闭是不会提交的)
        sqlSession.close();
    }
}

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

2.2 关于程序的小细节

mybatis中sql语句的结尾;可以省略

  • Resources.getResourceAsStream小技巧:
    以后繁琐遇到resource这个单词,大部分情况下,这种加载资源的方式就是从类的根路径下开始加载的。

    • 优点:采用这种方式,从类路径当中加载资源,项目的移植性很强。项目从windowst移植到Linux,代码不需要修改,因为这个资源文件一直都在类路径当中
  • InputStream is = new FileInputStream("d ::\\mybatis-config.xml");采用这种方式获取也可以

    • 缺点:可移植性太差了,路径定死了,可能会移植到其他的操作系统当中。,导致以上路径无效,还需要修改java代码中的路径,违背了OCP原则

所以:

  • mybatis核心配置文件的名字,不一定是:mybatis-config.xml。可以是其他的名字
  • mybatis核心配置文件存放的路径,也不一定是再类的根路径下。可以放到其他位置。但是为了项目的可移植性,最好将这个配置文件放到类路径下

系统类加载器有一个方法叫做:getResourceAsStream它就是从类路径当中加载资源的。
通过源代码分析发现:

  • InputStream is = Resources.getResourceAsStream( "mybatis-config.xml");

底层的源代码其实就是:

  • InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");

CarMapper.xml文件的名字是固定的吗?
都不是固定的。

<mapper resource="CarHapper .xml"/> resource属性:这种方式是从类路径当中加载资源。
<mapper url="file:///d:/ CarMapper.xml"/> url属性:这种方式是从绝对路径当中加载资源。

2.3 mybatis事务管理机制解析

在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理<transactionManager type="JDBC"/>

  • type属性的值包括两个:
    • JDBC(jdbc)
    • MANAGED(managed)
    • type后面的值,只有以上两个值可选,不区分大小写

在mybatis中提供了两种事务管理:

  • 第一种:JDBC事务管理
  • 第二种:MANAGED事务管理

JDBC事务管理器:

  • mybatis框架自己管理事务,自己采用元素的JDBC代码去管理事务:
    • conn.setAutoCommit(false);开启事务
    • …业务处理…
    • conn.commit();手动提交事务
  • 使用JDBC事务管理器的话,底层创建的事务管理器对象:jdbctransaction对象
  • 如果编写的代码是下面的代码:
    • SqlSession sqlSession = sqlSessionFactory.openSession(true);
    • 表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false);
    • 在JDBC事务种,没有执行conn.setAutoCommit(false);那么autoCommit就是true
    • 如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次

MANAGED事务管理器:

  • mybatis不再负责事务的管理了。事务管理交给其他容器来负责。例如:spring—— 我不管事务了,你来负责

  • 对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED
    那么事务这块是没人管的。没有人管理事务表示事务压根没有开启

public class MyBatisIntroductionTest {
    public static void main(String[] args) throws IOException {
        //1.获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

        //2.获取SqlSessionFactory对象,通过传入输入流的文件路径在工厂builder.build方法来进行创建
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");//填写mybatis-config.xml文件的路径
        //Resources.getResourceAsStream默认就是从类的根路径下开始查找资源

        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);//一般情况下都是一个数据库对应一个SqlsessionFactory对象


        //3.获取SqlSession对象,通过工厂的openSession方法进行获取
//        SqlSession sqlSession = sqlSessionFactory.openSession(); //如果使用的事务管理器是JDBC的话,底层实际上会执行: conn.setAutoCommit(false);
        //这种方式实际上是不建议的,因为么一开启事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.执行SQL语句
        int count = sqlSession.insert("car");//放入设置的mapper文件里sql语句的id
        //返回值是影响数控表当中的记录条数

        System.out.println("插入了几条记录:" + count);

        //manager时
        //5.然后手动提交,因为SqlSession不支持自动提交
//        sqlSession.commit(); 如果使用的事务管理器是JDBC的话,底层实际上还是会执行conn.commit();

        //6.关闭资源,(只关闭是不会提交的)
        sqlSession.close();
    }
}

JDBC的事务:

  • 如果没有在JDBC代码种执行:conn.setAutoCommit(false);的话,默认的autoCommimt是true

完整的mybatis程序展示:

package com.powernode.mybatis.test;

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

import java.io.IOException;

public class MyBatisCompleteTestjava {
    //完整的写法
    public static void main(String[] args) {
        SqlSession sqlSession =null;
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
            //开启会(底层会开启事务)
            sqlSession = sqlSessionFactory.openSession();
            //执行SQL语句,处理相关业务
            int count = sqlSession.insert("car");

            //执行到这里,没有发生任何异常,提交事务,终止事务
            sqlSession.commit();
        } catch (IOException e) {
            //最好回滚事务
            if(sqlSession != null){
                sqlSession.rollback();
            }
            e.printStackTrace();
        } finally {
            //关闭会话(释放资源)
            if(sqlSession != null){
                sqlSession.close();
            }
        }

    }
}

2.4 开发中的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业务方法名(){}
    • @Test注解非常重要,被这个注解标注的方法就是一个单元测试方法
// 测试用例
public class CarMapperTest{
    
    // 测试方法
    @Test
    public void testInsert(){
   		//单元测试中有两个重要的概念:
		//一个是:实际值(被测试的业务方法的真正执行结果)
		//一个是:期望值(执行了这个业务方法之后,你期望的执行结果是多少)

    }
    
    @Test
    public void testUpdate(){}
    
}
  • 第三步:可以在类上执行,也可以在方法上执行
    • 在类上执行时,该类中所有的测试方法都会执行。
    • 在方法上执行时,只执行当前的测试方法。

案例:

import com.powernode.junit.service.MathService;
import org.junit.Assert;
import org.junit.Test;

public class MathServiceTest {
    @Test
    public void testSum(){
        //单元测试中有两个重要的概念:
        //一个是:实际值(被测试的业务方法的真正执行结果)
        //一个是:期望值(执行了这个业务方法之后,你期望的执行结果是多少)
        MathService mathService = new MathService();
        //获取实际值
        int actual = mathService.sum(1, 2);
        //期望值
        int expected = 3;

        //加个断言进行测试
        Assert.assertEquals(expected,actual);
    }

    @Test
    public void testSub(){
        MathService mathService = new MathService();
        int actual = mathService.sub(10, 5);

        int expected = 5;

        Assert.assertEquals(expected,actual);
    }
}

在这里插入图片描述

2.5 Mybatis 集成日志框架logback

关于mybatis集成日志组件,让我们调试起来更加方便

  • mybatis常见的集成的日志组件有哪些呢

    • SLF4J(沙拉风):沙拉风是一个日志标准,其中有一个框架叫做logback,它实现了沙拉风规范
    • LOG4J
    • STDOUT_LOGGING…
  • 其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志。mybatis框架本身已经实现了这种标准只要开启即可。

怎么开启呢?

mybatis-config.xml文件中使用settings标签进行配置开启。
例如:<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>

  • 这个标签在编写的时候需要注意,它应该出现在environments标签之前。注意顺序。当然,不需要记忆这个顺序。因为又dtd文件进行约束,只要参考dta约束即可
  • 这种实现也是可以的,可以看到一些信息,比如:连接对象什么时候创建,什么时候关闭,sql语句是怎样的
    在这里插入图片描述
    但是没有详细的日期,线程名字等。如果你想使用更加丰富的配置,可以集成第三方的log组件

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

logback是目前日志框架中性能较好的,较流行的,所以我们选它。logback日志框架实现了slf4j标准

引入logback的步骤:

  • 第一步:映入logback的依赖
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.11</version>
</dependency>
  • 第二步:引入logback所必须的xml配置文件(文件名叫做logback.xml或logback-test.xml,放到类路径当中)
    • 这个配置文件的名字必须叫做:logback.xml或者logback-test.xml,不能是其他的名字
    • 这个配置文件必须放到类的根路径下。不能是其他位置
    • 主要配置日志输出相关的级别以及日志具体的格式
<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

开启的日志:
在这里插入图片描述

2.6 Mybatis工具类的编写

创建utils包:
在这里插入图片描述

package com.powernode.mybatis.utils;

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

import java.io.IOException;

//mybatis工具类
public class SqlSessionUtil {
    //工具类的构造方法一般都是私有化的
    //工具类中的所有方法都是静态的,直接采用类名即可调用。不需要new对象
    // 为了防止new对象,构造方法私有化
    private SqlSessionUtil(){}


    private static SqlSessionFactory sqlSessionFactory;
    //类加载时执行
    //SqlSessionUtil工具类在进行第一次加载的时候,
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//    public static SqlSession openSession() throws IOException {
//        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//        //SqlSessionFactory对象:一个SqlSessionFactory对于一个environment,一个environment通常是一个数据库环境
//        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
//        SqlSession sqlSession = sqlSessionFactory.openSession();
//        return sqlSession;
//    }
    public static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }

}

测试:

    @Test
    public void testInsertCarByUtil(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.insert("car");
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }

三 小黑子教你MyBatis如何完成CRUD(增删改查)

什么是CRUD?

  • C:create 增
  • R:retrieve 查(检索)
  • U:update 改
  • D:delete 删

准备工作

  • 创建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 mybatis完成insert使用Map集合传参

使用MAP集合传参
分析以下SQL映射文件中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_type) values('103', '奔驰E300L', 50.3, '2022-01-01', '燃油车')
    </insert>
</mapper>

这样写存在的问题是:

  • SQL语句中的值不应该是写死到配置文件里,值应该是用户提供的,这样子写在实际开发中不存在。
  • 一定是要前端的form表单提交过来数据,然后将值传递给sql语句

之前的JDBC代码是这样写的:

// JDBC中采用 ? 作为占位符。那么MyBatis中会使用什么作为占位符呢?
String sql = "insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)";
// ......
// 给 ? 传值。那么MyBatis中应该怎么传值呢?
//例如:
ps.setString(1,"103");
ps.setString(2,"奔驰E300L");
ps.setDouble(3,50.3);
ps.setString(4,"2022-01-01");
ps.setString(5,"燃油车");

在MyBatis中可以这样做:

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

范例:
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语句:id是这条SQL语句的唯一标识。这个id就代表了这条SQL语句          insert sql:保存一个汽车信息-->
    <insert id="insertCar">
        insert into t_car
            (id,car_num,brand,guide_price,produce_time,car_type)
        values
            (null,#{k1},#{k2},#{k3},#{k4},#{k5})#放入map集合的key
    </insert>
</mapper>

测试代码:

package com.powernode.mybatis.test;

import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;

public class CarMapperTest {
    @Test
    public void testInsertCar(){
        SqlSession sqlSession = SqlSessionUtil.openSession();

        //假装前端传过来数据了
        //这个对象我们先使用Map集合进行数据的封装
        HashMap<String, Object> map = new HashMap<>();
        map.put("k1","1111");
        map.put("k2","byd");
        map.put("k3",10.0);
        map.put("k4","2020-11-11");
        map.put("k5","电车");


        //执行sql语句
        //insert方法的参数
        //第一个参数:sqlid,从CarMapper.xml文件中复制
        //第二个参数:封装数据对象
        int count = sqlSession.insert("insertCar", map);
        System.out.println(count);

        sqlSession.commit();
        sqlSession.close();
    }
}

在这里插入图片描述
在这里插入图片描述
所以java程序中使用Map可以给sql语句的占位符传值:

        HashMap<String, Object> map = new HashMap<>();
        map.put("k1","1111");
        map.put("k2","byd");
        map.put("k3",10.0);
        map.put("k4","2020-11-11");
        map.put("k5","电车");

        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{k1},#{k2},#{k3},#{k4},#{k5})

注意:#{这里写什么?写map集合的key,如果key不存在,获取的是null}
因此,一般map集合的key起名的时候要见名知意

如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写一个-->
<mapper namespace="car">
    <!-- insert语句:id是这条SQL语句的唯一标识。这个id就代表了这条SQL语句          insert 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})#放入map集合的key
    </insert>
</mapper>
package com.powernode.mybatis.test;

import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;

public class CarMapperTest {
    @Test
    public void testInsertCar(){
        SqlSession sqlSession = SqlSessionUtil.openSession();

        //假装前端传过来数据了
        //这个对象我们先使用Map集合进行数据的封装
        HashMap<String, Object> map = new HashMap<>();
        map.put("carNum","1111");
        map.put("brand","byd");
        map.put("guidePrice",10.0);
        map.put("produceTime","2020-11-11");
        map.put("carType","电车");


        //执行sql语句
        //insert方法的参数
        //第一个参数:sqlid,从CarMapper.xml文件中复制
        //第二个参数:封装数据对象
        int count = sqlSession.insert("insertCar", map);
        System.out.println(count);

        sqlSession.commit();
        sqlSession.close();
    }
}

3.1.1 使用POJO集合传参

java程序中使用POJO类给SQL语句的占位符传值

  • 第一步:定义一个pojo类Car,提供相关属性。
    在这里插入图片描述
package com.powernode.mybatis.pojo;

//封装汽车相关信息的pojo类,普遍的java类
public class Car {
    // 数据库表当中的字段应该和pojo类的属性一一对应
    //建议使用包装类Long,这样可以防止null的问题
    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 testInsertCarByPOJO(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //封装数据

       Car car = new Car(null,"3333","bydnmd",30.0,"2020-11-11","新能源");

        int count = sqlSession.insert("insertCar", car);//第一个放入的是CarMapper.xm里面的sqlid
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }
  • 第三步:SQL语句,#{ } 里写的是POJO的属性名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写一个-->
<mapper namespace="car">
    <!-- insert语句:id是这条SQL语句的唯一标识。这个id就代表了这条SQL语句          insert sql:保存一个汽车信息-->
    <insert id="insertCar">
            insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
            values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
</mapper>

在这里插入图片描述
在这里插入图片描述

经过测试得出结论:
在这里插入图片描述
严格意义上来说:如果使用POJO对象传递值的话,#{}这个大括号中到底写什么?

  • 写的是get方法名去掉get,然后将剩下的单词首字母小写,然后放进去
    例如:getUsername() --> #{username}

  • 如果采用map集合传参,#{} 里写的是map集合的key,如果key不存在不会报错,数据库表中会插入NULL

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

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

3.2 mybatis完成delete删除操作

需求:根据id删除数据

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

  • SQL语句写:
    注意:如果占位符只有一个,那么#{}的大括号里可以随意。但是最好见名知意思

    <delete id="deleteById">
        delete from t_car where id = #{SuiBianXie}
    </delete>
  • Java程序
@Test
    public void testDeleteById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.delete("deleteById", 5);
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }

在这里插入图片描述

3.3 mybatis完成update更新操作

需求:根据id修改某条记录

在这里插入图片描述

  • sql语句如下:
<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();

        //4L表示的是Long类型表示id:4 Long类型
        Car car = new Car(4L,"9999","magua",30.3,"1999-11-10","旧能源");
        int count = sqlSession.update("updateById", car);
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }

在这里插入图片描述
在这里插入图片描述

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

3.4 mybatis完成select查询一个的操作

select语句和其它语句不同的是:查询会有一个结果集对象:ResultSet。

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

  • SQL语句如下:
    <select id="selectById">
        select * from t_car where id = #{id}
    </select>
  • 测试:
    在JDBC中叫做ReusltSet,接下来是mybatis应该从ResultSet中取出数据,封装java对象
@Test
    public void testSelectById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();

        //执行sql语句
        Object car = sqlSession.selectOne("selectById", 1);
        System.out.println(car);
        sqlSession.commit();
        sqlSession.close();
    }

紧接着就报错了:
表示:已运行查询,但未找到映射语句" "的结果映射。很有可能既没有指定结果类型也没有指定结果映射
在这里插入图片描述

  • 运行后之前的异常不再出现了,这说明添加了resultType属性之后,解决了之前的异常,可以看出resultType是不能省略的:
  • 需要特别注意的是:select标签中resultType属性,这个属性用来告诉mybatis,查询结果集封装成说明类型的java对象。你需要告诉mybatis。reultType通常写的是:全限定类名
    在这里插入图片描述
    <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
        select * from t_car where id = #{id}
    </select>

在这里插入图片描述

通过观察发现:只有id和brand是一致的,其他字段名和属性名对应不上,这是不是导致null的原因呢?

  • carNum以及其他的这几个属性没有赋上值的原因是:
    • 在这里插入图片描述
    • car_num、guide_price、produce_time、car_type这是查询结果的列名。
      这些列名和Car类中的属性名对不上。
    • car类的属性名:
      carNum、guidePrice、produceTime、carType

那么这个问题该怎么解决?
我们尝试在sql语句中使用as关键字来给查询结果列名起别名试试:

    <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">

        select
                id,car_num as carNum,brand,guide_price as guidePrice,
               produce_time as procedureTime,
               car_type as carType
        from t_car
        where id = #{id}
    </select>

在这里插入图片描述

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

3.4.1 完成select查询所有

需求:查询所有的Car信息

  • sql语句
 <select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,
            produce_time as procedureTime,
            car_type as carType
        from t_car
    </select>
  • 测试:
@Test
    public void testSelectAll(){
        SqlSession sqlSession = SqlSessionUtil.openSession();

        List<Object> cars = sqlSession.selectList("selectAll");
        cars.forEach(car -> System.out.println(car));

        sqlSession.commit();
        sqlSession.close();
    }

在这里插入图片描述
在这里插入图片描述

注意:resultType还是指定要封装的结果集的类型。不是指定List类型,是指定List集合中元素的类型。selectList方法: mybatis通过这个方法就可以得知你需要一个List集合。它会自动给你返回一个List集合

3.5 SQLMapper映射文件中namespace的作用

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

  • 创建UserMapper.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="aaaaaaaaaaaaaaa">

    <select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,
            produce_time as procedureTime,
            car_type as carType
        from t_car
    </select>

</mapper>

不难看出,CarMapper.xml和UserMapper.xml文件中都有 id=“selectAll”
将UserMapper.xml配置到mybatis-config.xml文件中

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

报错:
在这里插入图片描述

【翻译】selectCarAll在Mapped Statements集合中不明确(请尝试使用包含名称空间的全名,或重命名其中一个条目)
【大致意思是】selectCarAll重名了,你要么在selectCarAll前添加一个名称空间,要有你改个其它名字。

  • 代码修改:
 @Test
    public void testNameSpace(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //List<Object> cars = sqlSession.selectList("selectAll");
        //正确完整的写法:namespace.id
        List<Object> cars = sqlSession.selectList("aaaaaaaaaaaaaaa.selectAll");
        cars.forEach(car-> System.out.println(car));
        sqlSession.commit();
        sqlSession.close();
    }

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值