Java MyBatis 框架
MyBatis 是一个优秀的持久层框架,它简化了 Java 应用程序与关系型数据库之间的交互。MyBatis 通过 XML 或注解的方式将 SQL 语句与 Java 对象进行映射,避免了传统 JDBC 编程中的大量样板代码。
MyBatis 的核心特性
1. SQL 与代码分离
MyBatis 允许开发者将 SQL 语句从 Java 代码中分离出来,存储在 XML 文件或注解中,使得代码更加清晰易维护。2. 自动映射
MyBatis 能够自动将数据库查询结果映射到 Java 对象,大大减少了数据转换的工作量。3. 动态 SQL
MyBatis 提供了强大的动态 SQL 功能,可以根据不同条件生成不同的 SQL 语句。4. 缓存机制
MyBatis 内置了一级缓存和二级缓存,可以有效提高应用程序的性能。MyBatis 的基本架构
1. 核心组件
- SqlSessionFactory:创建 SqlSession 的工厂类
- SqlSession:执行 SQL 命令的主要接口
- Mapper 接口:定义数据库操作的方法
- Mapper XML:包含 SQL 语句的配置文件
2. 工作流程
- 应用程序通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory
- SqlSessionFactory 创建 SqlSession
- SqlSession 获取 Mapper 接口的实例
- 调用 Mapper 方法执行数据库操作
- 提交事务并关闭 SqlSession
MyBatis 的配置
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>
<environments default="development">
<environment>
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment>
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
2. Mapper 文件示例
实例
<?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">
<select id="getUserById" parameterType="int" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO users(name, email) VALUES(#{name}, #{email})
</insert>
</mapper>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<select id="getUserById" parameterType="int" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO users(name, email) VALUES(#{name}, #{email})
</insert>
</mapper>
MyBatis 的基本使用
1. 获取 SqlSession
实例
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
2. 执行查询
实例
// 方式1:直接使用 SqlSession
User user = session.selectOne("com.example.mapper.UserMapper.getUserById", 1);
// 方式2:通过 Mapper 接口
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
User user = session.selectOne("com.example.mapper.UserMapper.getUserById", 1);
// 方式2:通过 Mapper 接口
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
3. 执行插入
实例
User newUser = new User();
newUser.setName("张三");
newUser.setEmail("zhangsan@example.com");
int rows = mapper.insertUser(newUser);
session.commit(); // 提交事务
newUser.setName("张三");
newUser.setEmail("zhangsan@example.com");
int rows = mapper.insertUser(newUser);
session.commit(); // 提交事务
MyBatis 的动态 SQL
MyBatis 提供了多种动态 SQL 元素,可以根据不同条件动态生成 SQL 语句:
1. if 元素
实例
<select parameterType="map" resultType="User">
SELECT * FROM users
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</select>
SELECT * FROM users
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</select>
2. choose/when/otherwise 元素
实例
<select parameterType="map" resultType="User">
SELECT * FROM users
WHERE status = 'ACTIVE'
<choose>
<when test="name != null">
AND name like #{name}
</when>
<when test="email != null">
AND email = #{email}
</when>
<otherwise>
AND 1=1
</otherwise>
</choose>
</select>
SELECT * FROM users
WHERE status = 'ACTIVE'
<choose>
<when test="name != null">
AND name like #{name}
</when>
<when test="email != null">
AND email = #{email}
</when>
<otherwise>
AND 1=1
</otherwise>
</choose>
</select>
3. foreach 元素
实例
<select parameterType="list" resultType="User">
SELECT * FROM users
WHERE id IN
<foreach item="id" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
</select>
SELECT * FROM users
WHERE id IN
<foreach item="id" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
</select>
MyBatis 的关联查询
1. 一对一关联
实例
<resultMap type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<association property="address" javaType="Address">
<id property="id" column="address_id"/>
<result property="street" column="street"/>
<result property="city" column="city"/>
</association>
</resultMap>
<select resultMap="userWithAddress">
SELECT
u.id as user_id, u.name as user_name,
a.id as address_id, a.street, a.city
FROM users u
LEFT JOIN addresses a ON u.address_id = a.id
WHERE u.id = #{id}
</select>
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<association property="address" javaType="Address">
<id property="id" column="address_id"/>
<result property="street" column="street"/>
<result property="city" column="city"/>
</association>
</resultMap>
<select resultMap="userWithAddress">
SELECT
u.id as user_id, u.name as user_name,
a.id as address_id, a.street, a.city
FROM users u
LEFT JOIN addresses a ON u.address_id = a.id
WHERE u.id = #{id}
</select>
2. 一对多关联
实例
<resultMap type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderDate" column="order_date"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
<select resultMap="userWithOrders">
SELECT
u.id as user_id, u.name as user_name,
o.id as order_id, o.order_date, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderDate" column="order_date"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
<select resultMap="userWithOrders">
SELECT
u.id as user_id, u.name as user_name,
o.id as order_id, o.order_date, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
MyBatis 的缓存机制
1. 一级缓存
一级缓存是 SqlSession 级别的缓存,默认开启。在同一个 SqlSession 中,相同的查询只会执行一次 SQL。
特性:
作用范围:
SqlSession
级别(默认开启,不可关闭)生命周期:随
SqlSession
创建而创建,随SqlSession
关闭而销毁触发清空的情况:
执行
INSERT/UPDATE/DELETE
操作调用
sqlSession.clearCache()
执行事务回滚
配置不同的
Statement ID
(即使SQL相同)
注意事项:
实例
// 示例:相同查询在同一个SqlSession中只执行一次
User user1 = sqlSession.selectOne("getUserById", 1); // 执行SQL
User user2 = sqlSession.selectOne("getUserById", 1); // 从缓存获取
User user1 = sqlSession.selectOne("getUserById", 1); // 执行SQL
User user2 = sqlSession.selectOne("getUserById", 1); // 从缓存获取
2. 二级缓存
二级缓存是 Mapper 级别的缓存,多个 SqlSession 可以共享缓存。需要在配置文件中开启:
<settings> <setting name="cacheEnabled" value="true"/> <!-- 默认true可省略 --> </settings>
并在 Mapper 文件中配置:
<cache eviction="LRU" <!-- 淘汰策略(默认LRU) --> flushInterval="60000" <!-- 刷新间隔(毫秒) --> size="512" <!-- 缓存对象数量 --> readOnly="true"/> <!-- 只读模式(性能更优) -->
缓存策略对比:
策略 | 描述 | 适用场景 |
---|---|---|
LRU | 最近最少使用 | 常规使用 |
FIFO | 先进先出 | 固定顺序访问 |
SOFT | 软引用 | 内存敏感场景 |
WEAK | 弱引用 | 内存极度敏感 |
MyBatis 与 Spring 集成
1. 依赖配置(现代 Spring Boot 推荐)
实例
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Starter 整合方案(推荐) -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- 或传统 Spring 项目使用 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<!-- 数据库驱动根据实际选择 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
<dependencies>
<!-- Spring Boot Starter 整合方案(推荐) -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- 或传统 Spring 项目使用 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<!-- 数据库驱动根据实际选择 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
2. 配置方案(三种主流方式)
方案一:Spring Boot 自动配置(最简单)
实例
# application.yml
mybatis:
mapper-locations: classpath*:mapper/**/*.xml
type-aliases-package: com.example.model
configuration:
map-underscore-to-camel-case: true # 自动驼峰转换
mybatis:
mapper-locations: classpath*:mapper/**/*.xml
type-aliases-package: com.example.model
configuration:
map-underscore-to-camel-case: true # 自动驼峰转换
方案二:Java Config 全配置(精细化控制)
实例
@Configuration
public class MyBatisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage("com.example.model");
// 自定义配置(示例)
org.apache.ibatis.session.Configuration config = new Configuration();
config.setMapUnderscoreToCamelCase(true);
config.setDefaultFetchSize(100);
factory.setConfiguration(config);
// 插件配置(示例)
factory.setPlugins(
new MyBatisInterceptor(),
new PaginationInterceptor()
);
return factory.getObject();
}
@Bean
public MapperScannerConfigurer mapperScanner() {
MapperScannerConfigurer scanner = new MapperScannerConfigurer();
scanner.setBasePackage("com.example.mapper");
scanner.setAnnotationClass(Repository.class); // 限定注解扫描
return scanner;
}
}
public class MyBatisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage("com.example.model");
// 自定义配置(示例)
org.apache.ibatis.session.Configuration config = new Configuration();
config.setMapUnderscoreToCamelCase(true);
config.setDefaultFetchSize(100);
factory.setConfiguration(config);
// 插件配置(示例)
factory.setPlugins(
new MyBatisInterceptor(),
new PaginationInterceptor()
);
return factory.getObject();
}
@Bean
public MapperScannerConfigurer mapperScanner() {
MapperScannerConfigurer scanner = new MapperScannerConfigurer();
scanner.setBasePackage("com.example.mapper");
scanner.setAnnotationClass(Repository.class); // 限定注解扫描
return scanner;
}
}
方案三:XML 传统配置(兼容旧项目)
实例
<!-- applicationContext.xml -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
</bean>
3. 事务管理配置
实例
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 可定义事务模板
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager manager) {
return new TransactionTemplate(manager);
}
}
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 可定义事务模板
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager manager) {
return new TransactionTemplate(manager);
}
}
4. 使用规范与最佳实践
Mapper 接口定义:
实例
@Repository // 明确DAO层注解
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(@Param("id") int id);
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("INSERT INTO users(name) VALUES(#{name})")
int insert(User user);
}
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(@Param("id") int id);
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("INSERT INTO users(name) VALUES(#{name})")
int insert(User user);
}
Service 层示例:
实例
@Service
@Transactional(readOnly = true) // 默认只读
public class UserService {
private final UserMapper userMapper;
@Autowired // 构造器注入推荐
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Transactional // 写操作单独开启事务
public User createUser(String name) {
User user = new User();
user.setName(name);
userMapper.insert(user);
return userMapper.selectById(user.getId());
}
}
@Transactional(readOnly = true) // 默认只读
public class UserService {
private final UserMapper userMapper;
@Autowired // 构造器注入推荐
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Transactional // 写操作单独开启事务
public User createUser(String name) {
User user = new User();
user.setName(name);
userMapper.insert(user);
return userMapper.selectById(user.getId());
}
}
5. 高级功能集成
动态数据源配置:
实例
@Bean
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource ds = new DynamicDataSource();
ds.setDefaultTargetDataSource(primaryDataSource());
ds.setTargetDataSources(Map.of(
"master", primaryDataSource(),
"slave", secondaryDataSource()
));
return ds;
}
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource ds = new DynamicDataSource();
ds.setDefaultTargetDataSource(primaryDataSource());
ds.setTargetDataSources(Map.of(
"master", primaryDataSource(),
"slave", secondaryDataSource()
));
return ds;
}
多数据源事务管理:
实例
@Bean
public PlatformTransactionManager transactionManager() {
return new ChainedTransactionManager(
new JpaTransactionManager(entityManagerFactory()),
new DataSourceTransactionManager(dataSource())
);
}
public PlatformTransactionManager transactionManager() {
return new ChainedTransactionManager(
new JpaTransactionManager(entityManagerFactory()),
new DataSourceTransactionManager(dataSource())
);
}
MyBatis 的最佳实践
- 使用 Mapper 接口而不是直接使用 SqlSession:这样代码更加类型安全,也更易于维护。
- 合理使用动态 SQL:避免 SQL 注入风险,保持 SQL 的可读性。
- 批量操作:对于大批量数据操作,使用批量处理提高性能。
- 合理使用缓存:根据业务需求配置合适的缓存策略。
- SQL 优化:在 Mapper XML 中编写高效的 SQL 语句。
MyBatis 是一个灵活、强大的 ORM 框架,通过合理使用可以大大提高开发效率和应用程序性能。希望这篇文章能帮助你理解和使用 MyBatis 框架。
点我分享笔记