MyBatis框架详解

MyBatis简介

MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects)为数据库中的记录。

与其他ORM框架相比,MyBatis更加灵活,允许开发者直接编写SQL语句,这在处理复杂查询时特别有用。

MyBatis的主要特性

  1. 简单易学:MyBatis的基本概念简单明了,上手快
  2. 灵活的SQL操作:支持自定义SQL语句,而不是自动生成
  3. 支持动态SQL:可以根据不同条件构建不同的SQL
  4. 支持存储过程:可以调用数据库中的存储过程
  5. 支持高级映射:包括一对一、一对多、多对一等复杂映射关系
  6. 缓存机制:提供一级缓存和二级缓存,提高查询效率

MyBatis的基本工作流程

  1. 通过SqlSessionFactoryBuilder从XML配置文件或Configuration类创建SqlSessionFactory
  2. 通过SqlSessionFactory获取SqlSession实例
  3. 使用SqlSession执行SQL操作
  4. 关闭SqlSession

MyBatis的基本使用

添加依赖

Maven项目中添加依赖:

1
2
3
4
5
6
7
8
9
10
11
12
<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>

配置MyBatis

创建mybatis-config.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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/testdb?useSSL=false&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>

<!-- 映射器配置 -->
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>

创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.example.entity;

public class User {
private Long id;
private String username;
private String email;
private Integer age;

// 构造函数、getter和setter方法
public User() {}

public User(Long id, String username, String email, Integer age) {
this.id = id;
this.username = username;
this.email = email;
this.age = age;
}

// getter和setter方法
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}

创建Mapper接口

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.mapper;

import com.example.entity.User;
import java.util.List;

public interface UserMapper {
User getUserById(Long id);
List<User> getAllUsers();
void insertUser(User user);
void updateUser(User user);
void deleteUser(Long id);
}

创建Mapper XML文件

创建UserMapper.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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="UserResultMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
</resultMap>

<!-- 根据ID查询用户 -->
<select id="getUserById" parameterType="long" resultMap="UserResultMap">
SELECT id, username, email, age FROM users WHERE id = #{id}
</select>

<!-- 查询所有用户 -->
<select id="getAllUsers" resultMap="UserResultMap">
SELECT id, username, email, age FROM users
</select>

<!-- 插入用户 -->
<insert id="insertUser" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (username, email, age)
VALUES (#{username}, #{email}, #{age})
</insert>

<!-- 更新用户 -->
<update id="updateUser" parameterType="com.example.entity.User">
UPDATE users SET
username = #{username},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>

<!-- 删除用户 -->
<delete id="deleteUser" parameterType="long">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>

使用MyBatis执行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.example;

import com.example.entity.User;
import com.example.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisExample {

public static void main(String[] args) {
try {
// 加载MyBatis配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 创建SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 获取Mapper
UserMapper userMapper = session.getMapper(UserMapper.class);

// 插入用户
User newUser = new User(null, "张三", "zhangsan@example.com", 28);
userMapper.insertUser(newUser);

// 提交事务
session.commit();
System.out.println("插入用户ID: " + newUser.getId());

// 查询用户
User user = userMapper.getUserById(newUser.getId());
System.out.println("查询到用户: " + user);

// 更新用户
user.setAge(30);
userMapper.updateUser(user);
session.commit();

// 查询所有用户
List<User> users = userMapper.getAllUsers();
System.out.println("所有用户:");
for (User u : users) {
System.out.println(u);
}

// 删除用户
userMapper.deleteUser(user.getId());
session.commit();
}

} catch (IOException e) {
e.printStackTrace();
}
}
}

MyBatis动态SQL

MyBatis的一个强大特性是支持动态SQL,可以根据不同条件生成不同的SQL语句。

if语句

1
2
3
4
5
6
<select id="findActiveUsersByName" resultType="User">
SELECT * FROM users WHERE status = 'ACTIVE'
<if test="name != null">
AND name LIKE #{name}
</if>
</select>

