mybatis学习(一)

ops/2024/11/25 22:49:34/
mybatismybatis_1">声明:该内容来源于动力节点,本人在学习mybatis过程中参考该内容,并自己做了部分笔记,但个人觉得本人做的笔记不如动力节点做的好,故使用动力节点的笔记作为后续mybatis的复习。

一、MyBatis概述

1.1 框架

  • 在文献中看到的framework被翻译为框架
  • Java常用框架:
    • SSM三大框架:Spring + SpringMVC + MyBatis
    • SpringBoot
    • SpringCloud
    • 等。。
  • 框架其实就是对通用代码的封装,提前写好了一堆接口和类,我们可以在做项目的时候直接引入这些接口和类(引入框架),基于这些现有的接口和类进行开发,可以大大提高开发效率。
  • 框架一般都以jar包的形式存在。(jar包中有class文件以及各种配置文件等。)
  • SSM三大框架的学习顺序:MyBatis、Spring、SpringMVC(仅仅是建议)

1.2 三层架构

三层架构.png

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

1.3 JDBC不足

  • 示例代码1:
// ......
// sql语句写死在java程序中
String sql = "insert into t_user(id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade) values(?,?,?,?,?,?,?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 繁琐的赋值:思考一下,这种有规律的代码能不能通过反射机制来做自动化。
ps.setString(1, "1");
ps.setString(2, "123456789");
ps.setString(3, "zhangsan");
ps.setString(4, "123456");
ps.setString(5, "1980-10-11");
ps.setString(6, "男");
ps.setString(7, "zhangsan@126.com");
ps.setString(8, "北京");
ps.setString(9, "大兴区凉水河二街");
ps.setString(10, "1000000");
ps.setString(11, "16398574152");
ps.setString(12, "A");
// 执行SQL
int count = ps.executeUpdate();
// ......
  • 示例代码2:
// ......
// sql语句写死在java程序中
String sql = "select id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade from t_user";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
List<User> userList = new ArrayList<>();
// 思考以下循环中的所有代码是否可以使用反射进行自动化封装。
while(rs.next()){// 获取数据String id = rs.getString("id");String idCard = rs.getString("idCard");String username = rs.getString("username");String password = rs.getString("password");String birth = rs.getString("birth");String gender = rs.getString("gender");String email = rs.getString("email");String city = rs.getString("city");String street = rs.getString("street");String zipcode = rs.getString("zipcode");String phone = rs.getString("phone");String grade = rs.getString("grade");// 创建对象User user = new User();// 给对象属性赋值user.setId(id);user.setIdCard(idCard);user.setUsername(username);user.setPassword(password);user.setBirth(birth);user.setGender(gender);user.setEmail(email);user.setCity(city);user.setStreet(street);user.setZipcode(zipcode);user.setPhone(phone);user.setGrade(grade);// 添加到集合userList.add(user);
}
// ......
  • JDBC不足:
    • SQL语句写死在Java程序中,不灵活。改SQL的话就要改Java代码。违背开闭原则OCP。
    • 给?传值是繁琐的。能不能自动化???
    • 将结果集封装成Java对象是繁琐的。能不能自动化???

1.4 了解MyBatis

  • MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
  • MyBatis在三层架构中负责持久层的,属于持久层框架。
  • MyBatis的发展历程:【引用百度百科】
    • MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
    • iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
  • 打开mybatis代码可以看到它的包结构中包含:ibatis
    • 42857443-5184-45b5-8AD4-2B281C88A8CC.png
  • ORM:对象关系映射
    • O(Object):Java虚拟机中的Java对象
    • R(Relational):关系型数据库
    • M(Mapping):将Java虚拟机中的Java对象映射到数据库表中一行记录,或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象。
    • ORM图示
      • C48A1A89-244D-40f4-85DB-F1E665A2EA62.png
        -A21555BB-55B7-4d7c-8AB0-F49B10E96BD3.png
    • MyBatis属于半自动化ORM框架。
    • Hibernate属于全自动化的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 版本

软件版本:

  • IntelliJ IDEA:2022.1.4
  • Navicat for MySQL:16.0.14
  • MySQL数据库:8.0.30

组件版本:

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

