MyBatis①概述、CRUD、常见问题
基础知识
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、存储过程、高级映射。
相关链接
-
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 和接口类放在一起) - 这些资源配置文件不会被导出,导致资源配置文件读取失败。
- Maven 项目在构建编译时,默认只会将
-
解决:两种方案
-
在
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 默认使用
-
解决:将 IntelliJ IDEA 的字符编码设为 UTF-8(开启 IDEA 的小窗口、进入项目的设置,2 处都需设置)
3.2、SQL 注入问题
(回顾 JDBC - 2.5.1)
Web 攻击技术:通过特殊字符 恶意拼接 SQL,对 SQL 语句进行转义,从而非法运行 SQL。
解决思路:通过占位符的方式,取代 SQL 拼接。
JDBC | MyBatis | |
---|---|---|
拼接字符串 | statement | $ |
预处理 SQL | preparedStatement + 占位符? |
#{value} |
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(); }