MyBatis 概述与入门
# 1. MyBatis 概述
# 1.1 JDBC 的不足
一个 JDBC 的示例程序如下:
String sql = "INSERT INTO t_user(...) ...";
PreparedStatement ps = conn.prepareStatement(sql);
...
int count = ps.executeUpdate();
2
3
4
或者:
String sql = "SELECT * FROM t_user";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
String id = rs.getString("id");
...
}
2
3
4
5
6
7
JDBC 存在的不足:
- SQL 语句写死在 Java 程序中,改动 SQL 的话就要改 Java 代码,违背了开闭原则;
- 书写复杂,Java 对象与 SQL 的交互十分繁琐,能不能自动化?
# 1.2 了解 MyBatis
以 jar 包的形式存在,本质上就是对 JDBC 的封装,是对 SQL 的一种 mapper,在 MVC 中负责数据持久化。
# 2. MyBatis 入门程序
软件版本:
- MySQL 8.0.30
- MySQL driver:8.0.30
- MyBatis:3.5.10
- JDK 17
- JUnit 4.13.2
- Logback 1.2.11
以下的内容可以结合 MyBatis 官方文档 (opens new window) 来看。
# 2.1 引入的依赖
Maven 中需要引入的依赖:
- MyBatis 依赖
- MySQL 驱动依赖
# 2.2 MyBatis 中两个主要的配置文件
- mybatis-config.xml:核心配置文件,主要配置连接数据库的信息等,只有一个
- XxxMapper.xml:专门用来编写 SQL 语句的配置文件,往往一个表一个
# 2.2.1 mybatis-config.xml
mybatis-config.xml 示例:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
<mapper resource="org/mybatis/example/CarMapper.xml"/>
</mappers>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这个文件用来构建一个 SqlSessionFactory 对象。
结尾处的 <mapper resource=""/>
这行代码用来指定 XxxMapper.xml 文件的路径,且自动从类的根路径下开始查找资源。
# 2.2.2 XxxMapper.xml
一个 CarMapper.xml 的配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写一个-->
<mapper namespace="org.mybatis.example.CarMapper">
<!--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>
2
3
4
5
6
7
8
9
10
11
12
13
14
- id 是这条 SQL 语句的唯一标识。
# 2.3 编写 MyBatis 程序
在 MyBatis 中,SqlSession 对象负责执行 SQL 语句,它是一个 Java 程序和数据库之间的一次会话。
- 要想获得 SQLSession 对象,需要先获取 SqlSessionFactory 对象,然后用它来生产 SqlSession 对象。
- 怎么获取 SqlSesionFactory 对象呢?需要先获取 SqlSessionFactoryBuilder 对象,再使用 builder 的
build()
方法来获取一个 SqlSessionFactory 对象。
因此,MyBatis 的核心对象包括:
- SqlSessionFactoryBuilder
- SqlSessionFactory
- SqlSession
一个比较完整的 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 程序写法
*/
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.执行SQL
int count = sqlSession.insert("insertCar"); // 这里传入的是 SQL 语句的 id
System.out.println("更新了几条记录:" + count);
// 5.提交
sqlSession.commit();
} catch (Exception e) {
// 回滚
if (sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
} finally {
// 6.关闭
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
注意点:
- 在这里 SqlSession 需要手动提交,即需要手动调用
commit()
方法 - 传入 SqlSession 进行调用的是在 mapper.xml 中编写的 SQL 的 id,不是一个 SQL 语句
关于第一个 MyBatis 程序的小细节:
- mapper.xml 中的 SQL 语句的结尾的分号可写可不写
Resources.getResourceAsStream
- 小技巧:以后凡是遇到 resource 这个单词,大部分情况下这种加载资源的方式就是从类的根路径下开始加载(或开始查找)
- mybatis 核心配置文件的名字不一定非得是
mybatis-config.xml
,而且它存放的路径也不一定非得是在类的根路径下- 当然,mapper.xml 也是如此
- 但最好还是遵循约定
- 加载资源的
Resources.getResourceAsStream()
方法本质上还是在调用类加载的ClassLoader.getSystemClassLoader().getResourceAsStream()
方法
# 2.4 MyBatis 中的事务管理机制
在 mybatis-config.xml 文件中,可以通过以下的配置来进行 MyBatis 的事务管理:<transactionManager type="JDBC"/>
其中 type 属性的值包括两个,对应了 MyBatis 提供的两种事务管理机制:
JDBC
:JDBC 事务管理器MANAGED
:MANAGED 事务管理器
type 的值只有以上两个值可选,不区分大小写
# 2.4.1 JDBC 事务管理器
表示 MyBatis 框架自己管理事务,自己采用原生的 JDBC 代码去管理事务:
conn.setAutoCommit(false); // 开启事务
...业务处理...
conn.commit(); // 手动提交事务
2
3
使用 JDBC 事务管理器的话,底层创建的事务管理器对象:JdbcTransaction
对象。
如果你编写的代码是:SqlSession sess = sqlSessionFactory.openSession(true);
,那表示没有开启事务,因为这样 MyBatis 就不会执行 conn.setAutoCommit(false)
。
在 JDBC 事务中,没有执行
conn.setAutoCommit(false)
就默认 autoCommit 为 true,而如果 autoCommit 为 true,那就表示没有开启事务,这样只要执行任意一条 DML 语句就会 commit 一次。因此不推荐这样的做法,而是应该开启事务。
# 2.4.2 MANAGED 事务管理器
表示 MyBatis 不再负责事务的管理了,而是把事务管理交给其他容器来负责,比如 Spring 容器。
如果我们只是单纯使用 MyBatis 库,且配置为 MANAHED 事务,那么事务这块就没人管了,没人管理事务就表示事务压根不会开启,即没有事务了。
重点
以后注意,只要你的 autoCommit 是 true,就表示没有开启事务。只有你的 autoCommit 是 false 的时候,才表示开启了事务。
# 3. 使用 JUnit
JUnit 是做单元测试的,这里使用 JUnit 只是为了验证我们的程序,避免写过多的 main 方法,而不是为了单元测试。
JUnit 依赖:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
2
3
4
5
6
编写单元测试类:
// 测试用例
public class CarMapperTest{
// 测试方法
@Test
public void testInsert(){}
@Test
public void testUpdate(){}
}
2
3
4
5
6
7
8
9
10
11
# 4. 引入日志框架 LogBack
MyBatis 集成日志组件可以让我们调试起来更加方便。
# 4.1 MyBatis 支持的日志组件
MyBatis 常见的集成的日志组件有哪些呢?这可以参考文档中的配置项:
其中 STDOUT_LOGGING 是标准日志,MyBatis 已经实现了这种标准日志,我们只要开启即可。
开启的方式就是在 mybatis-config.xml 中使用 settings 标签进行配置开启:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
2
3
标准日志也可以用,可以看到 SQL 语句是怎样的、连接对象什么时候创建的等信息,但是配置不够灵活,也看不到详细的日期、线程名字等。如果想使用更加丰富的配置,可以继承第三方的 log 组件。
SLF4J(沙拉风)也是一个日志标准,而且 logback 实现了 SLF4J 规范。Logback 是目前日志框架中性能较好的、较流行的,所以我们选择它。
# 4.2 引入 Logback 的步骤
Logback 实现了 SLF4J 标准。
第一步:引入 logback 依赖:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>test</scope>
</dependency>
2
3
4
5
6
第二步:引入 logback 相关配置文件
- 这个配置文件的名字必须叫做
logback.xml
或者logback-test.xml
,不能是其他的名字 - 这个配置文件必须放到类的根路径下,不能是其他位置
logback.xml 如下(可以直接 copy 然后使用),用来配置格式等:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--mybatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
这时候再去执行 mybatis 程序,就可以看到输出中有实际执行的 SQL 语句了。
# 5. MyBatis 工具类 SqlSessionUtil 的封装
由于每次获取 SqlSession 对象的代码太繁琐了,所以这里封装一个工具类,用来获取 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工具类
*/
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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34