在Java持久层框架中,MyBatis以其灵活性和简单性赢得了广大开发者的青睐。相比于Hibernate等全自动ORM框架,MyBatis提供了更细粒度的SQL控制,同时减少了大量重复的JDBC代码。本文将详细介绍MyBatis的开发流程,帮助开发者快速掌握这一强大工具。
1. MyBatis简介
MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的工作,可以通过简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects)映射成数据库中的记录。
核心特点:
- 轻量级:框架本身非常小巧,没有第三方依赖
- SQL与代码分离:SQL写在XML文件中,与Java代码解耦
- 灵活的结果映射:支持将查询结果灵活地映射到各种对象结构
- 动态SQL:提供强大的动态SQL功能来满足复杂查询需求
2. 开发环境准备
2.1 项目依赖配置
对于Maven项目,在pom.xml中添加以下依赖:
<dependencies>
<!-- MyBatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- 数据库驱动,以MySQL为例 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- 其他可能需要的依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</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.6</version>
</dependency>
</dependencies>
2.2 数据库准备
以MySQL为例,创建一个简单的用户表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username_UNIQUE` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. MyBatis核心配置
3.1 全局配置文件(mybatis-config.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>
<!-- 配置日志实现 -->
<settings>
<setting name="logImpl" value="SLF4J"/>
<!-- 开启驼峰命名自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 类型别名 -->
<typeAliases>
<package name="com.example.model"/>
</typeAliases>
<!-- 环境配置 -->
<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/mybatis_demo?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<!-- 或者使用包扫描 -->
<!-- <package name="com.example.mapper"/> -->
</mappers>
</configuration>
3.2 实体类创建
package com.example.model;
import java.util.Date;
public class User {
private Integer id;
private String username;
private String password;
private String email;
private Date createTime;
// 省略getter/setter和toString方法
}
4. Mapper开发
MyBatis支持XML和注解两种方式编写Mapper,推荐使用XML方式,因为更清晰且支持更复杂的SQL。
4.1 Mapper接口
package com.example.mapper;
import com.example.model.User;
import java.util.List;
public interface UserMapper {
User selectById(Integer id);
List<User> selectAll();
int insert(User user);
int update(User user);
int delete(Integer id);
// 复杂查询示例
List<User> selectByCondition(User condition);
int batchInsert(List<User> users);
}
4.2 Mapper 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="User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="email" property="email"/>
<result column="create_time" property="createTime"/>
</resultMap>
<sql id="Base_Column_List">
id, username, password, email, create_time
</sql>
<select id="selectById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE id = #{id}
</select>
<select id="selectAll" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
</select>
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user(username, password, email)
VALUES(#{username}, #{password}, #{email})
</insert>
<update id="update" parameterType="User">
UPDATE user
SET username = #{username},
password = #{password},
email = #{email}
WHERE id = #{id}
</update>
<delete id="delete">
DELETE FROM user WHERE id = #{id}
</delete>
<!-- 动态SQL示例 -->
<select id="selectByCondition" parameterType="User" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
</select>
<!-- 批量插入 -->
<insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user(username, password, email)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.username}, #{item.password}, #{item.email})
</foreach>
</insert>
</mapper>
5. MyBatis API使用
5.1 获取SqlSessionFactory
package com.example.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException("初始化MyBatis失败", e);
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
5.2 基本CRUD操作示例
package com.example.demo;
import com.example.mapper.UserMapper;
import com.example.model.User;
import com.example.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class UserMapperTest {
@Test
public void testCRUD() {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
// 插入
User newUser = new User();
newUser.setUsername("testUser");
newUser.setPassword("123456");
newUser.setEmail("test@example.com");
mapper.insert(newUser);
System.out.println("插入成功,ID: " + newUser.getId());
// 查询
User user = mapper.selectById(newUser.getId());
System.out.println("查询结果: " + user);
// 更新
user.setEmail("updated@example.com");
mapper.update(user);
System.out.println("更新后的用户: " + mapper.selectById(user.getId()));
// 删除
mapper.delete(user.getId());
System.out.println("删除后查询: " + mapper.selectById(user.getId()));
session.commit();
}
}
@Test
public void testDynamicSQL() {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
// 条件查询
User condition = new User();
condition.setUsername("test");
List<User> users = mapper.selectByCondition(condition);
System.out.println("条件查询结果: " + users);
}
}
@Test
public void testBatchInsert() {
try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> userList = Arrays.asList(
new User(null, "user1", "pass1", "user1@example.com", new Date()),
new User(null, "user2", "pass2", "user2@example.com", new Date()),
new User(null, "user3", "pass3", "user3@example.com", new Date())
);
mapper.batchInsert(userList);
session.commit();
System.out.println("批量插入成功,生成的ID:");
userList.forEach(user -> System.out.println(user.getId()));
}
}
}
6. 高级特性
6.1 动态SQL
MyBatis提供了强大的动态SQL功能,可以根据不同条件拼接SQL语句:
<select id="selectByComplexCondition" parameterType="map" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="emailList != null and emailList.size > 0">
AND email IN
<foreach collection="emailList" item="email" open="(" separator="," close=")">
#{email}
</foreach>
</if>
<if test="createTimeStart != null">
AND create_time >= #{createTimeStart}
</if>
<if test="createTimeEnd != null">
AND create_time <![CDATA[ <= ]]> #{createTimeEnd}
</if>
</where>
<choose>
<when test="orderBy == 'username'">
ORDER BY username
</when>
<when test="orderBy == 'createTime'">
ORDER BY create_time
</when>
<otherwise>
ORDER BY id
</otherwise>
</choose>
</select>
6.2 结果映射
MyBatis支持复杂的结果映射,包括一对一、一对多、多对多关系:
<!-- 复杂结果映射示例 -->
<resultMap id="UserWithOrdersMap" type="User" extends="BaseResultMap">
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
<result property="createTime" column="order_create_time"/>
</collection>
</resultMap>
<select id="selectUserWithOrders" resultMap="UserWithOrdersMap">
SELECT
u.id, u.username, u.password, u.email, u.create_time,
o.id as order_id, o.order_no, o.amount, o.create_time as order_create_time
FROM user u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{userId}
</select>
6.3 缓存机制
MyBatis提供了一级缓存和二级缓存:
- 一级缓存:SqlSession级别的缓存,默认开启
- 二级缓存:Mapper级别的缓存,需要手动开启
<!-- 开启二级缓存 -->
<mapper namespace="com.example.mapper.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
...
</mapper>
7. 最佳实践
- SQL与Java代码分离:尽量将SQL写在XML文件中,保持代码清晰
- 使用ResultMap:避免直接使用字段名映射,提高可维护性
- 动态SQL:合理使用动态SQL处理复杂查询条件
- 批量操作:对于批量操作,使用
foreach
标签提高性能 - 日志配置:配置适当的日志级别,方便调试SQL
- 分页处理:对于大数据量查询,实现分页查询
- 事务管理:合理控制事务边界,避免长事务
8. 常见问题与解决方案
- SQL注入风险:
- 始终使用
#{}
参数绑定,避免使用${}
拼接SQL - 对用户输入进行严格校验
- 始终使用
- 性能问题:
- 使用连接池配置
- 合理使用缓存
- 优化复杂查询,添加适当索引
- 映射问题:
- 确保数据库字段名与Java属性名正确映射
- 使用
mapUnderscoreToCamelCase
配置自动转换下划线命名到驼峰命名
- 事务问题:
- 明确事务边界,及时提交或回滚
- 避免在事务中执行耗时操作
9. MyBatis与Spring集成
在实际项目中,MyBatis通常与Spring框架集成使用:
9.1 添加Spring集成依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
9.2 Spring Boot配置
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_demo
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.model
mybatis.configuration.map-underscore-to-camel-case=true
9.3 使用@MapperScan注解
@SpringBootApplication
@MapperScan("com.example.mapper")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
10. 总结
MyBatis作为一款优秀的持久层框架,在保持灵活性的同时提供了足够的便利性。通过本文的介绍,我们了解了MyBatis的核心配置、Mapper开发、动态SQL、结果映射等关键知识点。掌握这些内容后,开发者可以高效地使用MyBatis进行数据库操作开发。
在实际项目中,应根据具体需求合理使用MyBatis的各种特性,遵循最佳实践,才能充分发挥其优势,构建出高效、可维护的数据访问层。