MyBatis 是一款优秀的持久层框架,封装 JDBC 操作,基于 XML 或注解方式使用,支持自定义 SQL、存储过程、高级映射。
基础知识
- Java 基础
- MySQL 数据库
- JDBC
- Maven、JUnit 工具
1、概述
1.1、JDBC
Java 数据库连接(Java DataBase Connectivity,简称 JDBC)
- 使用 Java 语言操作关系型数据库的 API。
- SUN 公司定义的标准接口(JDBC),定义了操作关系型数据库的规则。
1.1.1、开发步骤
- 驱动管理
- 注册驱动:Driver
- 获取数据库连接:URL、用户名、密码
- 定义 SQL 模板语句
- 获取 SQL 执行对象:PreparedStatement,设置占位符参数。
- 执行 SQL,获得返回结果:受影响行数 / 结果集
- 释放资源
1.1.2、问题分析(❗)
- 重复编码:对于相同的 JDBC 操作(增删改查),在不同业务中
- 通常只有 SQL 语句不同。
- 驱动管理、设置参数、封装实体、释放资源等操作相同。
- SQL 硬编码:SQL 语句硬编码于 Java 代码中
- 当业务变动时,需要修改代码中的 SQL 语句。
- 不易于维护。
- 实体转换
- 封装实体:执行 DQL 时,需要手动将查询结果集中的字段封装到 Java 实体中。
- 参数设置:执行 DML 时,需要手动将实体数据设置到对应位置的占位符参数。
1.2、MyBatis
1.2.1、说明
原 apache 的开源项目 iBatis。
2010 年由 apache software foundation 迁移到 google code,改名 MyBatis。
2013 年 11 月迁移到 Github。
- 基于 Java 的持久层框架。
- 内部封装 JDBC
- 开发者只需关注 SQL 本身
- 无需关注 JDBC 访问细节(包括结果集的获取、实体封装和参数设置等工作)。
- 基于 XML 或注解方式,建立原生类型、接口、POJO与数据库记录之间的关系。
- 支持自定义 SQL、存储过程、高级映射。
相关链接
- 官方文档:https://mybatis.org/mybatis-3/zh/index.html
- W3C school:https://www.w3cschool.cn/mybatis/
- Maven 仓库:https://mvnrepository.com/artifact/org.mybatis/mybatis
1.2.2、优点
- 简单易学,无第三方依赖。
- SQL 与 Java 代码分离,耦合度低,可维护性高。
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
- 提供对象关系映射标签,支持对象关系组建维护。
- 支持动态 SQL。
2、MyBatis 使用
2.1、搭建环境
以 user(user_id, name, password) 为例
- 数据库:创建 user 表。
- 项目搭建
- 导入依赖:MySQL 驱动、MyBatis
- 实体类:User
- 核心配置文件:通常命名 SqlMapConfig 或 mybatis-config
- 映射文件:UserMapper.xml
2.1.1、数据库表
- 主键设计
- 逻辑主键:id,区分每个字段。
- 业务主键:user_id,区分每个业务实体。
- 除此之外,每个数据库表还应该有
create_time
、update_time
字段,此处省略。
2.1.2、搭建项目
- 导入依赖:MySQL 驱动、MyBatis
<!-- MySQL数据库连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
- 实体类:User
public class User {
private String userId;
private String name;
private String password;
// 构造方法
// toString()、getter、setter
}
2.1.3、核心配置文件(❗)
核心配置文件:
mybatis-config.xml
(存放于
resources
目录)
- 约束信息:从 官方文档 复制即可
- 配置信息
- setttings:开启命名映射,将数据库列名(xxx_yyy)自动映射为 Java 属性名(xxxYyy)
- environments:需选择默认环境(可参考 JDBC)
- 事务管理器:JDBC
- 数据源:驱动、URL、用户名、密码
- mappers:注意用
/
分隔符
<?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>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="数据库驱动"/>
<property name="url" value="jdbc:mysql://主机号:端口号/数据库名?参数"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="映射文件的全限类名"/>
</mappers>
</configuration>
2.2、映射文件
以查询所有用户为例,基于原生 MyBatis 实现。
2.2.1、映射文件模板
注:文件存放于 resources 目录,模板从 官方文档 复制即可。
- 约束信息
- 映射信息
- 命名空间:唯一区分 SQL 映射文件。
- SQL 标签
- id:唯一区分该命名空间下的 SQL 语句,与 Mapper 接口的方法同名即可。
- resultType:结果类型,即结果集所要封装的实体。
- parameterType:参数类型,占位符为
#{}
<?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="">
<insert id="" resultType="" parameterType="">
</insert>
</mapper>
2.2.2、UserMapper.xml(❗)
注意:CRUD 操作对应不同的 SQL 标签。
<?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="userMapper">
<select id="listUsers" resultType="indi.jaywee.pojo.User">
SELECT user_id, name, password
FROM study_mysql.t_user
</select>
<insert id="insertUser" parameterType="indi.jaywee.pojo.User">
INSERT INTO study_mysql.t_user(user_id, name, password)
VALUE (#{userId}, #{name}, #{password})
</insert>
<delete id="deleteUserByUserId" parameterType="java.lang.String">
DELETE
FROM study_mysql.t_user
WHERE user_id = #{userId}
</delete>
<update id="updateUser" parameterType="indi.jaywee.pojo.User">
UPDATE study_mysql.t_user
SET name = #{name},
password = #{password}
WHERE user_id = #{userId}
</update>
</mapper>
2.3、CRUD 代码(❗)
2.3.1、说明
- 环境仅需搭建一次,后续操作修改映射文件即可。
- MyBatis 默认不自动提交事务,DML 需要手动提交事务。
- 方法参数列表的第 2 个参数,对应 SQL 语句的
parameterType
。
2.3.2、工具类(❗)
MyBatis 中的 SQL 执行对象是
SqlSession
(相当于 JDBC 的 Statement)
-
工厂模式:SqlSession 实例由
SqlSessionFactory
工厂创建。 - 建造者模式:SqlSessionFactory 由
SqlSessionFactoryBuilder
构建。
工具类:构建
SqlSessionFactory
工厂,提供SqlSession
实例。
- 加载核心配置文件。
- 构建
sqlSessionFactory
工厂对象。 - 通过工厂创建
sqlSession
实例。
public class MyBatisUtils {
private static final SqlSessionFactory SQL_SESSION_FACTORY;
static {
String resource = "mybatis-config.xml";
InputStream is = null;
try {
// 加载核心配置文件
is = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// 构建sqlSessionFactory
SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(is);
}
public static SqlSession getSqlSession() {
// 创建sqlSession实例
return SQL_SESSION_FACTORY.openSession();
}
}
2.3.3、使用步骤
- 获取
sqlSession
实例(从工具类中获取)。 - 执行 SQL,获得返回结果。
- * 提交事务
- 释放资源。
@Test
public void testInsert() {
User user = new User("u_demo", "demo", "123456");
SqlSession sqlSession = MyBatisUtils.getSqlSession();
int row = sqlSession.insert("userMapper.insertUser", user);
System.out.println(row);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testDelete() {
String userId = "u_demo1";
SqlSession sqlSession = MyBatisUtils.getSqlSession();
int row = sqlSession.delete("userMapper.deleteUserByUserId", userId);
System.out.println(row);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testUpdate() {
User user = new User("u_demo2", "demo666", "666666");
SqlSession sqlSession = MyBatisUtils.getSqlSession();
int row = sqlSession.update("userMapper.updateUser", user);
System.out.println(row);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testQuery() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
List<User> userList = sqlSession.selectList("userMapper.listUsers");
System.out.println(userList);
sqlSession.close();
}
3、常见问题
3.1、常见报错
java.io.IOException
Could not find resource resources/mybatis-config.xml
- 报错:无法找到 MyBatis 核心配置文件。
- 原因:工具类
MyBatisUtils
中的 resource 变量值有误。
org.apache.ibatis.binding.BindingException
Type interface XxxMapper is not known to the MapperRegistry.
- 报错:Mapper 未知。
- 原因:没有在 MyBatis 核心配置文件中注册 Mapper(注意路径分隔符为
/
)。
java.lang.ExceptionInInitializerError
- 报错:初始化失败,无法找到 XxxMapper.xml 配置文件。
- 原因:资源配置文件没有存放在
resources
目录下。 - 说明
- Maven 项目在构建编译时,默认只会将
resources
目录下的资源配置文件导出到target
目录中。 - 在实际项目中,可能会把资源配置文件放在
resources
以外的位置。
(如 Mapper.xml 和接口类放在一起) - 这些资源配置文件不会被导出,导致资源配置文件读取失败。
- 解决:两种方案
- 在
resources
目录下建立与 Java 代码的同级目录,存放资源配置文件。 - 项目
pom.xml
中设置过滤。
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
无效的 2 字节、3 字节等问题
- 原因:在 XML 中使用中文注释。
- 说明:字符编码问题。
- IntelliJ IDEA 默认使用
GBK
对资源配置文件编码,以上的 XML 文件使用UTF-8
。 - 此时,若在 XML 文件中使用中文注释会报此异常。
- 解决:将 IntelliJ IDEA 的字符编码设为 UTF-8(开启 IDEA 的小窗口、进入项目的设置,2 处都需设置)
3.2、SQL 注入问题
(回顾 JDBC - 2.5.1)
Web 攻击技术:通过特殊字符 恶意拼接 SQL,对 SQL 语句进行转义,从而非法运行 SQL。
解决思路:通过占位符的方式,取代 SQL 拼接。
JDBC | MyBatis | |
拼接字符串 | statement | $ |
预处理 SQL | preparedStatement + 占位符 |
|
3.3、参数问题(❗)
3.3.1、说明
- 参数对应问题
- Mapper 接口方法的参数,对应 Mapper.xml 映射文件的 parameterType。
- Mapper 接口方法的返回值,对应 Mapper.xml 映射文件的 resultType。
- 参数省略
- parameterType 为简单类型(基本类型、String)时可省略,MyBatis 会自动识别类型。
- resultType 通常为自定义 pojo,不能省略。
3.3.2、多个参数
接口方法的参数列表有多个简单类型参数(基本类型、String)
以获取登录用户为例,演示以下 2 种处理方式。
- @Param 注解
- Map 集合
@Param 注解
- Mapper 接口:在简单类型参数前添加
@Param
注解。
User getLoginUser(@Param("name") String name,
@Param("password") String password);
- Mapper.xml:SQL 语句的
#{}
填写注解值。
<select id="getLoginUser" resultType="user">
SELECT user_id, name, password
FROM study_mysql.t_user
WHERE name = #{name}
AND password = #{password}
</select>
Map 集合
- Mapper 接口:将简单类型参数封装成
Map
集合
User getLoginUser1(Map<String, String> userMap);
- Mapper.xml:SQL 语句的
#{}
填写 Map 的 Key 值。
<select id="getLoginUser1" resultType="user">
SELECT user_id, name, password
FROM study_mysql.t_user
WHERE name = #{name}
AND password = #{password}
</select>
- 测试:封装 map,调用接口方法。
@Test
public void testGetLoginUser1() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, String> map = new HashMap<>();
map.put("name", "Jaywee");
map.put("password", "123456");
User user = mapper.getLoginUser1(map);
System.out.println(user);
sqlSession.close();
}