SSM框架-MyBatis(一)

目录

1 MyBatis概述

1.1 框架

1.2 MyBatis在三层架构的位置

1.3 JDBC的不足

1.4 关于MyBatis

2 MyBatis入门程序

2.1 组件版本

2.2 MyBatis入门程序开发步骤

2.2.1 写代码前的准备

2.2.2 编写mybatis步骤

2.3 总结上个程序的小细节

2.4 事务管理机制的深度剖析

2.5 一个比较完整的mybatis程序

2.6 引入JUnit

2.6.1 开发中junit是怎么使用的

2.6.2 mybatis中引入junit

2.7 MyBatis集成日志框架logback

2.8 MyBatis⼯具类SqlSessionUtil的封装

3 使用MyBatis完成CRUD

3.1 insert(Create)

3.2 delete(Delete)

3.3 update(Update)

3.4 select(Retrieve)

3.5 关于Mapper文件的namespace

4 MyBatis核心配置文件详解

4.1 environment

4.2 transactionManager

4.3 dataSource

4.4 properties


1 MyBatis概述

1.1 框架

Java常用的框架:

  • SSM三大框架:Spring+SpringMVC+MyBatis
  • SpringBoot
  • SpringCloud
  • 等等

框架就是对通用代码的封装,提前写好了一堆接口和类,我们可以在做项目的时候直接引入这些接口和类,这样可以大大提高开发效率

框架一般以jar包的形式存在

SSM三大框架建议学习顺序:MyBatis-->Spring-->SpringMVC

1.2 MyBatis在三层架构的位置

在学javaweb的时候我们就详细学过三层架构,大概就是把业务分为三层:

  • 表现层:直接根前端打交道(一是接收前端ajax请求,二是返回JSON数据给前端)
  • 业务逻辑层:一是处理表现层转发过来的前端请求(也就是具体业务),二是将持久层获取的数据返回到表现层
  • 数据访问层(持久层):直接操作数据库完成增删改查(CRUD),并将获取的数据返回到上一层(也就是业务逻辑层)

本次我们学的MyBatis就是持久层框架

当然持久层框架不止MyBatis,常用的还有Spring Data

1.3 JDBC的不足

  • SQL语句写死在java程序,如果业务变化,我们需要修改代码,违背了OCP原则
  • 给?传值是繁琐的,都是重复的代码,自动化会很舒服
  • 将结果集封装到java对象也是繁琐的,自动化会很舒服

1.4 关于MyBatis

MyBatis是一个ORM框架,是一个半自动化的ORM,MyBatis中的SQL语句是需要自己编写的

什么是ORM:

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

大概的意思就是:假如有个数据库表t_user,表有列名name、age等等,它有一行行数据。假如有一个类为User类,它有属性name、age等等,我可以new对象装数据。这里的数据库表t_user和User类就形成一个对应

MyBatis框架的特点:

  • 支持定制化SQL、存储过程、基本映射以及高级映射
  • 避免了几乎所有jdbc代码中手动设置参数以及获取结果
  • 支持XML开发,也支持注解开发(为了保证SQL语句的灵活,所以MyBatis大部分采用XML开发)
  • 接口和java的POJOs映射成数据库中的记录(这里的pojo有的人会使用bean或者domain)
  • 体积小,好学等等

2 MyBatis入门程序

2.1 组件版本

  • MySQL驱动:8.0.30
  • MyBatis:3.5.10
  • JDK:Java17
  • Logback:1.2.11

2.2 MyBatis入门程序开发步骤

2.2.1 写代码前的准备

先使用Navicat新建一个数据库如下:

设计一个数据库名为t_car,然后随便插入两个数据:

配置idea

先创建一个名为mybatis的空项目,然后设置maven,这里的maven之前设置过了 

选择jdk版本 

创建一个maven模块,不使用maven骨架

2.2.2 编写mybatis步骤

步骤一:打包方式:jar(不需要war,因为mybatis封装的是jdbc)

步骤二:引入依赖(mybatis和mysql依赖)

这个去这个网站上找https://mvnrepository.com/

    <dependencies>

        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
        <!--mysql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

    </dependencies>

步骤三:在resources根目录下新建mybatis-config.xml配置文件(参考mybatis手册拷贝)

