Mybatis学习笔记
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原生类型、接口和 Java POJO(Plain Old Java Objects)为数据库中的记录。
1. 什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原生类型、接口和 Java POJO(Plain Old Java Objects)为数据库中的记录。
主要特点:
- SQL 与代码分离:将 SQL 语句放在 XML 文件或注解中,避免硬编码。
- 灵活的映射机制:支持复杂的关联查询和结果映射。
- 轻量级:相比 Hibernate 等全功能 ORM 框架,MyBatis 更轻量、灵活。
- 支持存储过程:可以方便地调用数据库存储过程。
2. 核心概念
2.1 核心组件
- SqlSessionFactory:创建 SqlSession 的工厂类,是 MyBatis 的核心配置对象。
- SqlSession:提供执行 SQL 方法的会话对象,类似于 JDBC 中的 Connection。
- Mapper 接口:定义数据库操作方法的接口,由 MyBatis 自动生成实现类。
- 映射文件(XML)或注解:定义 SQL 语句和映射规则。
2.2 工作流程
- 读取 MyBatis 配置文件(
mybatis-config.xml
)和映射文件。 - 创建 SqlSessionFactory 实例。
- 通过 SqlSessionFactory 获取 SqlSession。
- 使用 SqlSession 执行 SQL 操作。
- 关闭 SqlSession。
3. 环境搭建
3.1 添加依赖(Maven)
xml
<!-- MyBatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- 数据库驱动(以 MySQL 为例) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- 日志(可选,方便调试) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
3.2 创建 MyBatis 配置文件 mybatis-config.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="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/test?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件位置 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
3.3 创建实体类
java
public class User {
private Long id;
private String username;
private String email;
private Integer age;
// 构造方法、Getter 和 Setter 略
}
4. 使用 XML 映射文件
4.1 创建 Mapper 接口
java
public interface UserMapper {
User selectById(Long id);
List<User> selectAll();
int insert(User user);
int update(User user);
int deleteById(Long id);
}
4.2 创建映射文件 UserMapper.xml
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="com.example.mapper.UserMapper">
<!-- 结果映射 -->
<resultMap id="BaseResultMap" type="com.example.entity.User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
</resultMap>
<!-- 查询单个用户 -->
<select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 查询所有用户 -->
<select id="selectAll" resultMap="BaseResultMap">
SELECT * FROM user
</select>
<!-- 插入用户 -->
<insert id="insert" parameterType="com.example.entity.User">
INSERT INTO user (username, email, age)
VALUES (#{username}, #{email}, #{age})
</insert>
<!-- 更新用户 -->
<update id="update" parameterType="com.example.entity.User">
UPDATE user
SET username = #{username},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>
<!-- 删除用户 -->
<delete id="deleteById" parameterType="java.lang.Long">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
5. 使用注解方式
5.1 简单 SQL 注解
java
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
@Select("SELECT * FROM user")
List<User> selectAll();
@Insert("INSERT INTO user (username, email, age) VALUES (#{username}, #{email}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE user SET username=#{username}, email=#{email}, age=#{age} WHERE id=#{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteById(Long id);
}
5.2 复杂映射注解
java
public interface UserMapper {
@Results({
@Result(property = "id", column = "id", id = true),
@Result(property = "username", column = "username"),
@Result(property = "email", column = "email"),
@Result(property = "age", column = "age")
})
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
}
6. 使用 MyBatis
6.1 创建 SqlSessionFactory
java
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
6.2 执行数据库操作
java
public class UserService {
public User getUserById(Long id) {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
return mapper.selectById(id);
}
}
public List<User> getAllUsers() {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
return mapper.selectAll();
}
}
public void addUser(User user) {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.insert(user);
session.commit(); // 提交事务
}
}
public void updateUser(User user) {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.update(user);
session.commit();
}
}
public void deleteUser(Long id) {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteById(id);
session.commit();
}
}
}
7. 高级特性
7.1 动态 SQL
MyBatis 提供了强大的动态 SQL 功能,可以根据条件动态生成 SQL 语句。
xml
<select id="findUsers" parameterType="map" resultType="User">
SELECT * FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="age != null">
AND age >= #{age}
</if>
</where>
</select>
7.2 关联查询
处理一对多、多对一关系。
xml
<resultMap id="UserWithOrdersMap" type="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<collection property="orders" ofType="Order">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
</collection>
</resultMap>
<select id="getUserWithOrders" resultMap="UserWithOrdersMap">
SELECT
u.id AS user_id,
u.username,
o.id AS order_id,
o.order_number
FROM user u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
7.3 分页查询
java
// 使用 RowBounds 进行分页(简单但性能较低)
List<User> selectAll(RowBounds rowBounds);
// 调用时
RowBounds rowBounds = new RowBounds(10, 20); // 从第10条开始,取20条
List<User> users = mapper.selectAll(rowBounds);
7.4 插件机制
自定义插件实现分页、性能监控等功能。
java
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 分页逻辑
return invocation.proceed();
}
}
8. 事务管理
8.1 自动提交事务
java
try (SqlSession session = sqlSessionFactory.openSession(true)) {
// 自动提交事务
UserMapper mapper = session.getMapper(UserMapper.class);
// 执行数据库操作
}
8.2 手动提交事务
java
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
// 执行多个数据库操作
session.commit(); // 提交事务
} catch (Exception e) {
session.rollback(); // 回滚事务
throw e;
} finally {
session.close();
}
9. 与 Spring 集成
9.1 添加依赖
xml
<dependency>
<groupId>org.mybatis.spring</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
9.2 配置 Spring 和 MyBatis
java
@Configuration
@MapperScan("com.example.mapper") // 扫描 Mapper 接口
public class MyBatisConfig {
@Bean
public DataSource dataSource() {
// 配置数据源
return new DriverManagerDataSource("jdbc:mysql://localhost:3306/test", "root", "password");
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mappers/*.xml"));
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
10. 什么是 ResultMap?
ResultMap 是 MyBatis 中最强大的特性之一,用于解决数据库表结构与 Java 对象结构之间的不匹配问题。例如:
- 数据库列名与 Java 属性名不一致(如
user_name
→username
)。 - 复杂的关联查询(如一对一、一对多关系)。
- 自定义类型转换(如将数据库的
INT
转为 Java 的Boolean
)。
1. 代码逐行解析
xml
<resultMap id="BaseResultMap" type="com.example.entity.User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
</resultMap>
1.1 根标签 <resultMap>
id
:结果映射的唯一标识符,用于在 SQL 语句中引用(如<select resultMap="BaseResultMap">
)。type
:映射的目标 Java 类的全限定名(这里是User
类)。
1.2 子标签 <id>
和 <result>
-
共同点:
column
:数据库表的列名。property
:Java 对象的属性名(通过 getter/setter 访问)。jdbcType
:列的 JDBC 类型(如BIGINT
、VARCHAR
),用于处理空值等情况。
-
区别:
<id>
:表示主键列,有助于提高 MyBatis 的缓存效率。<result>
:表示普通列。
2. 映射原理示例
假设执行 SQL:
sql
SELECT id, username, email, age FROM user WHERE id = 1;
MyBatis 会根据 BaseResultMap
将结果映射为 User
对象:
数据库列 | Java 属性 | 映射后效果 |
---|---|---|
id |
id |
user.setId(rs.getLong("id")) |
username |
username |
user.setUsername(rs.getString("username")) |
email |
email |
user.setEmail(rs.getString("email")) |
age |
age |
user.setAge(rs.getInt("age")) |
3. 为什么需要 ResultMap?
场景 1:列名与属性名不一致
若数据库列名为 user_name
,而 Java 属性为 username
,需显式映射:
xml
<result column="user_name" property="username"/>
场景 2:复杂关联查询
例如,查询 User
及其关联的 Order
列表:
xml
<resultMap id="UserWithOrders" type="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<!-- 一对多关联:一个用户对应多个订单 -->
<collection property="orders" ofType="Order">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
</collection>
</resultMap>
4. 简化写法:自动映射
如果数据库列名与 Java 属性名完全一致(如 id
→ id
),MyBatis 支持自动映射,无需编写 resultMap
:
xml
<select id="selectById" resultType="com.example.entity.User">
SELECT * FROM user WHERE id = #{id}
</select>
但建议复杂场景下仍使用 resultMap
以提高可读性和灵活性。
11.数据库操作映射
MyBatis 中使用 XML 配置文件定义的 数据库操作映射,包含了增删改查(CRUD)的基本操作。下面我会逐行解释每个 SQL 映射的含义和工作原理。
1. 查询单个用户
xml
<select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
关键属性:
id
:映射的唯一标识符,对应 Mapper 接口中的方法名(如User selectById(Long id)
)。parameterType
:输入参数的类型(这里是Long
)。resultMap
:指定结果集的映射规则(引用之前定义的BaseResultMap
)。
执行流程:
- Mapper 接口调用
selectById(1L)
。 - MyBatis 解析 SQL:
SELECT * FROM user WHERE id = ?
,并将参数1L
绑定到占位符?
。 - 执行 SQL,返回结果集。
- 根据
BaseResultMap
将结果集映射为User
对象。
2. 查询所有用户
xml
<select id="selectAll" resultMap="BaseResultMap">
SELECT * FROM user
</select>
特点:
- 无
parameterType
:因为不需要输入参数。 - 返回列表:对应 Mapper 接口中的
List<User> selectAll()
。
执行流程:
- 执行
SELECT * FROM user
。 - 将每行结果映射为
User
对象。 - 返回
User
列表。
3. 插入用户
xml
<insert id="insert" parameterType="com.example.entity.User">
INSERT INTO user (username, email, age)
VALUES (#{username}, #{email}, #{age})
</insert>
参数处理:
#{username}
:从User
对象中获取username
属性(通过user.getUsername()
)。- 自动类型转换:MyBatis 会根据属性类型(如
String
、Integer
)自动设置 JDBC 参数。
执行流程:
- 传入
User
对象(如new User("Alice", "alice@example.com", 25)
)。 - 解析 SQL:
INSERT INTO user (username, email, age) VALUES (?, ?, ?)
。 - 绑定参数:
? = "Alice", "alice@example.com", 25
。 - 执行插入,返回受影响的行数(
int
)。
4. 更新用户
xml
<update id="update" parameterType="com.example.entity.User">
UPDATE user
SET username = #{username},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>
关键逻辑:
- 动态参数:从
User
对象中提取属性值。 - 主键条件:通过
id
定位要更新的记录。
执行流程:
- 传入已修改的
User
对象(如id=1
,username="Bob"
)。 - 生成 SQL:
UPDATE user SET username=?, email=?, age=? WHERE id=?
。 - 执行更新,返回成功更新的行数。
5. 删除用户
xml
<delete id="deleteById" parameterType="java.lang.Long">
DELETE FROM user WHERE id = #{id}
</delete>
执行流程:
- 传入
id
(如1L
)。 - 生成 SQL:
DELETE FROM user WHERE id = ?
。 - 执行删除,返回受影响的行数。
6. 关键语法解析
#{}
与 ${}
的区别:
#{}
(推荐):- 预编译处理:SQL 会被编译为
SELECT * FROM user WHERE id = ?
,防止 SQL 注入。 - 类型安全:自动处理类型转换(如
Long
→BIGINT
)。
- 预编译处理:SQL 会被编译为
${}
(慎用):- 直接替换:SQL 会被替换为
SELECT * FROM user WHERE id = 1
,存在 SQL 注入风险。 - 适用场景:动态表名、列名(如
${tableName}
)。
- 直接替换:SQL 会被替换为
参数传递方式:
- 单个参数:直接使用
#{paramName}
,如#{id}
。 - 多个参数:
- 使用
@Param
注解(接口方法中):java
User selectUser(@Param("id") Long id, @Param("name") String name);
xml
WHERE id = #{id} AND username = #{name}
- 使用对象(如
User
):通过属性名访问(如#{username}
)。
- 使用
mybatis-spring-boot-starter
mybatis-spring-boot-starter
是 MyBatis 和 Spring Boot 的集成模块,它提供了自动配置功能,使得在 Spring Boot 项目中使用 MyBatis 更加方便。这个依赖会自动配置 MyBatis 的相关组件,包括 SqlSessionFactory
、SqlSessionTemplate
等,并且会自动扫描指定包下的 Mapper 接口。
特点
-
自动配置:提供了自动配置功能,减少了手动配置的工作量。
-
集成 Spring Boot:与 Spring Boot 的自动配置机制无缝集成。
-
简化开发:提供了
@MapperScan
注解,方便扫描 Mapper 接口。 -
依赖管理:管理了 MyBatis 和 Spring 相关的依赖,确保版本兼容。
更多推荐
所有评论(0)