2.2 MyBatis下载

  • 从github上下载,地址:https://github.com/mybatis/mybatis-3
    • 2FFBA369-A41E-4b17-81C5-68A18B8A428F.png
    • 0D36080D-53BB-42ac-9E19-26260818134E.png
  • 将框架以及框架的源码都下载下来,下载框架后解压,打开mybatis目录
    • 55C87E1A-E3AA-414f-9281-CE7D5702EA17.png
    • 通过以上解压可以看到,框架一般都是以jar包的形式存在。我们的mybatis课程使用maven,所以这个jar我们不需要。
    • 官方手册需要。

2.3 MyBatis入门程序开发步骤

  • 写代码前准备:
    • 准备数据库表:汽车表t_car,字段包括:
      • id:主键(自增)【bigint】
      • car_num:汽车编号【varchar】
      • brand:品牌【varchar】
      • guide_price:厂家指导价【decimal类型,专门为财务数据准备的类型】
      • produce_time:生产时间【char,年月日即可,10个长度,‘2022-10-11’】
      • car_type:汽车类型(燃油车、电车、氢能源)【varchar】
    • 使用navicat for mysql工具建表
      • 16B6D507-440F-460f-9D1F-39653A3B6EC2.png
    • 使用navicat for mysql工具向t_car表中插入两条数据,如下:
      -7E8510B9-9886-41c9-9495-0AE7888560B9.png - 创建Project:建议创建Empty Project,设置Java版本以及编译版本等。
      • 52176FB4-6DB5-4264-8940-34A3506C2A0C.png
      • 1C8EB1AF-0465-4e2d-816A-B7FFBA2E42C4.png
    • 设置IDEA的maven
      • 3EA79931-EE2C-46f4-BDB2-6AB99EDFCA87.png
    • 创建Module:普通的Maven Java模块
      • 8F6F3B34-B1F7-4fcb-9584-AF629200534F.png
  • 步骤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:在resources根目录下新建mybatis-config.xml配置文件(可以参考mybatis手册拷贝)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//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>

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

  • 步骤4:在resources根目录下新建CarMapper.xml配置文件(可以参考mybatis手册拷贝)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写一个-->
<mapper namespace="car"><!--insert sql:保存一个汽车信息--><insert id="insertCar">insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,'102','丰田mirai',40.30,'2014-10-05','氢能源')</insert>
</mapper>

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

<mapper resource="CarMapper.xml"/>
  • 步骤5:编写MyBatisIntroductionTest代码
package com.powernode.mybatis;import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;/*** MyBatis入门程序* @author 老杜* @since 1.0* @version 1.0*/
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. 执行sqlint count = sqlSession.insert("insertCar"); // 这个"insertCar"必须是sql的idSystem.out.println("插入几条数据:" + count);// 5. 提交(mybatis默认采用的事务管理器是JDBC,默认是不提交的,需要手动提交。)sqlSession.commit();// 6. 关闭资源(只关闭是不会提交的)sqlSession.close();}
}

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

  • 步骤6:运行程序,查看运行结果,以及数据库表中的数据
    • 211D2E7E-E62B-413a-9710-72AC1EC7D894.png
    • 2A4C1E6E-56B8-440e-A368-F10434EACC2D.png

2.4 关于MyBatis核心配置文件的名字和路径详解

  • 核心配置文件的名字是随意的,因为以下的代码:
// 文件名是出现在程序中的,文件名如果修改了,对应这里的java程序也改一下就行了。
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");
  • 核心配置文件必须放到resources下吗?放到D盘根目录下,可以吗?测试一下:

mybatis-config.xml文件拷贝一份放到D盘根下,然后编写以下程序:

package com.powernode.mybatis;import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.FileInputStream;
import java.io.InputStream;/*** 测试mybatis核心配置文件路径问题* @author 老杜* @since 1.0* @version 1.0*/
public class MyBatisConfigFilePath {public static void main(String[] args) throws Exception{// 1. 创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 2. 创建SqlSessionFactory对象// 这只是一个输入流,可以自己new。InputStream is = new FileInputStream("D:/mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);// 3. 创建SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();// 4. 执行sqlint count = sqlSession.insert("insertCar");System.out.println("插入几条数据:" + count);// 5. 提交(mybatis默认采用的事务管理器是JDBC,默认是不提交的,需要手动提交。)sqlSession.commit();// 6. 关闭资源(只关闭是不会提交的)sqlSession.close();}
}

以上程序运行后,看到数据库表t_car中又新增一条数据,如下(成功了):3DD22C5B-8359-4644-853E-A27AF1D0FBBA.png

经过测试说明mybatis核心配置文件的名字是随意的,存放路径也是随意的。
虽然mybatis核心配置文件的名字不是固定的,但通常该文件的名字叫做:mybatis-config.xml
虽然mybatis核心配置文件的路径不是固定的,但通常该文件会存放到类路径当中,这样让项目的移植更加健壮。