https://mybatis.org/mybatis-3/zh/index.html    这是mybatis手册中文版网址

这个文件是核心配置文件,主要配置连接数据库的信息等

注意1:放在resources目录中的一般都是资源文件、配置文件,等同于放到类的根路径下

注意2:mybatis核心配置文件文件名不一定是mybatis-config.xml,可以是其它名字,不过大家都是用这个。配置文件位置也可以随意,这里放到resources下相当于放到类的根路径下。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://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="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>

注意修改中间的连接数据库的信息部分,这里是我们学jdbc经常配置的

步骤四:在resources根目录下新建CarMapper.xml配置文件(参考mybatis手册)

这个文件是专门写SQL语句的配置文件(建议一个表一个)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写⼀个-->
<mapper namespace="car">
    <!--插入一个信息-->
    <!--这里的id是这条SQL语句的唯一标识,这个id就代表了这条SQL语句-->
    <insert id="insertCar">
        insert into t_car
        (id,car_num,brand,guide_price,produce_time,car_type)
        values
        (null,'102','丰⽥mirai',40.30,'2014-10-05','氢能源')
    </insert>
</mapper>

注意1:SQL语句最后结尾可以不写分号

注意2:CarMapper.xml文件的名字不是固定的

注意3:CarMapper.xml文件的位置也是随意的

注意4:将CarMapper.xml文件路径配置到mybatis-config.xml中

resources属性会自动从类的根路径下找资源,而CarMapper这个文件恰好在根路径

步骤五: 编写MyBatisIntroduction代码

在MyBatis中,负责执行SQL语句的对象叫SqlSession,SqlSession是专门用来执行SQL语句的,是一个Java程序和数据库之间的一次会话

要想获取SqlSession对象,需要先获取SqlSessionFactory对象,通过SqlSessionFactory工厂来生产SqlSession对象。要想获取SqlSessionFactory对象,需要先获取SqlSessionFactoryBuilder对象,通过SqlSessionFactoryBuilder对象的build方法获取一个SqlSessionFactory对象

实际操作如下

  • 先new一个SqlSessionFactoryBuilder对象
  • 再在使用SqlSessionFactoryBuilder对象调用build方法,我们需要给这个方法传个输入流参数,这个输入流指向mybatis-config.xml文件
  • 我们可以先new一个FileInputStream获取到这个文件的流,我们还可以使用mybatis的一个工具类:Resources,调用Resources的方法getResourcesAsStream。这个方法默认从根的路径下找资源。得到输入流传给build方法
  • 使用SqlSessionFactory对象调用openSession方法得到sqlSession对象
  • 使用sqlSession对象调用方法执行SQL语句
  • 切记要手动提交,sqlSession默认不提交数据

注意:一般情况下一个数据库对应一个sqlSessionFactory对象

代码如下:

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

import java.io.InputStream;

public class MyBatisIntroductionTest {
    public static void main(String[] args) throws Exception{
        //获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //获取SqlSessionFactory对象
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        //获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //执行SQL
        int count = sqlSession.insert("insertCar");//返回的是影响数据库条数
        System.out.println("插入几条记录:"+count);

        //手动提交
        sqlSession.commit();
    }
}

2.3 总结上个程序的小细节

  • mybatis中SQL语句的结尾“;”可以省略
  • 关于Resources.getResourceAsStream():小技巧:但凡遇到resource这个单词,大部分情况下,这种加载资源的方式是从类的根路径下开始查找。这种方式,项目的移植性很强
  • 在我们获取mybatis-config.xml这个核心文件时,还可以使用InputStream is = new FileInputStream()。但是这种方式移植性差,违背了OCP原则
  • InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(),使用这个也可以获取resources目录下的资源,和Resources.getResourceAsStream一样的效果。我们 通过源码发现,Resources工具类的getResourceAsStream底层就是使用ClassLoader获取到的。
  • CarMapper.xml文件的名字和目录也不是固定的。我们还可以使用url属性获取mapper文件,不过不建议,就像上面获取mybatis核心配置文件一样,这样获取移植性差,不建议使用。

2.4 事务管理机制的深度剖析

在mybatis-config.xml文件中,可以通过以下配置进行mybatis的事务管理

