mybatis学习(一)

声明:该内容来源于动力节点,本人在学习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.mzph.cn/news/887331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C0034.在Ubuntu中安装的Qt路径

Qt安装路径查询 在终端输入qmake -v如上中/usr/lib/x86_64-linux-gnu就是Qt的安装目录&#xff1b;

线程控制方法之wait和sleep的区别

线程控制方法之wait和sleep的区别 wait()和sleep()都是Java线程控制方法&#xff0c;但存在明显区别&#xff1a; 所属与调用&#xff1a;wait()属Object类&#xff0c;需synchronized调用&#xff1b;sleep()属Thread类&#xff0c;可随意调用。锁处理&#xff1a;wait()释放…

Harbor2.11.1生成自签证和配置HTTPS访问

文章目录 HTTPS的工作流程部署Harbor可参考上一篇文章生成自签证书1.修改/etc/hosts文件2.生成证书a.创建存放证书路径b.创建ca.key密钥c.创建ca.crtd.创建给Harbor服务器使用密钥 yunzhidong.harbor.com.keye.创建给Harbor服务器使用证书签名请求文件 yunzhidong.harbor.com.c…

Spring Boot3远程调用工具RestClient

Spring Boot3.2之后web模块提供了一个新的远程调用工具RestClient&#xff0c;它的使用比RestTemplate方便&#xff0c;开箱即用&#xff0c;不需要单独注入到容器之中&#xff0c;友好的rest风格调用。下面简单的介绍一下该工具的使用。 一、写几个rest风格测试接口 RestCont…

svn 崩溃、 cleanup失败 怎么办

在使用svn的过程中&#xff0c;可能出现整个svn崩溃&#xff0c; 例如cleanup 失败的情况&#xff0c;类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…

[Leetcode小记] 3233. 统计不是特殊数字的数字数量

代码&#xff1a; 方法一&#xff1a;平凡解法(最直观但时间复杂度最高) class Solution {public int nonSpecialCount(int l, int r) {int resr-l1;//初始不是特殊数字的答案为[l,r]范围内数字总数for(int i(int)Math.ceil(Math.sqrt(l));i<(int)Math.floor(Math.sqrt(r))…

jenkins 2.346.1最后一个支持java8的版本搭建

1.jenkins下载 下载地址&#xff1a;Index of /war-stable/2.346.1 2.部署 创建目标文件夹&#xff0c;移动到指定位置 创建一个启动脚本&#xff0c;deploy.sh #!/bin/bash set -eDATE$(date %Y%m%d%H%M) # 基础路径 BASE_PATH/opt/projects/jenkins # 服务名称。同时约定部…

package.json中^1.x.x、~1.x.x、1.x.x有什么区别

目录 包版本号的语义化 包版本号的符号 举例 包版本号的语义化 在开始回答这个问题之前&#xff0c;先简单介绍一下包版本号的语义化。 在npm中&#xff0c;包的版本号通常遵循语义化版本规范&#xff08;Semantic Versioning&#xff09;&#xff0c;即采用 major.minor.p…

el-table :span-method 合并单元格(2.0)

2024.11.23今天我学习了如何使用el-table组件的合并单元格方法&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; <template><div class"container"><el-table :data"table_data" :span-method"object_merge" border>&…

CWT-CNN-SABO-LSSVM | Matlab实现基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断

CWT-CNN-SABO-LSSVM | Matlab实现基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断 目录 CWT-CNN-SABO-LSSVM | Matlab实现基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断分类效果基本描述程序设计参考资料 分类效果 基本描述 基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断 matlab代码…

Java基于SSM框架的校园综合服务小程序【附源码、文档】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

WPF——ICON按钮制作

前言 首先ICON按钮&#xff0c;即带图标按钮&#xff0c;即图标按钮。 图标按钮在开发时&#xff0c;主要是有两种方式来进行。一是在Button的Content内添加Image&#xff0c;然后设置Image的属性Source来实现&#xff0c;这种方式主要是简单易操作&#xff0c;对于初学者来说…

LlamaIndex ollama 搭建本地RAG应用,建立本地知识库

目录 简介安装前的准备下载ollama创建llamaindex conda环境&#xff0c;为后面编码作准备 环境变量迁移ollama到其他盘运行ollama方式一方式二禁止ollama开机自启动运行第一个模型 Chatbox聊天下载Chatbox配置ollama地址和模型验证 建立自身特定知识数据搭配大语言模型创建项目…

[RabbitMQ] 重试机制+TTL+死信队列

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

ChatGPT 桌面版发布了,如何安装?

本章教程教大家如何进行安装。 一、下载安装包 官网地址地址&#xff1a;https://openai.com/chatgpt/desktop/ 支持Windows和MacOS操作系统 二、安装步骤 Windows用户下载之后&#xff0c;会有一个exe安装包&#xff0c;点击运行安装即可。 注意事项&#xff0c;如果Windows操…

uniapp vue2项目迁移vue3项目

uniapp vue2项目迁移vue3项目&#xff0c;必须适配的部分 一、main.js 创建应用实例 // 之前 - Vue 2 import Vue from vue import App from ./App Vue.config.productionTip false // vue3 不再需要 App.mpType app // vue3 不再需要 const app new Vue({ ...App }) …

Brain.js 用于浏览器的 GPU 加速神经网络

Brain.js 是一个强大的 JavaScript 库&#xff0c;它允许开发者在浏览器和 Node.js 环境中构建和训练神经网络 。这个库的目的是简化机器学习模型的集成过程&#xff0c;使得即使是没有深厚机器学习背景的开发者也能快速上手 。 概述 Brain.js 提供了易于使用的 API&#xff…

群核科技首次公开“双核技术引擎”,发布多模态CAD大模型

11月20日&#xff0c;群核科技在杭州举办了第九届酷科技峰会。现场&#xff0c;群核科技首次正式介绍其技术底层核心&#xff1a;基于GPU高性能计算的物理世界模拟器。并对外公开了两大技术引擎&#xff1a;群核启真&#xff08;渲染&#xff09;引擎和群核矩阵&#xff08;CAD…

oracle会话追踪

一 跟踪当前会话 1.1 查看当前会话的SID,SERIAL# #在当前会话里执行 示例&#xff1a; SQL> select distinct userenv(sid) from v$mystat; USERENV(SID) -------------- 1945 SQL> select distinct sid,serial# from v$session where sid1945; SID SERIAL# …

算法-快速排序-Python版详解

原题如下&#xff1a; 给定你一个长度为 n 的整数数列。请你使用快速排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。 输入格式 输入共两行&#xff0c;第一行包含整数 n。 第二行包含 n 个整数&#xff08;所有整数均在 1∼10^9 范围内&#xff09;&am…