目录
MyBatis介绍
MyBatis 是一款轻量级 Java 持久层框架,核心价值是封装 JDBC 繁琐操作(如加载驱动、创建连接、处理结果集),让开发者聚焦 SQL 逻辑而非重复的 JDBC 代码。
核心设计思想
- 配置驱动:通过 XML 或注解定义 SQL 语句,避免硬编码。
- 参数映射 :自动将 Java 对象与 SQL 动态参数绑定(如
#{username}
)。 - 结果映射:执行 SQL 后,自动将数据库结果集转换为 Java 对象(ORM 思想)。
- 低侵入性:不强制依赖复杂配置,可灵活集成到各类项目中。
Mybatis简单示例
1.创建库和表结构:
sql
-- 1. 创建数据库
create database mybatis_db;
use mybatis_db;
-- 2. 创建用户表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID(自增)',
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别(男/女)',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 3. 插入测试数据
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`)
values
(1,'老王','2018-02-27 17:47:08','男','北京'),
(2,'熊大','2018-03-02 15:09:37','女','上海'),
(3,'熊二','2018-03-04 11:34:34','女','深圳'),
(4,'光头强','2018-03-04 12:04:06','男','广州');
2.所需依赖:
XML
<dependencies>
<!-- 1. MyBatis 核心依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version> <!-- 稳定版本,兼容性好 -->
</dependency>
<!-- 2. MySQL 驱动(适配 MySQL 5.x;若用 MySQL 8.x,需改为 8.0+ 版本,驱动类为 com.mysql.cj.jdbc.Driver) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- 3. 单元测试(JUnit 4,简化测试代码) -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope> <!-- 仅测试环境生效 -->
</dependency>
<!-- 4. 日志(Log4j,打印 SQL 执行日志,便于调试) -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
补充:Log4j 配置(可选)
在 resources
目录下创建 log4j.properties
,开启 SQL 日志打印:
XML
# 日志输出级别:DEBUG(打印 SQL 细节)、INFO(仅关键信息)
log4j.rootLogger=DEBUG, Console
# 控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
# 日志格式:时间 | 线程 | 级别 | 类名 | 日志内容
log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %m%n
# MyBatis 日志细化(可选)
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
3.编写User实现类:
java
package com.mszlu.domain;
import java.util.Date;
/**
* 用户实体类(对应数据库 user 表)
*/
public class User {
private Integer id; // 对应表 id 列(自增)
private String username; // 对应表 username 列
private Date birthday; // 对应表 birthday 列(MyBatis 自动转换 datetime ↔ Date)
private String sex; // 对应表 sex 列
private String address; // 对应表 address 列
// 无参构造器(MyBatis 反射创建对象时必须)
public User() {}
// 有参构造器(便于手动创建对象)
public User(String username, Date birthday, String sex, String address) {
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
// Getter + Setter(MyBatis 需通过 Setter 注入结果集数据)
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Date getBirthday() { return birthday; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
// toString(便于打印对象信息)
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
4.编写userMapper以及userMapper.xml
userMapper:定义接口

userMapper.xml( 在resources目录下,创建mapper文件夹。编写UserMapper.xml的配置文件**):定义sql语句**

5.编写主配置文件 SqlMapConfig.xml,SqlMapConfig.xml是MyBatis框架的核心配置文件。 在resources目录下创建SqlMapConfig.xml的配置文件(名称可以任意),导入对应的约束,编写主配置文件。
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="mysql">
<!-- 配置具体的环境 -->
<environment id="mysql">
<!-- 配置事务管理类型 -->
<transactionManager type="JDBC"/>
<!-- 配置是否需要使用连接池,POOLED使用,UNPOOLED不使用 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射的配置文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
6.编写测试方法
java
public class testDemo1 {
@Test
public void testFindAll() throws IOException {
// 加载主配置文件,目的是构建SqlSessionFactory的对象
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 使用SqlSessionFactory工厂对象创建SqlSession对象
SqlSession session = factory.openSession();
// 通过session创建UserMapper接口的代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 调用查询所有的方法
//第一种方式:
List<User> list = mapper.findAll();
//第二种方式
// List<User> userList = session.selectList("com.mszlu.mapper.UserMapper.findAll");
// 遍历集合
for (User user : list) {
System.out.println(user);
}
// 释放资源
session.close();
in.close();
}
}
MyBatis编写流程
MyBatis 推荐使用 "接口 + XML" 的方式定义 SQL:Mapper 接口
定义方法签名,Mapper XML
定义具体 SQL 语句,两者通过全限定名 + 方法名关联(无需手动实现接口,MyBatis 动态生成代理对象)。
使用Dao进行增删改查
1.创建userMapper接口
java
package com.mszlu.mapper;
import com.mszlu.domain.QueryVo;
import com.mszlu.domain.User;
import java.util.List;
/**
* User 表 Mapper 接口(定义 SQL 方法签名)
*/
public interface UserMapper {
// 1. 查询所有用户
List<User> findAll();
// 2. 测试列名与属性名不一致(需用 resultMap 映射)
List<User> findAll2();
// 3. 按 ID 查询用户(参数为简单类型:Integer)
User findById(Integer userId);
// 4. 新增用户(返回自增 ID,参数为 POJO:User)
void insert(User user);
// 5. 修改用户(参数为 POJO:User)
void update(User user);
// 6. 删除用户(参数为简单类型:Integer)
void delete(Integer userId);
// 7. 模糊查询用户(参数为简单类型:String)
List<User> findByName(String username);
// 8. 查询用户总数(返回简单类型:Integer)
Integer findByCount();
// 9. 按包装类查询(参数为包装类:QueryVo)
List<User> findByVo(QueryVo queryVo);
}
2.在resources/mapper
目录下创建 UserMapper.xml
,编写 SQL 语句与结果映射:
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 接口的全限定名一致(关联接口与 XML)
例:com.mszlu.mapper.UserMapper
-->
<mapper namespace="com.mszlu.mapper.UserMapper">
<!-- 1. 结果映射(resultMap):解决"表列名与实体类属性名不一致"问题 -->
<!--
id:resultMap 的唯一标识(供 select 标签的 resultMap 属性引用)
type:映射的实体类类型(可直接用别名 user,对应 com.mszlu.domain.User)
-->
<resultMap id="userMap" type="user">
<!--
id 标签:映射主键列(性能优化,可选但推荐)
result 标签:映射普通列
property:实体类属性名
column:表列名(或 SQL 查询的列别名)
-->
<id property="id" column="id"/> <!-- 若列名与属性名一致,可省略该配置 -->
<result property="username" column="_username"/> <!-- 列别名为 _username,对应属性 username -->
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</resultMap>
<!-- 2. 查询所有用户(列名与属性名一致,用 resultType) -->
<!--
id:必须与 Mapper 接口的方法名一致(findAll)
resultType:返回结果类型(实体类别名或全限定名,MyBatis 自动映射)
-->
<select id="findAll" resultType="user">
select * from user;
</select>
<!-- 3. 查询所有用户(列名与属性名不一致,用 resultMap) -->
<!-- SQL 中 username 列取别名为 _username,需通过 userMap 映射到属性 username -->
<select id="findAll2" resultMap="userMap">
select id, username _username, birthday, sex, address from user;
</select>
<!-- 4. 按 ID 查询用户(简单参数,用 #{占位符}) -->
<!--
parameterType:参数类型(简单类型可省略,MyBatis 自动推断)
#{id}:占位符,对应方法参数(简单类型时,占位符名称可任意,如 #{userId} 也可)
-->
<select id="findById" parameterType="int" resultType="user">
select * from user where id = #{id};
</select>
<!-- 5. 新增用户(返回自增 ID) -->
<!--
<selectKey>:获取新增后的自增 ID(MySQL 用 last_insert_id() 函数)
keyProperty:将自增 ID 注入到实体类的哪个属性(User.id)
order:执行时机(AFTER:插入后执行,BEFORE:插入前执行)
resultType:返回 ID 的类型
-->
<insert id="insert" parameterType="user">
<selectKey keyProperty="id" order="AFTER" resultType="int">
select last_insert_id(); <!-- MySQL 系统函数:返回当前会话最后一次插入的自增 ID -->
</selectKey>
insert into user (username, birthday, sex, address)
values (#{username}, #{birthday}, #{sex}, #{address});
</insert>
<!-- 6. 修改用户 -->
<update id="update" parameterType="user">
update user
set username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address}
where id=#{id};
</update>
<!-- 7. 删除用户 -->
<delete id="delete" parameterType="int">
delete from user where id = #{id};
</delete>
<!-- 8. 模糊查询用户(两种方式:#{} 与 ${}) -->
<!-- 方式1:#{}(推荐,防 SQL 注入,需手动加 %) -->
<select id="findByName" parameterType="string" resultType="user">
select * from user where username like #{username};
<!-- 调用时需传参数:"%王%"(如 mapper.findByName("%王%")) -->
</select>
<!-- 方式2:${}(不防 SQL 注入,直接拼接字符串,占位符只能是 ${value}) -->
<!--
<select id="findByName" parameterType="string" resultType="user">
select * from user where username like '${value}';
<!-- 调用时需传参数:"%王%" 或 "王%"(如 mapper.findByName("%王%")) -->
</select>
-->
<!-- 9. 查询用户总数(返回简单类型) -->
<select id="findByCount" resultType="int">
select count(*) from user; <!-- count(*) 返回 int 类型 -->
</select>
<!-- 10. 按包装类查询(嵌套参数,用 #{对象.属性}) -->
<!-- parameterType:包装类类型(QueryVo),通过 #{user.username} 引用嵌套对象的属性 -->
<select id="findByVo" parameterType="queryVo" resultType="user">
select * from user where username = #{user.username};
</select>
</mapper>
3.测试代码...
java
import com.mszlu.domain.QueryVo;
import com.mszlu.domain.User;
import com.mszlu.mapper.UserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
public class UserTest {
private InputStream in;
private SqlSession session;
private UserMapper mapper;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in) ;
session = factory.openSession();
mapper = session.getMapper(UserMapper.class);
}
@After
public void destory() throws IOException {
session.close();
in.close();
}
@Test
public void testFindAll() throws IOException {
List<User> userList = mapper.findAll();
for(User user : userList){
System.out.println(user);
}
in.close();
}
@Test
public void testFindAll2() throws IOException {
List<User> userList = mapper.findAll2();
for(User user : userList){
System.out.println(user);
}
in.close();
}
@Test
public void testFindById() throws Exception {
User user = mapper.findById(4);
System.out.println(user);
in.close();
}
@Test
public void testInsert() throws Exception {
User user = new User();
user.setUsername("美美2");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("顺义");
mapper.insert(user);
session.commit();
System.out.println(user.getId());
}
@Test
public void testUpdate() throws Exception {
User user = mapper.findById(4);
user.setUsername("小凤");
mapper.update(user);
session.commit();
}
@Test
public void testDelete() throws Exception {
mapper.delete(5);
session.commit();
}
// 第一种
@Test
public void testFindByName() throws Exception {
List<User> list = mapper.findByName("%王%");
for (User user : list) {
System.out.println(user);
}
}
// 第二种
@Test
public void testFindByName2() throws Exception {
List<User> list = mapper.findByName("王");
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testFindByCount() throws Exception {
Integer count = mapper.findByCount();
System.out.println("总记录数:"+count);
}
//包装类测试
@Test
public void testFindByVo() throws Exception {
QueryVo queryVo = new QueryVo();
User user = new User();
user.setUsername("熊大");
queryVo.setUser(user);
List<User> userList = mapper.findByVo(queryVo);
for (User user1 : userList) {
System.out.println(user1);
}
}
}
模糊查询符号使用的区别
通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
通过可以将传入的内容拼接在中且不进行类型转换,{}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, {}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是value。
|----------|------------------------------|-------------------|
| 特性 | #{} | **{}** |
| 本质 | PreparedStatement 占位符(`?` ) | 字符串直接拼接 |
| 类型转换 | 自动进行 Java 类型 ↔ JDBC 类型转换 | 不转换,直接拼接字符串 |
| SQL 注入防护 | 支持(占位符不直接拼接 SQL) | 不支持(直接拼接,有注入风险) |
| 简单类型参数名称 | 可任意(如 `#{id}` 、`#{userId}` ) | 只能是 `{value}` |
| 适用场景 | 普通参数传递(推荐) | 动态表名、动态排序字段(谨慎使用) |
Mybatis参数
一.MyBatis参数详解
1、简单数据类型:java八大类+String(mybatis把String归为简单数据类型)
简单的写法:java.lang.Integer --> int integer Int Integer 都可以,框架提供简写的方式。