<transactionManager type="JDBC"/>

type属性的值有两个:JDBC(jdbc)、MANAGED(managed),不区分大小写

也就是说mybatis中提供了两种事务管理机制

JDBC事务管理器:

mybatis框架自己管理事务,自己采用原生的jdbc代码去管理事务:conn.setAutoCommit(false)用来开启事务--->业务处理--->conn.commit()手动提交事务。

使用JDBC事务管理器的话,底层创建的事务管理器对象是:JdbcTransaction对象

如果编写的代码是以下的代码:

SqlSession sqlSession = sqlSessionFactory.openSession(true);表示没有开启事务,因为这种方式压根不会执行:conn.setAutoCommit(false)。这种情况,只要DML语句执行就会提交。

SqlSession sqlSession = sqlSessionFactory.openSession(),也就是默认不写参数,这表示如果使用的事务管理器是jdbc的话,底层会执行conn.setAutoCommit(false),也就是默认开启事务。

sqlSession.commit(),如果使用的事务管理器是jdbc的话,底层实际上执行的是conn.commit()

不开启事务的方式我们是不建议的

MANAGED事务管理器

mybatis不再负责事务的管理,事务交给其它容器管理,例如:spring

对于我们目前单纯地使用mybatis的情况下,如果配置MANAGED,那么事务是没人管的,没人管表示事务没开启,就是没有事务。当然这也是我们不建议的。

重点:

以后注意,只要autoCommit是true就表示没有开启事务,是false就表示开启了事务

2.5 一个比较完整的mybatis程序

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

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

public class MyBatisCompleteTest {
    public static void main(String[] args){
        SqlSession sqlSession = null;
        try {
            //获取SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //获取SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
            //获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession();
            //执行SQL
            int count = sqlSession.insert("insertCar");
            System.out.println("执行条数:"+count);
            //执行到这里没有异常,提交事务,终止事务
            sqlSession.commit();
        } catch (IOException e) {
            //最好回滚事务
            if (sqlSession != null){
                sqlSession.rollback();
            }
            e.printStackTrace();
        }finally {
            //关闭会话
            sqlSession.close();
        }
    }
}

比上个程序多了:回滚事务和关闭会话还有异常使用try-catch处理

2.6 引入JUnit

2.6.1 开发中junit是怎么使用的

JUnit是专门做单元测试的组件,在实际开发中,单元测试是由我们Java程序员来完成的。测试过程中设计两个概念:期望值和实际值。期望值和实际值相同表示测试通过,不同则报错。

在这里引入JUnit是为了代替main方法

使用JUnit步骤:

第一步:引入依赖

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

第二步:在main目录里面编写一个类,类里面有方法

package com.itzw.junit;

public class MathService {
    /**
     * 求和业务方法
     * @param a
     * @param b
     * @return
     */
    public static int sum(int a,int b){
        return a + b;
    }

    /**
     * 相减业务方法
     * @param a
     * @param b
     * @return
     */
    public static int sub(int a,int b){
        return a - b;
    }
}

第三步:在test目录里面编写测试方法

  • 在这个目录里面创建的包名一般来说是和mian里面的包名一样。test里面的类名一般来说是main里面的类名加上Test。
  • 测试方法规范:public void testXxx(){},假如测试的方法是sum,那么测试方法名为testSum()
  • 另外要在测试方法上加上@Test注解
package com.itzw.junit;

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, 2);
        //期望值
        int expected = 8;
        //加断言进行测试
        Assert.assertEquals(expected,actual);
    }
}

2.6.2 mybatis中引入junit

和上面一样,最后测试如下:

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

import java.io.IOException;

public class CarMapperTest {
    @Test
    public void testInsertCar(){
        SqlSession sqlSession = null;
        try {
            //获取SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //获取SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
            //获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession();
            //执行SQL
            int count = sqlSession.insert("insertCar");
            System.out.println("执行条数:"+count);
            //执行到这里没有异常,提交事务,终止事务
            sqlSession.commit();
        } catch (IOException e) {
            //最好回滚事务
            if (sqlSession != null){
                sqlSession.rollback();
            }
            e.printStackTrace();
        }finally {
            //关闭会话
            sqlSession.close();
        }
    }
}