  • mybatis中提供了一个类:Resources【org.apache.ibatis.io.Resources】,该类可以从类路径当中获取资源,我们通常使用它来获取输入流InputStream,代码如下
// 这种方式只能从类路径当中获取资源,也就是说mybatis-config.xml文件需要在类路径下。
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

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

package com.powernode.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;/*** 比较完整的第一个mybatis程序写法* @author 老杜* @since 1.0* @version 1.0*/
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.执行SQLint 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();}}}
}

运行后数据库表的变化:EC983E7A-7A85-4df7-B5FC-5C4FDC8EA592.png

2.6 引入JUnit

  • JUnit是专门做单元测试的组件。
    • 在实际开发中,单元测试一般是由我们Java程序员来完成的。
    • 我们要对我们自己写的每一个业务方法负责任,要保证每个业务方法在进行测试的时候都能通过。
    • 测试的过程中涉及到两个概念:
      • 期望值
      • 实际值
    • 期望值和实际值相同表示测试通过,期望值和实际值不同则单元测试执行时会报错。
  • 这里引入JUnit是为了代替main方法。
  • 使用JUnit步骤:
    • 第一步:引入依赖
<!-- junit依赖 -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>
  • 第二步:编写单元测试类【测试用例】,测试用例中每一个测试方法上使用@Test注解进行标注。
    • 测试用例的名字以及每个测试方法的定义都是有规范的:
      • 测试用例的名字:XxxTest
      • 测试方法声明格式:public void test业务方法名(){}
// 测试用例
public class CarMapperTest{// 测试方法@Testpublic void testInsert(){}@Testpublic void testUpdate(){}}
  • 第三步:可以在类上执行,也可以在方法上执行
    • 在类上执行时,该类中所有的测试方法都会执行。
    • 在方法上执行时,只执行当前的测试方法。
  • 编写一个测试用例,来测试insertCar业务
package com.powernode.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;public class CarMapperTest {@Testpublic void testInsertCar(){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.执行SQLint 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();}}}
}

执行单元测试,查看数据库表的变化:AEE76A27-5761-4162-B9DB-CCE0814EC06C.png

2.7 引入日志框架logback

  • 引入日志框架的目的是为了看清楚mybatis执行的具体sql。
  • 启用标准日志组件,只需要在mybatis-config.xml文件中添加以下配置:【可参考mybatis手册】
<settings><setting name="logImpl" value="STDOUT_LOGGING" />
</settings>

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

  • logback是目前日志框架中性能较好的,较流行的,所以我们选它。
  • 引入logback的步骤:
    • 第一步:引入logback相关依赖
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version><scope>test</scope>
</dependency>
  • 第二步:引入logback相关配置文件(文件名叫做logback.xml或logback-test.xml,放到类路径当中)
<?xml version="1.0" encoding="UTF-8"?><configuration debug="false"><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!-- 按照每天生成日志文件 --><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名--><FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern><!--日志文件保留天数--><MaxHistory>30</MaxHistory></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder><!--日志文件最大的大小--><triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"><MaxFileSize>100MB</MaxFileSize></triggeringPolicy></appender><!--mybatis log configure--><logger name="com.apache.ibatis" level="TRACE"/><logger name="java.sql.Connection" level="DEBUG"/><logger name="java.sql.Statement" level="DEBUG"/><logger name="java.sql.PreparedStatement" level="DEBUG"/><!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR --><root level="DEBUG"><appender-ref ref="STDOUT"/><appender-ref ref="FILE"/></root></configuration>
  • 再次执行单元测试方法testInsertCar,查看控制台是否有sql语句输出
    • E75C0BCA-F8D3-44f8-AABB-282510093C8C.png

2.8 MyBatis工具类SqlSessionUtil的封装

  • 每一次获取SqlSession对象代码太繁琐,封装一个工具类
package com.powernode.mybatis.utils;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;/*** MyBatis工具类** @author 老杜* @version 1.0* @since 1.0*/
public class SqlSessionUtil {private static SqlSessionFactory sqlSessionFactory;/*** 类加载时初始化sqlSessionFactory对象*/static {try {SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));} catch (Exception e) {e.printStackTrace();}}/*** 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。** @return 新的会话对象*/public static SqlSession openSession() {return sqlSessionFactory.openSession(true);}
}
  • 测试工具类,将testInsertCar()改造