2、POJO(JavaBean实体类)对象类型,默认不能简写,可以配置
例子如:User对象
3、POJO包装对象类型,包含更多的实体类(即对象中引用其他对象)
**如在QueryVo实体类中:**包含了User对象

在mapper接口中,加入这个函数
public List<User> findByVo(QueryVo queryVo);
实现:
参数类型是QueryVo类,我们可以直接使用二级子类,比如user,name,role等,然后使用user里的属性再进行查询
XML
<!-- 根据封装类型来查询-->
<select id="findByVo" resultType="com.mszlu.domain.User" parameterType="com.mszlu.domain.QueryVo" >
select * from user where username = #{user.username}
</select>
测试类:
java
//包装类测试
@Test
public void testFindByVo() throws Exception {
QueryVo queryVo = new QueryVo();
User user = new User();
user.setUsername("熊大");
queryVo.setUser(user);
List<User> userList = mapper.findByVo(queryVo);
for (User user1 : userList) {
System.out.println(user1);
}
}
二.resultType
1.返回简单数据类型
int double long(Java八大基本类型) String
2.返回POJO数据类型
返回User对象类型
3.resultMap结果类型
resultType 用法 :
直接指定一个实体类(比如 User),查询结果会自动装进这个类的对象里。
但要求:数据库查出来的列名 (比如表的 username 列)和 实体类的属性名(比如 User 类的 username 属性)必须一模一样,才能对应上。
resultMap 用法 :
当列名和属性名不一样时 (比如表列是 user_name,类属性是 userName),用它来手动指定 "列名对应哪个属性名",帮它们搭个桥。
另外,它还能处理复杂的情况:比如实体类里不仅有基本属性,还包含另一个实体类(一对一)或者一个列表(一对多),也能正确映射进去。
<!--
配置resultMap,用来进行数据封装
id="唯一的名称,用来被引用的"
type="进行封装数据的类型"
-->
<resultMap id="userMap" type="com.qcbyjy.domain.User">
<!--
property="JavaBean中的属性"
column="表中的字段"
-->
<result property="id" column="_id"/>
<result property="username" column="_username" />
<result property="birthday" column="_birthday" />
<result property="sex" column="_sex" />
<result property="address" column="_address" />
</resultMap>
对于sql语句:
select id, username _username, birthday, sex, address from user;
实际查询的字段是username,但返回的结果的字段是别名_username
所以当我数据库表结构如下:

而查询的sql语句以及resultMap映射是上面所写的时
sql语句返回的结果集中,所对应字段名字是id,_username,birthday,sex,address,在结果集中查找,只有_username可以对上,所以只有_username才有结果,其他都为空

SqlMapConfig.xml配置文件
1.1. 定义properties标签的方式管理数据库的信息
即把数据库信息定义properties标签中的方法,然后dataSource再使用

1.2 在项目中定义jdbc.properties属性文件,存储数据库相关的信息,统一管理
1.2.1jdbc.properties属性文件
XML
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db
jdbc.username=root
jdbc.password=123456
1.2.2 编写sql配置文件

这样少了前面的<properties>标签中的配置
2、类型别名定义
- MyBatis自已有类型别名的注册类,编写int或者integer通过注册可以找到java.lang.Integer
- 我们自己也可以进行别名的注册
-
SqlMapConfig.xml的配置文件
<typeAliases> <typeAlias type="com.mszlu.domain.User" alias="user"/> <package name="com.mszlu.domain"/> </typeAliases>
(注意xml中顺序, MyBatis 配置文件的标签顺序不符合规范 。MyBatis 对 <configuration>
内部子标签的顺序有严格要求,必须按照官方指定的顺序排列,否则会报格式错误。)
官方规定的 <configuration>
子标签顺序为(按先后排列):
properties? → settings? → typeAliases? → typeHandlers? → objectFactory? → objectWrapperFactory? → reflectorFact


此时就可以成功修改了。
常见问题排查
- ClassNotFoundException**(类找不到)** :检查 Mapper 接口全限定名、实体类路径是否与 XML 中
namespace
、resultType
一致。 - BindingException**(接口与 XML 绑定失败)** :检查 XML 中
id
是否与接口方法名一致,namespace
是否与接口全限定名一致。 - SQLSyntaxErrorException**(SQL 语法错误)**:打印 SQL 日志(Log4j),检查生成的 SQL 是否正确(如参数拼接、表名 / 列名是否存在)。
- MySQL 8.x 驱动报错 :需将驱动版本改为
8.0+
,驱动类改为com.mysql.cj.jdbc.Driver
,URL 加时区参数?serverTimezone=UTC