就是在测试类里面创建一个方法,然后将之前写的代码main里面的部分复制到这里即可。

2.7 MyBatis集成日志框架logback

引入日志框架的目的是为了看清楚MyBatis执行的具体SQL

启用标准日志组件,只需在mybatis-config.xml文件中添加以下配置(参考mybatis手册):

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

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

  • SLF4J
  • LOG4J
  • LOG4J2
  • STDOUT_LOGGING

其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志,只要开启即可,怎么开启?上面那个setting配置就是开启了。

注意:使用settings标签要放在environment标签之前。注意顺序,当然你出错idea会提示,不用怕

经过测试STDOUT_LOGGING日志发现它内容很少,我们可以继承其它日志组件例如log4j,logback等

引入logback步骤:

第一步:引入logback相关依赖

        <!--logback依赖,这个日志框架实现了slf4j-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
            <scope>test</scope>
        </dependency>

第二步:引入logback相关配置文件(文件名叫做logback.xml或者logback-test.xml,放到类路径中)

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义⽇志⽂件的存储地址-->
    <property name="LOG_HOME" value="/home"/>
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncode
r">
            <!--格式化输出:%d表示⽇期,%thread表示线程名,%-5level:级别从左显示5
           个字符宽度%msg:⽇志消息,%n是换⾏符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logge
                r{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天⽣成⽇志⽂件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.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 %logge
                r{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>

我们使用这个日志就可以看到SQL语句了

注意:如果使用标准日志组件STDOUT_LOGGING需配置setting,如果是第三方日志组件则不需要配置也行。

2.8 MyBatis⼯具类SqlSessionUtil的封装

每一次获取sqlSession对象代码太繁琐了,封装一个工具类

SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库,所以并不是每次都需要创建一个新的SqlSessionFactory对象,所以我们把SqlSessionFactory对象的创建放在静态代码块中,也就是说类加载时创建而且只创建一次

工具类如下:

package com.itzw.mybatis.util;

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

import java.io.IOException;

public class SqlSessionUtil {

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

    /**
     * 类加载时获取sqlSessionFactory对象
     */
    private static SqlSessionFactory sqlSessionFactory ;
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 每调用一次openSession获得一个新的会话
     * @return
     */
    public static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }
}

测试工具类是否可用:

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

3 使用MyBatis完成CRUD

什么是CRUD

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

先准备环境,就是上面我们学到的:

  • MyBatis、mysql、junit、logback依赖
  • mybatis-config、mapper、logback配置文件
  • sqlSession工具类

3.1 insert(Create)

分析之前写的SQL语句存在的问题:值不该写死,应该是用户提供的值,在JDBC中我们用占位符?实现动态存值,这里我们使用#{}。这和JDBC中的?是等效的

我们在后端提供一个map集合,map集合中的key随意取值,不过我们一般见名知意。而map集合的值就是我们想存的值,那么这个key就可以取名为对应值得key,其实就可以根据数据库表的列名取值。然后在mapper.xml文件中,在#{}中的大括号中写入这个key,它会自动获取到这个key对应的值。修改SQL语句如下:

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

测试方法如下:主要关注map集合和insert方法

    @Test
    public void testInsetCar(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Map map = new HashMap();
        map.put("carNum","1111");
        map.put("brand","比亚迪");
        map.put("guidePrice",10);
        map.put("produceTime","2021-11-11");
        map.put("carType","新能源");
        int count = sqlSession.insert("insertCar", map);
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }

既然使用map集合可以封装数据进行插入数据操作,那么使用POJO类可不可以?

下面使用POJO类封装数据:

在封装数据前自然是要写一个类,名为Car。这里的属性应该和数据库表中的字段一一对应,并且建议使用包装类,这样可以防止null的问题,因为基本数据类型不能赋值为null

那SQL语句中的#{}中又改写什么呢?写POJO类定义的属性名称,因为它是和数据库表对应的,写这些属性名也是和数据库表对应的。

经过我们测试发现,为什么写的是属性名呢?因为底层得到这个属性它会去找对应的getter方法,比如#{username},它会去找getUsername方法,而我们创建的getter和setter方法都是符合java规范的,也都是这样命名的,所以我们直接写入属性名称它自然能找到对应的方法。或者我们可以说这个大括号内写的是get方法去掉get并且把首字母小写得到的。

pojo类:

package mybatis.pojo;

public class Car {
    private Long id;
    private String carName;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    public Car(){}

    public Car(Long id, String carName, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carName = carName;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carName='" + carName + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", carType='" + carType + '\'' +
                '}';
    }

    public Long getId() {
        return id;
    }

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

    public String getCarName() {
        return carName;
    }

    public void setCarName(String carName) {
        this.carName = carName;
    }

    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;
    }
}