@Test
public void testInsertCar(){SqlSession sqlSession = SqlSessionUtil.openSession();// 执行SQLint count = sqlSession.insert("insertCar");System.out.println("插入了几条记录:" + count);sqlSession.close();
}

三、使用MyBatis完成CRUD

  • 准备工作
    • 创建module(Maven的普通Java模块):mybatis-002-crud
    • pom.xml
      • 打包方式jar
      • 依赖:
        • mybatis依赖
        • mysql驱动依赖
        • junit依赖
        • logback依赖
    • mybatis-config.xml放在类的根路径下
    • CarMapper.xml放在类的根路径下
    • logback.xml放在类的根路径下
    • 提供com.powernode.mybatis.utils.SqlSessionUtil工具类
    • 创建测试用例:com.powernode.mybatis.CarMapperTest

3.1 insert(Create)

分析以下SQL映射文件中SQL语句存在的问题

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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语句中的值不应该写死,值应该是用户提供的。之前的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中的 ? ,#{}就是占位符
Java程序这样写:

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

SQL语句这样写:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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(#{k1},#{k2},#{k3},#{k4},#{k5})</insert>
</mapper>

#{} 的里面必须填写map集合的key,不能随便写运行测试程序,查看数据库:2DD856C0-9725-4073-98C4-129D86B03B35.png

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="car"><insert id="insertCar">insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{kk},#{k2},#{k3},#{k4},#{k5})</insert>
</mapper>

运行程序:13079E0B-F436-4100-B4E1-BB5C3A58ADD9.png
0E5B4E84-C76C-4724-87E5-D37C954D2C40.png

通过测试,看到程序并没有报错。正常执行。不过 #{kk} 的写法导致无法获取到map集合中的数据,最终导致数据库表car_num插入了NULL。
在以上sql语句中,可以看到#{k1} #{k2} #{k3} #{k4} #{k5}的可读性太差,为了增强可读性,我们可以将Java程序做如下修改:

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

SQL语句做如下修改,这样可以增强程序的可读性:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="car"><insert id="insertCar">insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})</insert>
</mapper>

运行程序,查看数据库表:B84BC4D9-24C1-4310-A5D1-DFC407BBFA8C.png

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

  • 第一步:定义一个pojo类Car,提供相关属性。