choose (when, otherwise)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="findUsersByCondition" resultType="User">
SELECT * FROM users WHERE 1=1
<choose>
<when test="name != null">
AND name LIKE #{name}
</when>
<when test="email != null">
AND email = #{email}
</when>
<otherwise>
AND created_date > CURRENT_DATE - 7
</otherwise>
</choose>
</select>

where语句

1
2
3
4
5
6
7
8
9
10
11
<select id="findUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>

set语句

1
2
3
4
5
6
7
8
9
<update id="updateUserSelective">
UPDATE users
<set>
<if test="username != null">username = #{username},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>

foreach语句

1
2
3
4
5
6
7
8
<select id="getUsersInList" resultType="User">
SELECT * FROM users
WHERE id IN
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

一对一关系映射

假设我们有用户和账户表,一个用户对应一个账户:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 一对一映射 -->
<resultMap id="userWithAccountMap" type="com.example.entity.User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>

<association property="account" javaType="com.example.entity.Account">
<id property="id" column="account_id"/>
<result property="accountNumber" column="account_number"/>
<result property="balance" column="balance"/>
</association>
</resultMap>

<select id="getUserWithAccount" parameterType="long" resultMap="userWithAccountMap">
SELECT u.id as user_id, u.username, u.email,
a.id as account_id, a.account_number, a.balance
FROM users u
LEFT JOIN accounts a ON u.id = a.user_id
WHERE u.id = #{id}
</select>

一对多关系映射

假设一个用户可以有多个订单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 一对多映射 -->
<resultMap id="userWithOrdersMap" type="com.example.entity.User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>

<collection property="orders" ofType="com.example.entity.Order">
<id property="id" column="order_id"/>
<result property="orderNumber" column="order_number"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>

<select id="getUserWithOrders" parameterType="long" resultMap="userWithOrdersMap">
SELECT u.id as user_id, u.username, u.email,
o.id as order_id, o.order_number, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>

MyBatis注解配置

除了XML配置,MyBatis还支持使用Java注解来配置Mapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.mapper;

import com.example.entity.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(Long id);

@Select("SELECT * FROM users")
List<User> getAllUsers();

@Insert("INSERT INTO users(username, email, age) VALUES(#{username}, #{email}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);

@Update("UPDATE users SET username = #{username}, email = #{email}, age = #{age} WHERE id = #{id}")
void updateUser(User user);

@Delete("DELETE FROM users WHERE id = #{id}")
void deleteUser(Long id);
}

MyBatis缓存机制

MyBatis提供了两级缓存机制:

一级缓存(会话级缓存)

一级缓存是SqlSession级别的缓存,默认开启,同一个SqlSession中执行相同的查询语句时会返回缓存的结果。

1
2
3
4
5
6
7
8
// 使用一级缓存
User user1 = userMapper.getUserById(1L); // 执行SQL查询
User user2 = userMapper.getUserById(1L); // 使用缓存结果,不执行SQL

// 以下操作会清空一级缓存
session.commit();
session.rollback();
session.close();

二级缓存(应用级缓存)

二级缓存是Mapper级别的缓存,可以被多个SqlSession共享。需要在配置文件中开启:

1
2
3
4
5
6
7
8
9
10
11
<!-- 开启二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

<!-- 在Mapper文件中配置缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>

与Spring集成

MyBatis可以很容易地与Spring框架集成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Spring配置文件 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/example/mapper/*.xml"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
</bean>

总结

MyBatis是一款功能强大且灵活的持久层框架,它的特点是提供了对SQL的完全控制,允许开发者编写原生SQL语句,同时还提供了强大的动态SQL功能和复杂的对象关系映射。对于需要对数据库操作有更精细控制的项目,MyBatis是一个很好的选择。

与Hibernate相比,MyBatis更加轻量级,学习曲线更平缓,而且在处理复杂查询和大量数据时往往表现更佳。与JDBC相比,MyBatis极大地简化了代码,同时提供了更强大的特性如缓存和动态SQL。