SQL语句:

        insert into t_car (id,car_num,brand,guide_price,produce_time,car_type)
        values
        (null,#{carName},#{brand},#{guidePrice},#{produceTime},#{carType})

测试类:

    @Test
    public void testInsertCarByPojo(){
        Car car = new Car(null,"123","比亚迪秦",30.0,"2021-4-3","新能源");
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.insert("insertCar", car);
        System.out.println(count);
        sqlSession.commit();
        sqlSession.close();
    }

3.2 delete(Delete)

根据id删除数据

SQL语句:

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

注意:如果占位符只有一个,那么可以随便写内容,但是还是建议见名知意

测试类:

    @Test
    public void deleteById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //删除id为12的
        //int cout = sqlSession.delete("deleteById", 12);
        int cout = sqlSession.delete("deleteById", "13");
        System.out.println(cout);
        sqlSession.commit();
        sqlSession.close();
    }

3.3 update(Update)

根据id修改某条信息

SQL语句:

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

测试类:

    @Test
    public void testUpdateById(){
        Car car = new Car(3L,"4716","奔驰",56.6,"2009-9-9","燃油车");
        SqlSession sqlSession = SqlSessionUtil.openSession();
        sqlSession.update("updateById",car);
        sqlSession.commit();
        sqlSession.close();
    }

3.4 select(Retrieve

根据id查询一条信息

SQL语句:

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

注意1:这里要使用resultType属性指定查询结果集封装成什么类型的java

注意2:我们不能直接查询所有列名,要和Car类的属性一致才行,否则查不到信息,我们可以使用as来起别名,和类的属性对应上

测试类:

    @Test
    public void testSelectById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Object car = sqlSession.selectOne("selectById", 1);
        System.out.println(car);
        sqlSession.close();
    }

上面是查询一个,那我们试试查询所有信息:

SQL语句:

    <select id="selectAll" resultType="com.itzw.mybatis.pojo.Car">
        select id,
            car_num as carName,
            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<Car> cars = sqlSession.selectList("selectAll");
        cars.forEach(car -> System.out.println(car));
        sqlSession.close();
    }

3.5 关于Mapper文件的namespace

我们新创建一个Mapper文件,名为UserMapper,在里面有和之前的CarMapper文件相同的id,然后在mybatis-config文件中引入这个文件,我们来测试这样还行不行,文件如下:

测试类:

    @Test
    public void testNamespace(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        List<Car> cars = sqlSession.selectList("selectAll");
        cars.forEach(car -> System.out.println(car));
        sqlSession.close();
    }

很自然的就出错了。

很显然是因为selectAll这个id在两个Mapper文件中都有

这时namespace就派上了用场,sqlId的完成写法为namespace.id,namespace就是为了防止命名冲突的

4 MyBatis核心配置文件详解

4.1 environment

首先新建一个数据库名为mybatis,在里面创建一个表名为t_car和之前一样

在核心配置文件中我们新建一个环境,如下:

    <environments default="powernodeDB">

        <environment id="mybatisDB">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

        <environment id="powernodeDB">
            <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="123456"/>
            </dataSource>
        </environment>
    </environments>

上面一共是两个环境,不同的就是一个连接的是powernode数据库,一个连接的是mybatis数据库,而在environments标签中有个default属性,它的值是powernodeDB,也就是powernode那个环境的id值,也就是说默认连接的是powernode数据库。一个环境对应一个SqlSessionFactory对象,也就是一个数据库对应一个SqlSessionFactory对象。

当我们创建SqlSessionFactory对象的时候,我们发现在我们填完mybatis核心配置文件后还能再写个参数,而这个参数就是environment。也就是说我们可以指定创建的SqlSessionFactory对象连接到的数据库是哪一个,而不指定就是默认使用environments中default的值。