package com.powernode.mybatis.pojo;/*** POJOs,简单普通的Java对象。封装数据用的。* @author 老杜* @version 1.0* @since 1.0*/
public class Car {private Long id;private String carNum;private String brand;private Double guidePrice;private String produceTime;private String carType;@Overridepublic String toString() {return "Car{" +"id=" + id +", carNum='" + carNum + '\'' +", brand='" + brand + '\'' +", guidePrice=" + guidePrice +", produceTime='" + produceTime + '\'' +", carType='" + carType + '\'' +'}';}public Car() {}public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {this.id = id;this.carNum = carNum;this.brand = brand;this.guidePrice = guidePrice;this.produceTime = produceTime;this.carType = carType;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getCarNum() {return carNum;}public void setCarNum(String carNum) {this.carNum = carNum;}public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public Double getGuidePrice() {return guidePrice;}public void setGuidePrice(Double guidePrice) {this.guidePrice = guidePrice;}public String getProduceTime() {return produceTime;}public void setProduceTime(String produceTime) {this.produceTime = produceTime;}public String getCarType() {return carType;}public void setCarType(String carType) {this.carType = carType;}
}
  • 第二步:Java程序
@Test
public void testInsertCarByPOJO(){// 创建POJO,封装数据Car car = new Car();car.setCarNum("103");car.setBrand("奔驰C200");car.setGuidePrice(33.23);car.setProduceTime("2020-10-11");car.setCarType("燃油车");// 获取SqlSession对象SqlSession sqlSession = SqlSessionUtil.openSession();// 执行SQL,传数据int count = sqlSession.insert("insertCarByPOJO", car);System.out.println("插入了几条记录" + count);
}
  • 第三步:SQL语句
<insert id="insertCarByPOJO"><!--#{} 里写的是POJO的属性名-->insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
  • 运行程序,查看数据库表:
    9C093F8D-3868-435d-AA67-91B779ADF7BE.png

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

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

运行程序,出现了以下异常:BDD7D72C-B8AD-46b0-9006-9671AE65B305.png

错误信息中描述:在Car类中没有找到a属性的getter方法。
修改POJO类Car的代码,只将getCarNum()方法名修改为getA(),其他代码不变,如下:2BD31517-18A1-40b0-ABB2-066E930366AE.png

再运行程序,查看数据库表中数据:17908172-C596-4d31-B3B8-09B6DDDD5C82.png

经过测试得出结论:
如果采用map集合传参,#{} 里写的是map集合的key,如果key不存在不会报错,数据库表中会插入NULL。
如果采用POJO传参,#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get方法不存在会报错。
注意:其实传参数的时候有一个属性parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的

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

3.2 delete(Delete)

需求:根据car_num进行删除。
SQL语句这样写:

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

Java程序这样写:

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

运行结果:2ECBA22E-ABC9-4dc1-87AD-F4842083A2CD.png

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

3.3 update(Update)

需求:修改id=34的Car信息,car_num为102,brand为比亚迪汉,guide_price为30.23,produce_time为2018-09-10,car_type为电车
修改前:25135D96-3CD7-43f9-9E0F-7C90BCEDF28E.png

SQL语句如下:

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

Java代码如下:

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

运行结果:0670B92C-E654-49a7-839C-814850A4D79A.png7EC1586E-DB9E-47ca-A556-B8822A8F7429.png

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

3.4 select(Retrieve)

select语句和其它语句不同的是:查询会有一个结果集。来看mybatis是怎么处理结果集的!!!

查询一条数据

需求:查询id为1的Car信息
SQL语句如下:

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

Java程序如下:

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

运行结果如下:

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

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

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

运行结果:5006A305-0033-4c71-91F4-23B7CA955AC2.png

运行后之前的异常不再出现了,这说明添加了resultType属性之后,解决了之前的异常,可以看出resultType是不能省略的。
仔细观察控制台的日志信息,不难看出,结果查询出了一条。并且每个字段都查询到值了:Row: 1, 100, 宝马520Li, 41.00, 2022-09-01, 燃油车
但是奇怪的是返回的Car对象,只有id和brand两个属性有值,其它属性的值都是null,这是为什么呢?我们来观察一下查询结果列名和Car类的属性名是否能一一对应:
查询结果集的列名:id, car_num, brand, guide_price, produce_time, car_type
Car类的属性名:id, carNum, brand, guidePrice, produceTime, carType
通过观察发现:只有id和brand是一致的,其他字段名和属性名对应不上,这是不是导致null的原因呢?我们尝试在sql语句中使用as关键字来给查询结果列名起别名试试:

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

运行结果如下:C08C9F72-CD0B-4848-AD33-D0DAC7078FDF.png

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

查询多条数据

需求:查询所有的Car信息。
SQL语句如下:

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

Java代码如下:

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

运行结果如下:E1B82451-6452-4e93-BD75-DC6D8B9ADD14.png

3.5 关于SQL Mapper的namespace

在SQL Mapper配置文件中标签的namespace属性可以翻译为命名空间,这个命名空间主要是为了防止sqlId冲突的。
创建CarMapper2.xml文件,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="car2"><select id="selectCarAll" resultType="com.powernode.mybatis.pojo.Car">selectid, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carTypefromt_car</select>
</mapper>

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

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

编写Java代码如下:

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

运行结果如下:

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

Java代码修改如下:

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

运行结果如下:C1ED1184-132C-4393-918B-54D9645FE4B8.png

四、MyBatis核心配置文件详解

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

4.1 environment

mybatis-003-configuration

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--默认使用开发环境--><!--<environments default="dev">--><!--默认使用生产环境--><environments default="production"><!--开发环境--><environment id="dev"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/powernode"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment><!--生产环境--><environment id="production"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="CarMapper.xml"/></mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="car"><insert id="insertCar">insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})</insert>
</mapper>
package com.powernode.mybatis;import com.powernode.mybatis.pojo.Car;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;public class ConfigurationTest {@Testpublic void testEnvironment() throws Exception{// 准备数据Car car = new Car();car.setCarNum("133");car.setBrand("丰田霸道");car.setGuidePrice(50.3);car.setProduceTime("2020-01-10");car.setCarType("燃油车");// 一个数据库对应一个SqlSessionFactory对象// 两个数据库对应两个SqlSessionFactory对象,以此类推SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 使用默认数据库SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession(true);int count = sqlSession.insert("insertCar", car);System.out.println("插入了几条记录:" + count);// 使用指定数据库SqlSessionFactory sqlSessionFactory1 = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "dev");SqlSession sqlSession1 = sqlSessionFactory1.openSession(true);int count1 = sqlSession1.insert("insertCar", car);System.out.println("插入了几条记录:" + count1);}
}

执行结果:FF0CD72D-D26C-4ccb-9202-593CDA8C53D8.png
B8C1BB12-6CB0-4f08-A96F-560B6268C8F8.png

4.2 transactionManager

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="dev"><environment id="dev"><transactionManager type="MANAGED"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/powernode"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="CarMapper.xml"/></mappers>
</configuration>
@Test
public void testTransactionManager() throws Exception{// 准备数据Car car = new Car();car.setCarNum("133");car.setBrand("丰田霸道");car.setGuidePrice(50.3);car.setProduceTime("2020-01-10");car.setCarType("燃油车");// 获取SqlSessionFactory对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config2.xml"));// 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();// 执行SQLint count = sqlSession.insert("insertCar", car);System.out.println("插入了几条记录:" + count);
}

当事务管理器是:JDBC

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

当事务管理器是:MANAGED

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

4.3 dataSource

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="dev"><environment id="dev"><transactionManager type="JDBC"/><dataSource type="UNPOOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/powernode"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="CarMapper.xml"/></mappers>
</configuration>
@Test
public void testDataSource() throws Exception{// 准备数据Car car = new Car();car.setCarNum("133");car.setBrand("丰田霸道");car.setGuidePrice(50.3);car.setProduceTime("2020-01-10");car.setCarType("燃油车");// 获取SqlSessionFactory对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config3.xml"));// 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession(true);// 执行SQLint count = sqlSession.insert("insertCar", car);System.out.println("插入了几条记录:" + count);// 关闭会话sqlSession.close();
}

当type是UNPOOLED,控制台输出:
D58DF616-529B-41c7-8C0F-8A931A380497.png
修改配置文件mybatis-config3.xml中的配置:

<dataSource type="POOLED">

Java测试程序不需要修改,直接执行,看控制台输出:8DFDAF99-AA22-41cd-99C7-227A4667A0EB.png

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

<dataSource type="JNDI">

JNDI的方式:表示对接JNDI服务器中的连接池。这种方式给了我们可以使用第三方连接池的接口。如果想使用dbcp、c3p0、druid(德鲁伊)等,需要使用这种方式。
这种再重点说一下type="POOLED"的时候,它的属性有哪些?

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

poolMaximumActiveConnections:最大的活动的连接数量。默认值10
poolMaximumIdleConnections:最大的空闲连接数量。默认值5
poolMaximumCheckoutTime:强行回归池的时间。默认值20秒。
poolTimeToWait:当无法获取到空闲连接时,每隔20秒打印一次日志,避免因代码配置有误,导致傻等。(时长是可以配置的)
当然,还有其他属性。对于连接池来说,以上几个属性比较重要。
最大的活动的连接数量就是连接池连接数量的上限。默认值10,如果有10个请求正在使用这10个连接,第11个请求只能等待空闲连接。
最大的空闲连接数量。默认值5,如何已经有了5个空闲连接,当第6个连接要空闲下来的时候,连接池会选择关闭该连接对象。来减少数据库的开销。
需要根据系统的并发情况,来合理调整连接池最大连接数以及最多空闲数量。充分发挥数据库连接池的性能。【可以根据实际情况进行测试,然后调整一个合理的数量。】
下图是默认配置:9970FDF3-6A6A-4530-84A3-B72FE189124E.png

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

@Test
public void testPool() throws Exception{SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config3.xml"));for (int i = 0; i < 4; i++) {SqlSession sqlSession = sqlSessionFactory.openSession();Object selectCarByCarNum = sqlSession.selectOne("selectCarByCarNum");}
}
<select id="selectCarByCarNum" resultType="com.powernode.mybatis.pojo.Car">select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where car_num = '100'
</select>

E2C814E9-489D-4f4a-94F6-2ED5F6E56F54.png

4.4 properties

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

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

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--引入外部属性资源文件--><properties resource="jdbc.properties"><property name="jdbc.username" value="root"/><property name="jdbc.password" value="root"/></properties><environments default="dev"><environment id="dev"><transactionManager type="JDBC"/><dataSource type="POOLED"><!--${key}使用--><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><mappers><mapper resource="CarMapper.xml"/></mappers>
</configuration>

编写Java程序进行测试:

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

properties两个属性:
resource:这个属性从类的根路径下开始加载。【常用的。】
url:从指定的url加载,假设文件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。
注意:如果不知道mybatis-config.xml文件中标签的编写顺序的话,可以有两种方式知道它的顺序:

  • 第一种方式:查看dtd约束文件。
  • 第二种方式:通过idea的报错提示信息。【一般采用这种方式】

4.5 mapper

mapper标签用来指定SQL映射文件的路径,包含多种指定方式,这里先主要看其中两种:
第一种:resource,从类的根路径下开始加载【比url常用】

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

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

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

第二种:url,从指定的url位置加载
假设CarMapper.xml文件放在d盘的根下,这个配置就需要这样写:

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

mapper还有其他的指定方式,后面再看!!!


http://www.ppmy.cn/ops/136690.html

相关文章

HBU算法设计与分析 贪心算法

1.最优会场调度 #include <bits/stdc.h> using namespace std; const int N1e55; typedef pair<int,int> PII; PII p[N]; priority_queue<int,vector<int>,greater<int>> q; //最小堆 存储最早结束的会场的结束时间 int n; //其实这个题可以理…

OpenAI震撼发布:桌面版ChatGPT,Windows macOS双平台AI编程体验!

【雪球导读】 「OpenAI推出ChatGPT桌面端」 OpenAI重磅推出ChatGPT桌面端&#xff0c;全面支持Windows和macOS系统&#xff01;这款新工具为用户在日常生活和工作中提供了前所未有的无缝交互体验。对于那些依赖桌面端进行开发工作的专业人士来说&#xff0c;这一更新带来了令人…

iOS屏幕共享技术实践

一、 背景 iOS应用中实现屏幕共享功能&#xff0c;允许用户在视频通话或互动直播中将屏幕内容以视频的方式分享给其他的观众&#xff0c;以增强互动体验&#xff0c;提高沟通效率。这种功能在视频会议、在线教育和游戏直播等场景中非常有用。 视频会议场景中&#xff0c;屏幕共…

网络安全的学习方向和路线是怎么样的?

最近有同学问我&#xff0c;网络安全的学习路线是怎么样的&#xff1f; 废话不多说&#xff0c;先上一张图镇楼&#xff0c;看看网络安全有哪些方向&#xff0c;它们之间有什么关系和区别&#xff0c;各自需要学习哪些东西。 在这个圈子技术门类中&#xff0c;工作岗位主要有以…

mysql约束和高级sql

约束 MySQL中的约束用于定义表中数据的规则&#xff0c;以确保数据的准确性和可靠性。以下是MySQL中常用的一些约束类型及其概述&#xff1a; PRIMARY KEY&#xff08;主键&#xff09;&#xff1a;唯一标识表中每条记录的字段或字段组合&#xff0c;一个表中只能有一个主键。…

深度学习中的长短期记忆网络(LSTM)与自然语言处理

一、LSTM简介 长短期记忆网络&#xff08;Long Short-Term Memory&#xff0c;简称LSTM&#xff09;是一种特殊的循环神经网络&#xff08;RNN&#xff09;&#xff0c;专门用于解决标准RNN在处理长期依赖问题时容易出现的梯度消失或梯度爆炸等问题。 LSTM的核心结构&#xf…

第三百二十九节 Java网络教程 - Java网络UDP套接字

Java网络教程 - Java网络UDP套接字 TCP套接字是面向连接的&#xff0c;基于流。基于UDP的套接字是无连接的&#xff0c;基于数据报。 使用UDP发送的数据块称为数据报或UDP数据包。每个UDP分组具有数据&#xff0c;目的地IP地址和目的地端口号。 无连接套接字在通信之前不建立…

【Github】如何使用Git将本地项目上传到Github

【Github】如何使用Git将本地项目上传到Github 写在最前面1. 注册Github账号2. 安装Git工具配置用户名和邮箱仅为当前项目配置&#xff08;可选&#xff09; 3. 创建Github仓库4. 获取仓库地址5. 本地操作&#xff08;1&#xff09;进入项目文件夹&#xff08;2&#xff09;克隆…