一、MyBatis 框架概述
1.1 框架定位与核心优势
MyBatis 是基于 Java 的持久层框架,通过封装 JDBC 简化数据库操作,开发者只需专注 SQL 语句编写,无需处理连接创建、参数设置、结果集解析等底层逻辑。其核心优势包括:
- ORM 思想集成:通过 XML 或注解实现 SQL 与 Java 对象的映射,减少手动数据转换代码。
- 灵活性与可控性:保留 SQL 编写的自由度,适合复杂查询场景。
- 动态 SQL 支持:通过
<if>
、<foreach>
等标签实现条件拼接,提升 SQL 复用性。
1.2 核心组件
- SqlSessionFactory:构建会话工厂,基于配置文件(如
SqlMapConfig.xml
)创建SqlSession
。 - SqlSession:执行 SQL 的核心会话对象,提供
selectList
、selectOne
等方法,并管理事务。 - Mapper 接口与 XML 映射文件:通过接口定义方法,XML 配置 SQL 语句及参数/结果映射。
二、入门实战:从环境搭建到首个查询
2.1 数据库准备
创建数据库与表结构(mybatis_db
数据库,user
表):
CREATE DATABASE mybatis_db;
USE mybatis_db;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`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;
INSERT INTO `user` 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.2 Maven 依赖配置
在 pom.xml
中引入核心依赖:
<dependencies>
<!-- MyBatis 核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- JUnit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!-- Log4j 日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2.3 代码实现步骤
2.3.1 定义实体类 (User)
package cn.tx.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class User implements Serializable{
private static final long serialVersionUID = 525400707336671154L;
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
2.3.2 编写 Mapper 接口 (UserMapper)
package cn.tx.mapper;
import java.util.List;
import cn.tx.domain.User;
public interface UserMapper {
List<User> findAll(); // 查询所有用户
}
2.3.3 配置 Mapper XML 文件(UserMapper.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="cn.tx.mapper.UserMapper">
<select id="findAll" resultType="cn.tx.domain.User">
SELECT * FROM user;
</select>
</mapper>
2.3.4 主配置文件 (SqlMapConfig.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"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis_db"/>
<property name="username" value="root"/>
<property name="password" value="12345"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
2.3.5 编写测试代码
package cn.tx.test;
import java.io.InputStream;
import java.util.List;
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.Test;
import cn.tx.domain.User;
import cn.tx.mapper.UserMapper;
public class UserTest {
@Test
public void testFindAll() throws Exception {
// 加载主配置文件,目的是构建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();
// 遍历集合
for (User user : list) {
System.out.println(user);
}
// 释放资源
session.close();
in.close();
}
@Test
public void run2() throws Exception {
// 加载配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 构建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取到session对象
SqlSession session = factory.openSession();
// 查询所有的数据
List<User> list = session.selectList("cn.tx.mapper.UserMapper.findAll");
// 变量集合
for (User user : list) {
System.out.println(user);
}
// 关闭资源
session.close();
inputStream.close();
}
}
三、代理 Dao 方式实现 CRUD
3.1 Mapper 接口扩展方法
public interface UserMapper {
public List<User> findAll(); // 查询所有用户
public User findById(Integer userId); // 根据 ID 查询
public void insert(User user); // 插入用户
public void update(User user); // 更新用户
public void delete(Integer userId); // 删除用户
public List<User> findByName(String username); // 模糊查询
public Integer findByCount(); // 统计总数
}
3.2 Mapper XML 完整配置(UserMapper.xml)
<mapper namespace="cn.tx.mapper.UserMapper">
<select id="findAll" resultType="cn.tx.domain.User">
SELECT * FROM user
</select>
<!--
通过id查询
SQL语句使用#{占位符的名称,名称可以任意},仅限于基本数据类型和String类型
-->
<select id="findById" resultType="cn.tx.domain.User" parameterType="int">
SELECT * FROM user WHERE id = #{id}
</select>
<insert id="insert" parameterType="cn.tx.domain.User">
/*
keyProperty表示要返回的属性名称
order取值AFTER表示插入数据后的行为
resultType表示返回值的类型
*/
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO user (username, birthday, sex, address)
VALUES (#{username}, #{birthday}, #{sex}, #{address})
</insert>
<update id="update" parameterType="cn.tx.domain.User">
UPDATE user
SET username=#{username}, birthday=#{birthday},
sex=#{sex}, address=#{address}
WHERE id=#{id}
</update>
<delete id="delete" parameterType="Integer">
DELETE FROM user WHERE id=#{id}
</delete>
<!-- 模糊查询 -->
<select id="findByName" resultType="cn.tx.domain.User" parameterType="string">
<!-- 第一种方式的SQL语句
select * from user where username like #{username}
-->
<!-- 第二种方式的SQL语句 强调:'%${value}%'不能修改,固定写法(不推荐使用) -->
SELECT * FROM user WHERE username LIKE '%${value}%' <!-- 注意:$ 可能存在 SQL 注入风险 -->
</select>
<select id="findByCount" resultType="int">
SELECT COUNT(*) FROM user
</select>
</mapper>
3.3 测试代码示例
public class UserTest {
private InputStream in;
private SqlSession session;
private UserMapper mapper;
@Before
public void init() throws Exception {
// 加载配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 创建Session对象
session = factory.openSession();
// 获取到代理对象
mapper = session.getMapper(UserMapper.class);
}
@After
public void destory() throws IOException {
in.close();
session.close();
}
/**
* 测试查询所有的方法
* @throws Exception
*/
@Test
public void testFindAll() throws Exception {
List<User> list = mapper.findAll();
// 遍历
for (User user : list) {
System.out.println(user);
}
in.close();
}
@Test
public void testFindById() throws Exception {
User user = mapper.findById(41);
System.out.println(user);
in.close();
}
@Test
public void testInsert() throws Exception {
User user = new User();
user.setUsername("美美");
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(41);
user.setUsername("小凤");
mapper.update(user);
session.commit();
}
@Test
public void testDelete() throws Exception {
mapper.delete(48);
session.commit();
}
// 第一种
@Test
public void testFindByName1() 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);
}
}
四、参数与结果映射详解
4.1 参数类型(parameterType)
4.1.1 简单类型
支持 int
、String
、long
等基本类型及包装类,XML 中使用 #{参数名}
或 #{value}
取值:
<select id="findById" resultType="User" parameterType="int">
SELECT * FROM user WHERE id=#{id} <!-- 参数为简单类型时,#{} 内可任意命名 -->
</select>
4.1.2 POJO 对象
直接传入实体类,XML 中通过 #{属性名}
取值:
public void insert(User user); // 传入 User 对象
<insert id="insert" parameterType="User">
INSERT INTO user (username) VALUES (#{username})
</insert>
4.1.3 包装对象(QueryVo)
用于封装多个查询条件或关联对象:
public class QueryVo implements Serializable {
// 自己属性
private String name;
// user属性
private User user;
// role属性
private Role role;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
<!--包装类测试查询-->
<select id="findByVo" parameterType="cn.tx.domain.QueryVo" resultType="cn.tx.domain.User">
SELECT * FROM user WHERE username=#{user.username}
</select>
4.2 结果类型(resultType 与 resultMap)
4.2.1 resultType 直接映射
当数据库字段名与实体类属性名一致时,直接指定 resultType
即可:
<select id="findAll" resultType="cn.tx.domain.User">
SELECT id, username, birthday FROM user
</select>
4.2.2 resultMap 自定义映射
字段名与属性名不匹配时,通过 resultMap
配置映射关系:
<select id="findUsers" resultMap="userMap">
select id _id,username _username,birthday _birthday,sex _sex,address _address from user
</select>
<!--
配置resultMap,用来进行数据封装
id="唯一的名称,用来被引用的"
type="进行封装数据的类型"
-->
<resultMap id="userMap" type="cn.tx.domain.User">
<!-- property:属性名,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>
五、核心配置文件(SqlMapConfig.xml)优化
5.1 外部属性文件管理
将数据库配置提取到 jdbc.properties
文件中:
# jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db
jdbc.username=root
jdbc.password=12345
在 SqlMapConfig.xml
中引用:
<configuration>
<!-- 一、定义property标签 -->
<!--<properties>-->
<!--<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!--<property name="jdbc.url" value="jdbc:mysql:///mybatis_db"/>-->
<!--<property name="jdbc.username" value="root"/>-->
<!--<property name="jdbc.password" value="12345"/>-->
<!--</properties>-->
<!-- 二、加载外部文件 -->
<properties resource="jdbc.properties"/>
<environments default="mysql">
<environment id="mysql">
<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}"/>
</dataSource>
</environment>
</environments>
</configuration>
5.2 类型别名(typeAliases)
简化全类名引用,两种配置方式:
5.2.1 单个类别名
<typeAliases>
<!-- 别名不区分大小写 -->
<typeAlias type="cn.tx.domain.User" alias="user"/>
</typeAliases>
使用时:
<select id="findAll" resultType="user"/> <!-- 替代全路径 -->
5.2.2 包扫描自动别名
<typeAliases>
<package name="cn.tx.domain"/> <!-- 包下所有类以类名小写作为别名 -->
</typeAliases>
例如 User
类别名为 user
,Order
类别名为 order
。
六、关键细节与注意事项
6.1 SQL 注入防范
- 优先使用
#{}
:预处理语句(PreparedStatement)会对参数进行转义,防止 SQL 注入。 - 谨慎使用
$
:直接拼接字符串,仅在动态表名/列名场景使用,需手动过滤特殊字符:<select id="findByColumn" resultType="User"> SELECT * FROM user WHERE ${column} = #{value} <!-- 危险!需确保 column 可控 --> </select>
6.2 事务管理
- MyBatis 默认不自动提交事务,增删改操作需调用
session.commit()
。 - 可通过配置
SqlSession
的autoCommit
参数开启自动提交:SqlSession session = factory.openSession(true); // 自动提交事务
6.3 日志配置
添加 log4j.properties
文件配置日志输出,方便调试 SQL:
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{ABSOLUTE} %5p %c{1}:%L - %m%n
# 打印 MyBatis 执行的 SQL
log4j.logger.cn.tx.mapper=DEBUG
七、总结
MyBatis 通过 ORM 映射和动态 SQL 机制,在保留 SQL 灵活性的同时减少了重复代码,适用于各类 Java 项目的持久层开发。核心学习路径包括:
- 掌握入门流程:环境搭建 → 实体类 → Mapper 接口与 XML → 配置文件 → 测试。
- 深入 CRUD 操作:理解参数传递、结果映射、事务管理。
- 优化配置:使用外部属性、类型别名、日志系统提升开发效率。
- 安全与性能:避免 SQL 注入,合理使用连接池(
POOLED
)和缓存。
通过以上实战与知识点总结,可快速上手 MyBatis 并应用于实际项目中。