我们先测试默认情况下会连接哪个数据库:

    @Test
    public void testEnvironment() throws Exception{
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Car car = new Car(null,"4321","劳斯莱斯",222.2,"2004-4-4","燃油车");
        sqlSession.insert("insertCar",car);
        sqlSession.commit();
        sqlSession.close();
    }

运行以上代码我们发现正是powernode数据库中的表新增了数据,和我们想的一样

我们再指定数据库为mybatis试试

    @Test
    public void testEnvironment2() throws Exception{
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"),"mybatisDB");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Car car = new Car(null,"1234","aolia",222.2,"2004-4-4","燃油车");
        sqlSession.insert("insertCar",car);
        sqlSession.commit();
        sqlSession.close();
    }

 果然这次是mybatis数据库新增了数据

4.2 transactionManager

这个属性在前面就讲过,它的作用是配置事务管理器,指定mybatis具体使用什么方式管理事务

其中type属性有两个值:

  • 第一个:JDBC
  • 第二个:MANAGED

大概就是JDBC会使用原生的JDBC来挂你了事务。而使用MANAGED,mybatis不再负责事务的管理,将事务管理交给其它容器管理:比如Spring

4.3 dataSource

在核心配置文件中,dataSource是如下的:

dataSource配置:

  • dataSource被称为数据源
  • dataSource作用是什么:为程序提供Connection对象(但凡给程序提供Connection对象的都叫做数据源)
  • 数据源实际上是一套规范。JDK中有这套规范:javax.sql.DataSource
  • 我们自己也可以编写数据源组件,只要实现javax.sql.DataSource接口就行了。比如自己可以写一个数据库连接池(数据库连接池是提供连接对象的,所以数据库连接池就是一个数据源)

常见的数据源组件有哪些(常见的数据库连接池):

  • 阿里巴巴的德鲁伊连接池:druid
  • c3p0
  • dbcp

type属性用来指定数据源的类型,就是具体指定使用什么方式来获取Connection对象:

  • type属性有三个值,必须是三选一
  • type="[UNPOOLED|POOLED|JNDI]"
  • UNPOOLED:不使用数据库连接池技术,每一次请求都是创建新的Connection对象
  • POOLED:使用mybatis自己实现的数据库连接池
  • JNDI:集成其它第三方的数据库连接池

JDNI是一套规范,谁实现了这套规范呢?大部分的web容器都实现了JNDI规范:例如Tomcat、Jetty、WebLogic\WebSphere

连接池的优点:每一次获取连接都是从池中拿,效率高。因为每一次只能从池中拿,所以连接对象的创建数量是可控的。

我们打开mybatis帮助文档,发现不止有我们之前配置的那几个属性,还有别的,我们测试一下

正常使用连接池的话,池中有很多参数是需要设置的,设置好参数可以让连接池发挥的更好

下面是一些属性的设置:

            <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="123456"/>
                <!--poolMaximumActiveConnections:连接池当中对多的正在使用的连接对象数量上限,默认为10 -->
                <property name="poolMaximumActiveConnections" value="10"/>
                <!--poolTimeToWait:每隔两秒打印日志,并且尝试获取连接对象-->
                <property name="poolTimeToWait" value="2000"/>
                <!--poolMaximumCheckoutTime:超时时间限制为10秒-->
                <property name="poolMaximumCheckoutTime" value="10000"/>
                <!--poolMaximumIdleConnections:最多空闲数量-->
                <property name="poolMaximumIdleConnections" value="5"/>
            </dataSource>

4.4 properties

我们可以在properties标签中配置很多属性。比如配置jdbc连接的信息

    <properties>
        <property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis"/>
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="123456"/>
    </properties>

这就相当于map集合,然后在dataSource中获取上面的配置信息:

                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>

但是这个多少有点多此一举,这只是展示mybatis的灵活性。我们有更好用的方式如下:

在resources目录中新建jdbc.properties文件

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

然后在mybatis核心配置文件引入这个配置信息,使用properties标签引入:

    <!--resouce一定是从类路径下开始查找资源-->
    <properties resource="jdbc.properties"></properties>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值