MyBatis 进阶应用
分页插件、逆向工程、Spring 集成、批处理、性能优化
分页插件
PageHelper 集成
Maven 依赖
xml
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>6.1.0</version>
</dependency>配置
xml
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
<property name="supportMethodsArguments" value="true"/>
</plugin>
</plugins>Spring Boot 配置
yaml
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql使用
java
// 方式一:静态方法
@Test
public void testPageHelper() {
PageHelper.startPage(1, 10); // 第1页,每页10条
List<User> list = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(list);
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("当前页数据:" + pageInfo.getList().size());
}
// 方式二:PageRowBounds
@Test
public void testPageRowBounds() {
try (SqlSession session = sqlSessionFactory.openSession()) {
RowBounds rowBounds = new RowBounds(0, 10);
List<User> list = session.selectList("selectAll", null, rowBounds);
}
}分页参数
| 参数 | 说明 |
|---|---|
pageNum | 当前页码 |
pageSize | 每页条数 |
total | 总记录数 |
pages | 总页数 |
hasNextPage | 是否有下一页 |
hasPreviousPage | 是否有上一页 |
navigatePages | 导航页码数 |
自定义分页插件
java
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class,
BoundSql.class, CacheKey.class})
})
public class CustomPageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
BoundSql boundSql = (BoundSql) args[4];
// 获取分页参数
Page<?> page = PageUtil.getPage();
if (page == null) {
return invocation.proceed();
}
// SQL 重写:添加 LIMIT
MappedStatement ms = (MappedStatement) args[0];
String originalSql = boundSql.getSql();
String countSql = "SELECT COUNT(*) " +
originalSql.substring(originalSql.toUpperCase().indexOf("FROM"));
String pageSql = originalSql + " LIMIT " +
page.getStartRow() + ", " + page.getPageSize();
// 执行分页查询
BoundSql newBoundSql = new BoundSql(
ms.getConfiguration(), pageSql,
boundSql.getParameterMappings(),
boundSql.getParameterObject());
// ...
return invocation.proceed();
}
}逆向工程
MyBatis Generator
Maven 插件
xml
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.2</version>
<configuration>
<configurationFile>generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>配置文件 (generatorConfig.xml)
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动 -->
<classPathEntry location="mysql-connector-java.jar"/>
<context id="Mysql">
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_db"
userId="root" password="password"/>
<!-- Java 类型解析 -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成实体类 -->
<javaModelGenerator targetPackage="com.example.entity"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成 Mapper 接口 -->
<javaClientGenerator type="ANNOTATEDMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 生成表映射 -->
<table tableName="user" domainObjectName="User">
<generatedKey column="id" sqlStatement="MYSQL"
identity="true"/>
</table>
<table tableName="orders" domainObjectName="Order"/>
</context>
</generatorConfiguration>运行
bash
mvn mybatis-generator:generate生成结果
com.example.entity
├── User.java # 实体类
└── Order.java # 实体类
com.example.mapper
├── UserMapper.java # Mapper 接口
├── UserMapper.xml # Mapper XML
├── OrderMapper.java
└── OrderMapper.xmlSpring 集成
依赖
xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>配置 (application.yml)
yaml
mybatis:
# Mapper XML 路径
mapper-locations: classpath:mapper/*.xml
# 类型别名包
type-aliases-package: com.example.entity
# 配置
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
cache-enabled: true
lazy-loading-enabled: true
aggressive-lazy-loading: false
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_db?useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: password
hikari:
maximum-pool-size: 10
minimum-idle: 5Mapper 扫描
java
// 方式一:@MapperScan 注解
@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 方式二:每个 Mapper 加 @Mapper
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
}SqlSession 注入
java
// Mapper 通过 SqlSession 获取
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectById(1L);
}
// Spring 环境自动注入
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUser(Long id) {
return userMapper.selectById(id);
}
}批处理
JDBC 批处理
xml
<!-- UserMapper.xml -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO user (name, email) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.email})
</foreach>
</insert>ExecutorType.BATCH
java
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (int i = 0; i < 1000; i++) {
User user = new User();
user.setName("user" + i);
user.setEmail("user" + i + "@example.com");
mapper.insert(user);
}
session.commit(); // 一次性执行所有批量操作
}性能对比
| 方式 | 1000 条数据耗时 |
|---|---|
| 单条插入 | ~3000ms |
| 批量 INSERT VALUES | ~50ms |
| ExecutorType.BATCH | ~80ms |
性能优化
1. 延迟加载优化
xml
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 只触发关联对象时才加载 -->
<setting name="lazyLoadTriggerMethods" value="getUser,getOrders"/>
</settings>2. 预热缓存
java
// 应用启动时预热缓存
@Override
public void afterPropertiesSet() {
// 查询常用数据,填充缓存
userMapper.selectAll();
}3. 防止 N+1 查询
xml
<!-- 嵌套结果优于嵌套查询 -->
<resultMap id="UserWithOrders" type="User">
<association property="user" javaType="User">
<!-- 嵌套结果,一次查询 -->
</association>
</resultMap>
<!-- 而非嵌套查询(N+1) -->
<resultMap id="UserWithOrders2" type="User">
<association column="user_id" select="selectUserById"/>
<!-- 先查 user,再查 orders,N+1 问题 -->
</resultMap>4. 使用抓取策略
java
// 只查询,不触发关联查询
User user = session.selectOne("selectUser", 1L,
new FetchPolicy(FetchType.LAZY)); // 自定义策略5. 批量操作优化
yaml
# 连接池配置
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000常见问题
1. 找不到 Mapper
xml
<!-- 检查 mapper-locations 路径 -->
mybatis:
mapper-locations: classpath:mapper/*.xml # 或 classpath*:2. 列名与属性名不匹配
xml
<!-- 方案一:开启驼峰映射 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 方案二:使用 resultMap -->
<resultMap id="ResultMap" type="User">
<result column="user_name" property="name"/>
</resultMap>3. 事务未提交
java
// 确保 commit
session.insert("insert", user);
session.commit(); // 或 autoCommit=true4. 性能问题
java
// 添加索引
// 分页查询
// 减少关联查询深度
// 使用懒加载相关页面
- **Java\MyBatis\MyBatis-完全指南** - 返回总览
- **Java\MyBatis\MyBatis-核心配置** - 配置详解
- **Java\MyBatis\MyBatis-Mapper映射** - SQL 映射
- **Java\MyBatis\MyBatis-关联查询** - 复杂查询
- **Java\MyBatis\MyBatis-缓存机制** - 缓存配置
#标签 #mybatis #分页 #逆向工程 #spring #batch #performance