[Java]Mybatis学习笔记
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
- 🥽 课件&资料
- 🥽 Mybatis下载
- 🥽 了解Mybatis
- 🥽 Mybatis入门程序
- 🥽 使用MyBatis完成CRUD
- 🥽 MyBatis核心配置文件详解
- 🥽 手写MyBatis框架
- 🥽🌊💦💧
🥽 课件&资料
课件老杜MyBatis–原版:【https://www.yuque.com/zuihoudewu/java_note/mt2812】
资料【https://pan.baidu.com/s/1SsN1Zbs-VrJBqlLbvL5k7g?pwd=1234 】
提取码1234
🥽 Mybatis下载
【Mybatis github地址https://github.com/mybatis/mybatis-3】
【Mybatis中文使用手册Mybatis中文网 https://mybatis.net.cn/】
🥽 了解Mybatis
- MyBatis本质上就是对JDBC的封装通过MyBatis完成CRUD。
- MyBatis在三层架构中负责持久层的属于持久层框架。
🌊 ORM思想
- ORM对象关系映射
- OObjectJava虚拟机中的Java对象
- RRelational关系型数据库
- MMapping映射将Java虚拟机中的Java对象映射到数据库表中⼀⾏记录或是将数据库表中⼀⾏记录映射成Java虚拟机中的⼀个Java对象。
- 在ORM思想中一个Java类对应数据库中一个表该Java类实例化出来的对象对应数据库表中的一条记录其中Java对象到数据库表中每条记录的对应关系为映射。
- 在ORM中数据库表对应的Java类被称为pojo(普通Java类)、javabean(咖啡豆)或domain(邻域模型)。
🌊 Mybatis与ORM
- Mybatis可以实现Java对象与数据库表中一条记录的映射。
- MyBatis属于半⾃动化ORM框架。MyBatis中SQL语句需要我们自己编写。
- Hibernate属于全⾃动化的ORM框架。Hibernate中SQL语句不需要我们自己编写SQL语句可以自动生成。
🌊 MyBatis框架特点
- ⽀持定制化 SQL、存储过程、基本映射以及⾼级映射
- 避免了⼏乎所有的 JDBC 代码中⼿动设置参数以及获取结果集
- ⽀持XML开发也⽀持注解式开发。【为了保证sql语句的灵活所以mybatis⼤部分是采⽤XML⽅式开发。】
- 将接⼝和 Java 的 POJOs(Plain Ordinary Java Object简单普通的Java对象)映射成数据库中的记录
- 体积⼩好学两个jar包两个XML配置⽂件。
- 完全做到sql解耦合。
- 提供了基本映射标签。
- 提供了⾼级映射标签。
- 提供了XML标签⽀持动态SQL的编写。
- …
🥽 Mybatis入门程序
🌊 数据库表的准备
- 准备数据库表汽⻋表t_car字段包括
- 数据库中字段名小写单词之间使用下划线分割
- id主键⾃增【bigint】
- car_num汽⻋编号【varchar】
- brand品牌【varchar】
- guide_price⼚家指导价【decimal类型专⻔为财务数据准备的类型】
- produce_time⽣产时间【char年⽉⽇即可10个⻓度‘2022-10-11’】
- car_type汽⻋类型燃油⻋、电⻋、氢能源【varchar】
# 创建数据库
CREATE DATABASE mybatis_study;
# 使用数据库
USE mybatis_study;
# 创建表 汽⻋表t_car
CREATE TABLE t_car(
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '自然主键',
car_num VARCHAR(255) COMMENT '汽车编号',
brand VARCHAR(255) COMMENT '汽车品牌',
guide_price DECIMAL(10, 2) COMMENT '厂家指导价',
produce_time CHAR(10) COMMENT '生产时间 如2022-10-11',
car_type VARCHAR(255) COMMENT '汽车类型'
);
# 添加数据
INSERT INTO t_car(car_num, brand, guide_price, produce_time, car_type)
VALUES ('1001', '宝马520Li', 10.00, '2022-10-11', '燃油车'),
('1002', '奔驰E300L', 55.00, '2022-11-11', '新能源');
🌊 创建Project
建议创建Empty Project设置Java版本以及编译版本等。
🌊 创建Module
普通的Maven Java模块
resources用于存放配置文件或资源文件放在该目录下的文件相当于放到了类的根路径下。
不管是maven还是普通的java模块,只要是idea文件夹中变成蓝色的部分,就是可以理解为类路径的起始地点(之后的编译就是由蓝色文件夹开始算类路径)
自定义文件夹后用idea标记为resource后的文件夹也会被最终打包到类路径下
🌊 设置打包⽅式
设置打包⽅式为jar不需要war因为mybatis封装的是jdbc。
<!-- 打包方式 -->
<packaging>jar</packaging>
🌊 引⼊依赖
mybatis依赖 + mysql驱动依赖
<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>
🌊 编写mybatis核心配置文件mybatis-config.xml
- 从 XML 中构建 SqlSessionFactory
- 通过官方的这句话你能想到什么呢
- 第一在MyBatis中一定是有一个很重要的对象这个对象是SqlSessionFactory对象。
- 第二SqlSessionFactory对象的创建需要XML。
- XML是什么
- 它一定是一个配置文件。
- 通过官方的这句话你能想到什么呢
- 注意
- 第一这个文件名不是必须叫做mybatis-config.xml可以用其他的名字。只是大家都采用这个名字。
- 第二这个文件存放的位置也不是固定的可以随意但一般情况下会放到类的根路径下。(打包后在classes的目录下)
- mybatis-config.xml文件中的配置信息不理解没关系先把连接数据库的信息修改以下即可。
- 其他的别动。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 修改链接数据库的配置信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/po
wernode"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--sql映射⽂件创建好之后需要将该⽂件路径配置到这⾥-->
<mapper resource=""/>
</mappers>
</configuration>
🌊 mybatis中有两个主要的配置文件
- 其中一个是mybatis-config.xml这是核心配置文件主要配置连接数据库的信息等。一个
- 另一个是XxxxMapper.xml这个文件是专门用来编写SQL语句的配置文件。一个表一个
- t_user表一般会对应一个UserMapper.xml
- t_student表一般会对应一个StudentMapper.xml
🌊 编写XxxxMapper.xml文件
- 在这个配置文件当中编写SQL语句。
- 这个文件名也不是固定的放的位置也不是固定我们这里给它起个名字叫做CarMapper.xml
- 把它暂时放到类的根路径下。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写⼀个-->
<mapper namespace="car">
<!--insert sql保存⼀个汽⻋信息-->
<!-- 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>
- 注意1sql语句最后结尾可以不写“;”
- 注意2CarMapper.xml⽂件的名字不是固定的。可以使⽤其它名字。
- 注意3CarMapper.xml⽂件的位置也是随意的。这⾥选择放在resources根下相当于放到了类的根路径下。
🌊 在mybatis-config.xml文件中指定XxxxMapper.xml文件的路径
<mapper resource="CarMapper.xml"/>
- 注意resource属性会自动从类的根路径下开始查找资源。
<mappers>
<!--sql映射⽂件创建好之后需要将该⽂件路径配置到这⾥-->
<!-- resource属性自动会从类的根路径下开始查找资源 -->
<mapper resource="CarMapper.xml"/>
</mappers>
🌊 编写MyBatis程序。
-
使用mybatis的类库编写mybatis程序连接数据库做增删改查就行了。
-
在MyBatis当中负责执行SQL语句的那个对象叫做什么呢
- SqlSession
- SqlSession是专门用来执行SQL语句的是一个Java程序和数据库之间的一次会话。
- 要想获取SqlSession对象需要先获取SqlSessionFactory对象通过SqlSessionFactory工厂来生产SqlSession对象。
- 怎么获取SqlSessionFactory对象呢
- 需要首先获取SqlSessionFactoryBuilder对象。
- 通过SqlSessionFactoryBuilder对象的build方法来获取一个SqlSessionFactory对象。
-
mybatis的核心对象包括
- SqlSessionFactoryBuilder
- SqlSessionFactory
- SqlSession
- SqlSessionFactoryBuilder --> SqlSessionFactory --> SqlSession
package cw.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 MybatisTest {
public static void main(String[] args) throws Exception{
// 获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory对象
InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // Resources.getResourceAsStream默认就是从类的根路径下开始查找资源。
//InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); // 需要传入核心配置文件对应的输入流一般情况下都是一个数据库对应一个SqlSessionFactory对象。
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL语句
int count = sqlSession.insert("insertCar"); // 传入SQL语句对应的id返回值是影响数据库表当中的记录条数。
System.out.println("插入了几条记录" + count);
// 手动提交
sqlSession.commit();
}
}
🌊 关于第一个程序的小细节
- mybatis中sql语句的结尾";"可以省略。
Resources.getResourceAsStream
- 小技巧以后凡是遇到resource这个单词大部分情况下这种加载资源的方式就是从类的根路径下开始加载。开始查找
- 优点采用这种方式从类路径当中加载资源项目的移植性很强。项目从windows移植到linux代码不需要修改因为这个资源文件一直都在类路径当中。
InputStream is = new FileInputStream("d:\\mybatis-config.xml");
采用这种方式也可以。- 缺点可移植性太差程序不够健壮。可能会移植到其他的操作系统当中。导致以上路径无效还需要修改java代码中的路径。这样违背了OCP原则。
- 已经验证了
- mybatis核心配置文件的名字不一定是mybatis-config.xml。可以是其它名字。
- mybatis核心配置文件存放的路径也不一定是在类的根路径下。可以放到其它位置。但为了项目的移植性健壮性最好将这个配置文件放到类路径下面。
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
- ClassLoader.getSystemClassLoader() 获取系统的类加载器。
- 系统类加载器有一个方法叫做getResourceAsStream它就是从类路径当中加载资源的。
- 通过源代码分析发现
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
底层的源代码其实就是InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
- CarMapper.xml文件的名字是固定的吗CarMapper.xml文件的路径是固定的吗
- 都不是固定的。
<mapper resource="CarMapper.xml"/>
resource属性这种方式是从类路径当中加载资源。<mapper url="file:///d:/CarMapper.xml"/>
url属性这种方式是从绝对路径当中加载资源。绝对路径前需要加上file:///
🌊 关于mybatis的事务管理机制。深度剖析
- 在mybatis-config.xml文件中可以通过以下的配置进行mybatis的事务管理
<transactionManager type="JDBC"/>
- type属性的值包括两个
- JDBC(jdbc)
- MANAGED(managed)
- type后面的值只有以上两个值可选不区分大小写。
- 在mybatis中提供了两种事务管理机制
- 第一种JDBC事务管理器
- 第二种MANAGED事务管理器
- JDBC事务管理器
- mybatis框架自己管理事务自己采用原生的JDBC代码去管理事务
- conn.setAutoCommit(false); 开启事务。
- …业务处理…
- conn.commit(); 手动提交事务
// 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 如果使用的事务管理器是JDBC的话底层实际上会执行conn.setAutoCommit(false); // 执行SQL语句 int count = sqlSession.insert("insertCar"); // 返回值是影响数据库表当中的记录条数。 System.out.println("插入了几条记录" + count); // 手动提交 sqlSession.commit(); // 如果使用的事务管理器是JDBC的话底层实际上还是会执行conn.commit();
- 使用JDBC事务管理器的话底层创建的事务管理器对象JdbcTransaction对象。
- 如果你编写的代码是下面的代码
SqlSession sqlSession = sqlSessionFactory.openSession(true);
表示没有开启事务。因为这种方式压根不会执行conn.setAutoCommit(false);
- 在JDBC事务中没有
执行conn.setAutoCommit(false);
那么autoCommit就是true。如果autoCommit是true就表示没有开启事务(事务自动提交)。只要执行任意一条DML语句就提交一次。
- mybatis框架自己管理事务自己采用原生的JDBC代码去管理事务
- MANAGED事务管理器
- mybatis不再负责事务的管理了。事务管理交给其它容器来负责。例如spring。我不管事务了你来负责吧。
- 对于我们当前的单纯的只有mybatis的情况下如果配置为MANAGED那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。
- 没有人管理事务就是没有事务。不开启事务autoCommit是true就表示没有事务。只要执行任意一条DML语句就提交一次。
- JDBC中的事务
- 如果你没有在JDBC代码中执行
conn.setAutoCommit(false);
的话默认的autoCommit是true。
- 如果你没有在JDBC代码中执行
- 重点
- 以后注意了只要你的autoCommit是true就表示没有开启事务(事务自动提交)。
- 只有你的autoCommit是false的时候就表示开启了事务。
🌊 MyBatis第⼀个⽐较完整的代码写法
package com.powernode.mybatis.test;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisCompleteTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 开启会话底层会开启事务
sqlSession = sqlSessionFactory.openSession();
// 执行SQL语句处理相关业务
int count = sqlSession.insert("insertCar");
System.out.println(count);
// 执行到这里没有发生任何异常提交事务。终止事务。
sqlSession.commit();
} catch (Exception e) {
// 最好回滚事务
if (sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
} finally {
// 关闭会话释放资源
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
🌊 引⼊JUnit
- JUnit是专⻔做单元测试的组件。
- 在实际开发中单元测试⼀般是由我们Java程序员来完成的。
- 我们要对我们⾃⼰写的每⼀个业务⽅法负责任要保证每个业务⽅法在进⾏测试的时候都能通过。
- 测试的过程中涉及到两个概念
- 期望值执行了这个业务方法之后你期望的执行结果是多少
- 实际值被测试的业务方法的真正执行结果
- 期望值和实际值相同表示测试通过期望值和实际值不同则单元测试执⾏时会报错。
- 引⼊JUnit是为了代替main⽅法。
💦 JUnit的使用
第⼀步引⼊依赖
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
第⼆步编写单元测试类【测试⽤例】测试⽤例中每⼀个测试⽅法上使⽤@Test注解进⾏标注
package com.powernode.junit.service;
import org.junit.Assert;
import org.junit.Test;
public class MathServiceTest { // 名字规范你要测试的类名+Test
// 单元测试方法写多少个。
// 一般是一个业务方法对应一个测试方式。
// 测试方法的规范: public void testXxxx(){}
// 测试方法的方法名以test开始。假设测试的方法是sum这个测试方法名testSum
// @Test注解非常重要被这个注解标注的方法就是一个单元测试方法。
@Test
public void testSum(){
// 单元测试中有两个重要的概念
// 一个是实际值被测试的业务方法的真正执行结果
// 一个是期望值执行了这个业务方法之后你期望的执行结果是多少
MathService mathService = new MathService();
// 获取实际值
int actual = mathService.sum(1, 2);
// 期望值
//int expected = 3;
int expected = 30;
// 加断言进行测试
Assert.assertEquals(expected, actual);
}
@Test
public void testSub(){
MathService mathService = new MathService();
// 实际值
int actual = mathService.sub(10, 5);
// 期望值
int expected = 5;
// 添加断言机制
Assert.assertEquals(expected, actual);
}
}
🌊 关于mybatis集成日志组件。
- mybatis常见的集成的日志组件有哪些呢
- SLF4J沙拉风沙拉风是一个日志标准其中有一个框架叫做logback它实现了沙拉风规范。
- LOG4J
- LOG4J2
- STDOUT_LOGGING
- …
- 注意log4j log4j2 logback都是同一个作者开发的。
- 其中STDOUT_LOGGING是标准日志mybatis已经实现了这种标准日志。mybatis框架本身已经实现了这种标准。只要开启即可。
- 怎么开启呢在mybatis-config.xml文件中使用settings标签进行配置开启。(可参考mybatis⼿册)
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
- 这个标签在编写的时候要注意它应该出现在environments标签之前。注意顺序。
- 当然不需要记忆这个顺序。因为有dtd文件进行约束呢。我们只要参考dtd约束即可。
- 这种实现也是可以的可以看到一些信息比如连接对象什么时候创建什么时候关闭sql语句是怎样的。但是没有详细的日期线程名字等。如果你想使用更加丰富的配置可以集成第三方的log组件。
- 怎么开启呢在mybatis-config.xml文件中使用settings标签进行配置开启。(可参考mybatis⼿册)
💦 集成logback日志框架。
logback日志框架实现了slf4j标准。(沙拉风日志门面。日志标准。)
使用标准日志STDOUT_LOGGING之外的其他日志框架可以不在mybatis的配置文件中指定 MyBatis 所用日志的具体实现未指定时将会自动查找只需要引入相应日志框架的依赖即可。
第一步引入logback的依赖。
<!-- 引入logback依赖logback日志框架实现了slf4j规范 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
第二步引入logback所必须的xml配置文件。
这个配置文件的名字必须叫做logback.xml或者logback-test.xml不能是其它的名字。
这个配置文件必须放到类的根路径下。不能是其他位置。
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>[%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</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>
🌊 MyBatis⼯具类SqlSessionUtil的封装
package cw.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
/**
* Mybatis工具类
*/
public class SqlSessionUtils {
// 工具类的构造方法一般为私有防止实例化对象
// 工具类中的方法都是静态的可以直接采用类名进行调用不需要new对象方便调用
private SqlSessionUtils() {}
private static SqlSessionFactory sqlSessionFactory;
// 类加载时执行
// SqlSessionUtil工具类在进行第一次加载的时候解析mybatis-config.xml文件。创建SqlSessionFactory对象。
static {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// SqlSessionFactory对象一个SqlSessionFactory对应一个environment一个environment通常是一个数据库。
try {
sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取数据库会话对象
* @return 数据库会话对象
*/
public static SqlSession openSession() {
// SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// SqlSessionFactory对象一个SqlSessionFactory对应一个environment一个environment通常是一个数据库。
// SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
测试
package cw.mybatis.test;
import cw.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class SqlSessionUtilsTest {
@Test
public void testOpenSession() {
SqlSession sqlSession = SqlSessionUtils.openSession();
int count = sqlSession.insert("insertCar");
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
}
🥽 使用MyBatis完成CRUD
- CRUD
- C: Create增
- R: Retrieve查检索
- U: Update改
- D: Delete删
🌊 insert
<!--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>
- 这样写的问题是
- 值显然是写死到配置文件中的。
- 这个在实际开发中是不存在的。
- 一定是前端的form表单提交过来数据。然后将值传给sql语句。
JDBC的代码是怎么写的
String sql = "insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)";
ps.setString(1, xxx);
ps.setString(2, yyy);
....
- 在JDBC当中占位符采用的是?在mybatis当中是什么呢
- 和?等效的写法是#{}
- 在mybatis当中不能使用?占位符必须使用 #{} 来代替JDBC当中的 ?
- #{} 和 JDBC当中的 ? 是等效的。
<!--namespace先随便写-->
<mapper namespace="car">
<insert id="insertCar">
insert into t_car(car_num,brand,guide_price,produce_time,car_type)
values(#{}, #{}, #{}, #{}, #{})
</insert>
</mapper>
💦 使用Map集合传参
java程序中使用Map可以给SQL语句的占位符传值占位符中写map集合的key如果key不存在获取的是null一般map集合的key起名的时候要见名知意。
<mapper namespace="car">
<!--insert sql保存⼀个汽⻋信息-->
<insert id="insertCar">
insert into t_car
(id,car_num,brand,guide_price,produce_time,car_type)
values
(null, #{k1}, #{k2}, #{k3}, #{k4}, #{k5})
</insert>
</mapper>
package cw.mybatis;
import cw.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.HashMap;
import java.util.Map;
public class CarMapperTest {
@Test
public void testInsertCar() {
// 使用map集合进行数据的封装。
Map<String, Object> map = new HashMap<>();
map.put("k1", "1111");
map.put("k2", "比亚迪汉");
map.put("k3", 10.0);
map.put("k4", "2020-11-11");
map.put("k5", "电车");
SqlSession sqlSession = SqlSessionUtils.openSession();
// 执行SQL语句
// insert方法的参数
// 第一个参数sqlId从CarMapper.xml文件中复制。
// 第二个参数封装数据的对象。
sqlSession.insert("insertCar", map);
sqlSession.commit();
sqlSession.close();
}
}
💦 使用POJO(简单普通的java对象)传参
第一步定义一个pojo类Car提供相关属性
package cw.mybatis.pojo;
/**
* 封装汽车相关信息的pojo类。普通的java类。
*/
public class Car {
// 数据库表当中的字段应该和pojo类的属性一一对应。
// 建议使用包装类这样可以防止null的问题。
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String 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;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + carType + '\'' + '}';
}
}
第二步Java程序
@Test
public void testInsertCarPOJO() {
// 封装数据
Car car = new Car(null, "3333", "比亚迪", 30.0, "2020-11-11", "新能源");
SqlSession sqlSession = SqlSessionUtils.openSession();
// 执行sql
sqlSession.insert("insertCar", car);
sqlSession.commit();
sqlSession.close();
}
第三步SQL语句
<mapper namespace="car">
<!--insert sql保存⼀个汽⻋信息-->
<!-- 占位符 #{} 中写POJO的属性名 -->
<insert id="insertCar">
insert into t_car
(id,car_num,brand,guide_price,produce_time,car_type)
values
(null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
</insert>
</mapper>
- 把SQL语句写成这个德行
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{xyz},#{brand},#{guidePrice},#{produceTime},#{carType})
- 出现了什么问题呢
- There is no getter for property named ‘xyz’ in ‘class com.powernode.mybatis.pojo.Car’
- mybatis去找Car类中的getXyz()方法去了。没找到。报错了。
- 怎么解决的
- 可以在Car类中提供一个getXyz()方法。这样问题就解决了。
- 通过这个测试得出一个结论
- 严格意义上来说如果使用POJO对象传递值的话#{}这个大括号中到底写什么
- 写的是get方法的方法名去掉get然后将剩下的单词首字母小写然后放进去。
- 例如getUsername() --> #{username}
- 例如getEmail() --> #{email}
- …
- 也就是说mybatis在底层给?传值的时候先要获取值怎么获取的
- 调用了pojo对象的get方法。例如car.getCarNum()car.getCarType()car.getBrand()
🌊 delete
需求根据id删除数据
实现
SQL语句
<!-- 当sql语句中的占位符只有一个时占位符中的内容可以任意写但是不能不写一般采用见名知意的命名 -->
<!-- 只有一个占位符时mybatis可以知道数据填放的位置 -->
<delete id="deleteById">
delete from t_car where id = #{id}
</delete>
java程序
@Test
public void testDeleteById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 第二个参数会被自动装箱成相应的类型
sqlSession.delete("deleteById", 10);
sqlSession.commit();
sqlSession.close();
}
注意如果占位符只有一个那么#{}的大括号里可以随意。但是最好见名知意。
🌊 update
需求根据id修改某条记录。
<update id="updateById">
update t_car set
car_num = #{carNum}, brand = #{brand},
guide_price = #{guidePrice}, produce_time = #{produceTime},
car_type = #{carType}
where id = #{id}
</update>
@Test
public void testUpdateById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 封装数据
Car car = new Car(4L, "9999", "凯美瑞", 30.3, "1999-11-10", "燃油车");
int count = sqlSession.update("updateById", car);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
🌊 select
💦 查一条数据
需求根据id查询。
<!-- 查询时需要告诉mybatis查询结果集封装成的java对象类型 -->
<!-- select标签中resultType属性这个属性用来告诉mybatis查询结果集封装成什么类型的java对象。你需要告诉mybatis。 -->
<!-- resultType通常写的是全限定类名。 -->
<!-- sql语句中如果表的字段名与相应的java类的属性名不一致需要使用别名为查询语句的字段指定别名否则对应字段的查询为null -->
<select id="selectById" resultType="cw.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
where
id = #{id}
</select>
@Test
public void testSelectById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 执行DQL语句。查询。根据id查询。返回结果一定是一条。
// mybatis底层执行了select语句之后一定会返回一个结果集对象ResultSet
// JDBC中叫做ResultSet接下来就是mybatis应该从ResultSet中取出数据封装java对象。
Object car = sqlSession.selectOne("selectById", 1);
System.out.println(car);
sqlSession.close();
}
💦 查多条数据
<!-- resultType还是指定要封装的结果集的类型。不是指定List类型是指定List集合中元素的类型。 -->
<!-- selectList方法mybatis通过这个方法就可以得知你需要一个List集合。它会自动给你返回一个List集合。 -->
<select id="selectAll" resultType="cw.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
</select>
@Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
🌊 SQL Mapper的namespace
在sql mapper.xml文件当中有一个namespace,这个属性是用来指定命名空间的。用来防止id重复。
实际上本质上mybatis中的sqlId的完整写法namespace.id
创建CarMapper2.xml文件代码如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="car2">
<select id="selectCarAll" 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
</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 testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
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 testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
List<Car> cars = sqlSession.selectList("car.selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
🥽 MyBatis核心配置文件详解
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 文档类型说明中的 configuration是根标签的名称一个文档一个根标签 -->
<!--
http://mybatis.org/dtd/mybatis-3-config.dtd
xml文档的dtd约束约束文档中可以出现什么标签、标签能有什么子标签、标签中可以有什么属性以及标签出现的顺序
-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration根标签表示配置信息。 -->
<configuration>
<!--java.util.Properties类。是一个Map集合。key和value都是String类型-->
<!-- property标签中的name属性为keyvalue属性为value -->
<!--在properties标签中可以配置很多属性-->
<!-- 在下面的标签中可以使用property配置的属性值使用 ${property的name属性} 取出相应的值 -->
<!--<properties>-->
<!--这是其中的一个属性-->
<!--<property name="属性名" value="属性值"/>-->
<!--<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="root"/>-->
<!--</properties>-->
<!-- properties中的property可以配置到配置文件中 -->
<!--resource,一定是从类路径下开始查找资源-->
<properties resource="jdbc.properties" />
<!--从绝对路径当中加载资源。绝对路径怎么写file:///路径-->
<!--<properties url="file:///d:/jdbc.properties" />-->
<!-- environments环境多个以“s”结尾表示复数也就是说mybatis的环境可以配置多个数据源。 -->
<!--default表示默认使用的环境。-->
<!--默认环境什么意思当你使用mybatis创建SqlSessionFactory对象的时候没有指定环境的话默认使用哪个环境。-->
<environments default="powernodeDB">
<!--其中的一个环境。连接的数据库是powernode-->
<!--一般一个数据库会对应一个SqlSessionFactory对象。-->
<!--一个环境environment会对应一个SqlSessionFactory对象-->
<!-- 一个数据库对应一个环境 -->
<!--
// 这种方式就是获取的默认环境
SqlSessionFactory sqlSessionFactory =
sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 这种方式就是通过环境id来使用指定的环境第二个参数为环境id
SqlSessionFactory sqlSessionFactory1 =
sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "powernodeDB");
-->
<environment id="powernodeDB">
<!--
transactionManager标签
1.作用配置事务管理器。指定mybatis具体使用什么方式去管理事务。
2.type属性有两个值
第一个JDBC: 使用原生的JDBC代码来管理事务。
conn.setAutoCommit(false);
....
conn.commit();
第二个MANAGEDmybatis不再负责事务的管理将事务管理交给其它的JEE(JavaEE)容器来管理。例如spring
3. 大小写无所谓。不缺分大小写。但是不能写其他值。只能是二选一
jdbc、managed
4. 在mybatis中提供了一个事务管理器接口Transaction
该接口下有两个实现类
JdbcTransaction
ManagedTransaction
如果type="JDBC"那么底层会实例化JdbcTransaction对象。
如果type="MANAGED"那么底层会实例化ManagedTransaction
-->
<transactionManager type="JDBC"/>
<!--
dataSource配置
1.dataSource被称为数据源。
2.dataSource作用是什么为程序提供Connection对象。但凡是给程序提供Connection对象的都叫做数据源。
3.数据源实际上是一套规范。JDK中有这套规范javax.sql.DataSource这个数据源的规范这套接口实际上是JDK规定的。
4.我们自己也可以编写数据源组件只要实现javax.sql.DataSource接口就行了。实现接口当中所有的方法。这样就有了自己的数据源。
比如你可以写一个属于自己的数据库连接池数据库连接池是提供连接对象的所以数据库连接池就是一个数据源。
5.常见的数据源组件有哪些呢【常见的数据库连接池有哪些呢】
阿里巴巴的德鲁伊连接池druid
c3p0
dbcp
....
6. type属性用来指定数据源的类型就是指定具体使用什么方式来获取Connection对象
type属性有三个值必须是三选一。
type="[UNPOOLED|POOLED|JNDI]"
UNPOOLED不使用数据库连接池技术。每一次请求过来之后都是创建新的Connection对象。
POOLED使用mybatis自己实现的数据库连接池。
JNDI集成其它第三方的数据库连接池。
JNDI是一套规范。谁实现了这套规范呢大部分的web容器都实现了JNDI规范
例如Tomcat、Jetty、WebLogic、WebSphere这些服务器(容器)都实现了JNDI规范。
JNDI是java命名目录接口。Tomcat服务器实现了这个规范。
-->
<!-- 数据源的type属性指定不同的值需要配置不太的属性 https://mybatis.net.cn/configuration.html#environments -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 以下配置数据库连接池的参数 -->
<!--提醒正常使用连接池的话池中有很多参数是需要设置的。设置好参数可以让连接池发挥的更好。事半功倍的效果。-->
<!--具体连接池当中的参数如何配置呢需要反复的根据当前业务情况进行测试。-->
<!--poolMaximumActiveConnections连接池当中最多的正在使用的连接对象的数量上限。最多有多少个连接可以活动。默认值10-->
<property name="poolMaximumActiveConnections" value="10"/>
<!--每隔2秒打印日志并且尝试获取连接对象-->
<property name="poolTimeToWait" value="2000"/>
<!--强行让某个连接空闲超时时间的设置-->
<property name="poolMaximumCheckoutTime" value="10000"/>
<!--最多的空闲数量-->
<!--
假设最多的连接数量为10个最多空闲数量为5个现在已经空闲5个了马上第六个也要空闲了
如果第六个空闲下来连接池为了保证空闲的数量最多5个会真正关闭多余的空闲连接对象
可以节省系统资源
-->
<property name="poolMaximumIdleConnections" value="5"/>
</dataSource>
</environment>
<!--这是mybatis的另一个环境也就是连接的数据库是另一个数据库mybatis-->
<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="root"/>
</dataSource>
</environment>
</environments>
<!-- mappers在mappers标签中可以配置多个sql映射文件的路径。 -->
<mappers>
<!-- mapper配置某个sql映射文件的路径 -->
<!-- resource属性使用相对于类路径的资源引用方式 -->
<!-- url属性使用完全限定资源定位符URL方式 -->
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
🥽 手写MyBatis框架
🌊 dom4j解析XML文件
引入dom4j的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.group</groupId>
<artifactId>parse-xml-by-dom4j</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- dom4j解析XML文件需要dom4j依赖与jaxen依赖 -->
<!--dom4j依赖-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!--jaxen依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
mybatis核心配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_study"/>
<property name="username" value="root"/>
<property name="password" value="123123"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
CarMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="car">
<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>
package com.powernode.xml.test;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class ParseXMLByDom4jTest {
@Test
public void testParseMyBatisConfigXML() throws Exception{
// 创建SAXReader对象
SAXReader reader = new SAXReader();
// 获取输入流
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
// 读XML文件返回document对象。document对象是文档对象代表了整个XML文件。
Document document = reader.read(is);
// 获取文档当中的根标签
//Element rootElt = document.getRootElement();
//String rootEltName = rootElt.getName();
//System.out.println("根节点的名字" + rootEltName);
//获取default默认的环境id
// xpath是做标签路径匹配的。能够让我们快速定位XML文件中的元素。
// 以下的xpath代表了从根下开始找configuration标签然后找configuration标签下的子标签environments
String xpath = "/configuration/environments";
Element environments = (Element) document.selectSingleNode(xpath); // Element是Node类的子类方法更多使用更便捷。
// 获取属性的值
String defaultEnvironmentId = environments.attributeValue("default");
//System.out.println("默认环境的id" + defaultEnvironmentId);
// 获取具体的环境environment
xpath = "/configuration/environments/environment[@id='"+defaultEnvironmentId+"']";
//System.out.println(xpath);
Element environment = (Element) document.selectSingleNode(xpath);
// 获取environment节点下的transactionManager节点(Element的element()方法用来获取孩子节点)
Element transactionManager = environment.element("transactionManager");
String transactionType = transactionManager.attributeValue("type");
System.out.println("事务管理器的类型" + transactionType);
// 获取dataSource节点
Element dataSource = environment.element("dataSource");
String dataSourceType = dataSource.attributeValue("type");
System.out.println("数据源的类型" + dataSourceType);
// 获取dataSource节点下的所有子节点
List<Element> propertyElts = dataSource.elements();
// 遍历
propertyElts.forEach(propertyElt -> {
String name = propertyElt.attributeValue("name");
String value = propertyElt.attributeValue("value");
System.out.println(name + "=" + value);
});
// 获取所有的mapper标签
// 不想从根下开始获取你想从任意位置开始获取所有的某个标签xpath该这样写
xpath = "//mapper";
List<Node> mappers = document.selectNodes(xpath);
// 遍历
mappers.forEach(mapper -> {
Element mapperElt = (Element) mapper;
String resource = mapperElt.attributeValue("resource");
System.out.println(resource);
});
}
@Test
public void testParseSqlMapperXML() throws Exception{
SAXReader reader = new SAXReader();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("CarMapper.xml");
Document document = reader.read(is);
// 获取namespace
String xpath = "/mapper";
Element mapper = (Element) document.selectSingleNode(xpath);
String namespace = mapper.attributeValue("namespace");
System.out.println(namespace);
// 获取mapper节点下所有的子节点
List<Element> elements = mapper.elements();
// 遍历
elements.forEach(element -> {
// 获取sqlId
String id = element.attributeValue("id");
System.out.println(id);
// 获取resultType
String resultType = element.attributeValue("resultType"); // 没有这个属性的话会自动返回"null"
System.out.println(resultType);
// 获取标签中的sql语句表示获取标签中的文本内容而且去除前后空白
String sql = element.getTextTrim();
System.out.println(sql);
// insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
// insert into t_car values(null,?,?,?,?,?)
// mybaits封装了jdbc。早晚要执行带有?的sql语句。
// 转换
String newSql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");
System.out.println(newSql);
});
}
}
🌊 GodBatis
GodBatishttps://www.yuque.com/zuihoudewu/java_note/mt2812#g4aW0