【MyBatis-2】深入浅出MyBatis开发流程:从入门到实战
在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 configurationPUBLIC "-//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 mapperPUBLIC "-//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 userWHERE 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 userSET 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 {@Testpublic 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();}}@Testpublic 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);}}@Testpublic 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_timeFROM user uLEFT JOIN orders o ON u.id = o.user_idWHERE 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的各种特性,遵循最佳实践,才能充分发挥其优势,构建出高效、可维护的数据访问层。