引言
在现代Java企业级应用开发中,Spring Boot与MyBatis Plus的组合已经成为主流的技术栈。Spring Boot以其"约定优于配置"的理念简化了Spring应用的初始搭建和开发过程,而MyBatis Plus则在MyBatis的基础上提供了更强大的功能和更便捷的使用方式。本文将深入探讨如何利用这两个框架实现高效的开发实践,从代码自动生成到性能调优,帮助开发者构建高性能、易维护的Java应用系统。
一、Spring Boot + MyBatis Plus 环境搭建与基础配置
1.1 项目依赖配置
在开始开发之前,首先需要配置正确的项目依赖。以下是一个典型的Spring Boot + MyBatis Plus项目依赖配置:
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2 核心配置文件
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
# 连接池配置
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyHikariCP
# MyBatis Plus 配置
mybatis-plus:
configuration:
# 开启驼峰命名转换
map-underscore-to-camel-case: true
# 日志配置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 主键策略
id-type: auto
# 表名前缀
table-prefix: t_
# 逻辑删除字段
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# Mapper扫描路径
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.example.demo.entity
二、代码自动生成:提升开发效率的关键
2.1 MyBatis Plus 代码生成器基础使用
代码生成器是提升开发效率的重要工具,可以自动生成Entity、Mapper、Service、Controller等基础代码,减少重复劳动。
// 代码生成器配置
@Configuration
public class CodeGenerator {
public static void main(String[] args) {
// 1. 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setAuthor("作者名");
globalConfig.setOutputDir("D://mybatis-plus//generator");
globalConfig.setFileOverride(true);
globalConfig.setActiveRecord(true);
globalConfig.setEnableCache(false);
globalConfig.setBaseResultMap(true);
globalConfig.setBaseColumnList(true);
// 2. 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL);
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("password");
// 3. 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
strategyConfig.setEntityLombokModel(true);
strategyConfig.setRestControllerStyle(true);
strategyConfig.setInclude("user", "order", "product"); // 指定生成的表名
// 4. 包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.demo");
packageConfig.setEntity("entity");
packageConfig.setMapper("mapper");
packageConfig.setService("service");
packageConfig.setServiceImpl("service.impl");
packageConfig.setController("controller");
// 5. 模板配置
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setEntity("/templates/entity.java");
templateConfig.setMapper("/templates/mapper.java");
templateConfig.setService("/templates/service.java");
templateConfig.setServiceImpl("/templates/serviceImpl.java");
templateConfig.setController("/templates/controller.java");
// 6. 执行生成
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(globalConfig);
autoGenerator.setDataSource(dataSourceConfig);
autoGenerator.setStrategy(strategyConfig);
autoGenerator.setPackageInfo(packageConfig);
autoGenerator.setTemplate(templateConfig);
autoGenerator.execute();
}
}
2.2 自定义代码生成器增强
为了满足更复杂的业务需求,我们可以自定义代码生成器,添加业务逻辑处理:
public class CustomCodeGenerator {
public static void generateWithCustomLogic() {
// 配置全局信息
GlobalConfig config = new GlobalConfig();
config.setAuthor("开发者");
config.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
config.setOpen(false);
config.setSwagger2(true);
// 数据源配置
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDbType(DbType.MYSQL);
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude("user", "role", "user_role");
// 自定义字段处理
strategy.setTableFillList(Arrays.asList(
new TableFill("create_time", FieldFill.INSERT),
new TableFill("update_time", FieldFill.INSERT_UPDATE)
));
// 包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.demo");
packageConfig.setEntity("entity");
packageConfig.setMapper("mapper");
packageConfig.setService("service");
packageConfig.setServiceImpl("service.impl");
packageConfig.setController("controller");
// 模板配置
TemplateConfig template = new TemplateConfig();
template.setEntity("/templates/entity.java.vm");
template.setMapper("/templates/mapper.java.vm");
template.setService("/templates/service.java.vm");
template.setServiceImpl("/templates/serviceImpl.java.vm");
template.setController("/templates/controller.java.vm");
AutoGenerator generator = new AutoGenerator();
generator.setGlobalConfig(config);
generator.setDataSource(dataSource);
generator.setStrategy(strategy);
generator.setPackageInfo(packageConfig);
generator.setTemplate(template);
generator.execute();
}
}
三、分页查询优化:处理大数据量场景
3.1 基础分页查询实现
分页查询是Web应用中的常见需求,MyBatis Plus提供了便捷的分页查询功能:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* 基础分页查询
*/
public IPage<User> getUserPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, null);
}
/**
* 带条件的分页查询
*/
public IPage<User> getUserPageByCondition(String name, int current, int size) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(name)) {
wrapper.like("name", name);
}
return userMapper.selectPage(page, wrapper);
}
}
3.2 高性能分页查询优化
对于大数据量的分页查询,需要进行优化以避免性能问题:
@Service
public class OptimizedUserService {
@Autowired
private UserMapper userMapper;
/**
* 优化的分页查询 - 使用游标分页
*/
public PageResult<User> getUserPageWithCursor(Long lastId, int size) {
PageResult<User> result = new PageResult<>();
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 使用游标分页,避免OFFSET过大导致的性能问题
if (lastId != null) {
wrapper.gt("id", lastId);
}
wrapper.orderByAsc("id");
wrapper.last("LIMIT " + size);
List<User> users = userMapper.selectList(wrapper);
result.setRecords(users);
result.setHasMore(users.size() == size);
return result;
}
/**
* 带缓存的分页查询
*/
@Cacheable(value = "userPage", key = "#current + '_' + #size + '_' + #condition")
public IPage<User> getUserPageWithCache(String condition, int current, int size) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(condition)) {
wrapper.like("name", condition);
}
return userMapper.selectPage(page, wrapper);
}
/**
* 预估总记录数的分页查询
*/
public PageResult<User> getUserPageWithCount(String condition, int current, int size) {
PageResult<User> result = new PageResult<>();
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(condition)) {
wrapper.like("name", condition);
}
// 先查询总记录数
Long total = userMapper.selectCount(wrapper);
page.setTotal(total);
// 查询分页数据
List<User> records = userMapper.selectPage(page, wrapper).getRecords();
result.setRecords(records);
result.setTotal(total);
result.setPages((int) Math.ceil((double) total / size));
return result;
}
}
3.3 分页查询性能监控
@Component
public class PageQueryMonitor {
private static final Logger logger = LoggerFactory.getLogger(PageQueryMonitor.class);
/**
* 监控分页查询性能
*/
public <T> IPage<T> monitorPageQuery(Supplier<IPage<T>> queryFunction, String queryName) {
long startTime = System.currentTimeMillis();
try {
IPage<T> result = queryFunction.get();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
if (duration > 1000) { // 超过1秒记录警告
logger.warn("分页查询性能警告: {} 执行时间: {}ms", queryName, duration);
} else {
logger.info("分页查询完成: {} 执行时间: {}ms", queryName, duration);
}
return result;
} catch (Exception e) {
long endTime = System.currentTimeMillis();
logger.error("分页查询异常: {} 执行时间: {}ms", queryName, endTime - startTime, e);
throw e;
}
}
}
四、缓存策略优化:提升系统响应速度
4.1 Spring Cache 集成
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory())
.withInitialCacheConfigurations(getCacheConfigurations());
return builder.build();
}
private Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
configMap.put("userCache", defaultConfig);
configMap.put("productCache", defaultConfig);
return configMap;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.poolConfig(getPoolConfig())
.build();
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379), clientConfig);
}
private GenericObjectPoolConfig<?> getPoolConfig() {
GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
config.setTestOnBorrow(true);
return config;
}
}
4.2 缓存策略实现
@Service
public class CachedUserService {
@Autowired
private UserMapper userMapper;
/**
* 基础缓存查询
*/
@Cacheable(value = "userCache", key = "#id")
public User getUserById(Long id) {
return userMapper.selectById(id);
}
/**
* 带条件的缓存查询
*/
@Cacheable(value = "userCache", key = "#condition")
public List<User> getUsersByCondition(String condition) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", condition);
return userMapper.selectList(wrapper);
}
/**
* 缓存更新
*/
@CachePut(value = "userCache", key = "#user.id")
public User updateUser(User user) {
userMapper.updateById(user);
return user;
}
/**
* 缓存删除
*/
@CacheEvict(value = "userCache", key = "#id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
/**
* 批量缓存操作
*/
@Cacheable(value = "userPageCache", key = "#current + '_' + #size")
public IPage<User> getUserPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, null);
}
/**
* 缓存清除策略
*/
@CacheEvict(value = "userCache", allEntries = true)
public void clearUserCache() {
// 清除所有用户缓存
}
}
4.3 缓存穿透、击穿、雪崩防护
@Service
public class CacheProtectionService {
private static final Logger logger = LoggerFactory.getLogger(CacheProtectionService.class);
private static final String CACHE_PREFIX = "user_cache:";
private static final String LOCK_PREFIX = "user_lock:";
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserMapper userMapper;
/**
* 防缓存穿透的查询
*/
public User getUserWithProtection(Long id) {
String cacheKey = CACHE_PREFIX + id;
String lockKey = LOCK_PREFIX + id;
// 先查缓存
Object cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return (User) cached;
}
// 获取分布式锁
if (redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS)) {
try {
// 再次检查缓存
cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return (User) cached;
}
// 查询数据库
User user = userMapper.selectById(id);
if (user != null) {
// 缓存数据
redisTemplate.opsForValue().set(cacheKey, user, 30, TimeUnit.MINUTES);
} else {
// 缓存空值,防止缓存穿透
redisTemplate.opsForValue().set(cacheKey, new User(), 5, TimeUnit.MINUTES);
}
return user;
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
} else {
// 等待其他线程查询结果
try {
Thread.sleep(100);
return getUserWithProtection(id);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
}
/**
* 防缓存击穿的查询
*/
public User getUserWithTTL(Long id) {
String cacheKey = CACHE_PREFIX + id;
// 检查缓存是否即将过期
Long ttl = redisTemplate.getExpire(cacheKey);
if (ttl != null && ttl < 30) { // 小于30秒重新加载
// 异步刷新缓存
CompletableFuture.runAsync(() -> refreshCache(id));
}
Object cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return (User) cached;
}
return null;
}
private void refreshCache(Long id) {
User user = userMapper.selectById(id);
if (user != null) {
String cacheKey = CACHE_PREFIX + id;
redisTemplate.opsForValue().set(cacheKey, user, 30, TimeUnit.MINUTES);
}
}
}
五、数据库连接池配置优化
5.1 HikariCP 连接池配置
# application.yml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
# 连接池配置
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyHikariCP
# 连接测试配置
validation-timeout: 5000
connection-test-query: SELECT 1
# 自动提交
auto-commit: true
# 连接属性
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
5.2 自定义连接池配置
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池配置
dataSource.setMaximumPoolSize(20);
dataSource.setMinimumIdle(5);
dataSource.setConnectionTimeout(30000);
dataSource.setIdleTimeout(600000);
dataSource.setMaxLifetime(1800000);
dataSource.setPoolName("MyHikariCP");
// 连接测试配置
dataSource.setValidationTimeout(5000);
dataSource.setConnectionTestQuery("SELECT 1");
// 性能优化配置
dataSource.setLeakDetectionThreshold(60000);
dataSource.setRegisterMbeans(true);
// 连接属性优化
Properties props = new Properties();
props.setProperty("cachePrepStmts", "true");
props.setProperty("prepStmtCacheSize", "250");
props.setProperty("prepStmtCacheSqlLimit", "2048");
props.setProperty("useServerPrepStmts", "true");
props.setProperty("rewriteBatchedStatements", "true");
dataSource.setDataSourceProperties(props);
return dataSource;
}
}
5.3 连接池监控与调优
@Component
public class ConnectionPoolMonitor {
private static final Logger logger = LoggerFactory.getLogger(ConnectionPoolMonitor.class);
@Autowired
private HikariDataSource dataSource;
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void monitorConnectionPool() {
HikariPoolMXBean poolBean = dataSource.getHikariPoolMXBean();
logger.info("连接池监控信息:");
logger.info("活跃连接数: {}", poolBean.getActiveConnections());
logger.info("空闲连接数: {}", poolBean.getIdleConnections());
logger.info("总连接数: {}", poolBean.getTotalConnections());
logger.info("等待连接数: {}", poolBean.getThreadsAwaitingConnection());
logger.info("最大连接数: {}", poolBean.getMaxConnections());
logger.info("最小空闲连接数: {}", poolBean.getMinIdle());
// 如果等待连接数过多,记录警告
if (poolBean.getThreadsAwaitingConnection() > 5) {
logger.warn("连接池等待连接数过多: {},可能需要增加连接池大小",
poolBean.getThreadsAwaitingConnection());
}
// 如果活跃连接数接近最大连接数,记录警告
if (poolBean.getActiveConnections() > poolBean.getMaxConnections() * 0.8) {
logger.warn("连接池活跃连接数过高: {},接近最大连接数: {}",
poolBean.getActiveConnections(), poolBean.getMaxConnections());
}
}
/**
* 动态调整连接池配置
*/
public void adjustPoolSize(int newMaxSize) {
try {
HikariConfig config = dataSource.getHikariConfigMXBean();
config.setMaximumPoolSize(newMaxSize);
logger.info("连接池最大连接数已调整为: {}", newMaxSize);
} catch (Exception e) {
logger.error("调整连接池配置失败", e);
}
}
}
六、性能调优实战技巧
6.1 SQL 优化策略
@Service
public class OptimizedQueryService {
@Autowired
private UserMapper userMapper;
/**
* 优化前的查询 - 不推荐
*/
public List<User> getActiveUsersWithBadQuery() {
// 这种写法会查询所有字段,即使只需要部分字段
return userMapper.selectList(new QueryWrapper<User>().eq("status", 1));
}
/**
* 优化后的查询 - 推荐
*/
public List<User> getActiveUsersWithGoodQuery() {
// 只查询需要的字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "email", "create_time") // 明确指定字段
.eq("status", 1)
.orderByDesc("create_time"); // 按时间倒序排列
return userMapper.selectList(wrapper);
}
/**
* 使用原生SQL优化复杂查询
*/
public List<UserVO> getComplexUserQuery() {
// 复杂查询使用原生SQL
String sql = "SELECT u.id, u.name, u.email, u.create_time, " +
"(SELECT COUNT(*) FROM user_order o WHERE o.user_id = u.id) as order_count " +
"FROM user u WHERE u.status = 1 ORDER BY u.create_time DESC";
return userMapper.selectCustomUserList(sql);
}
/**
* 批量操作优化
*/
public void batchInsertUsers(List<User> users) {
// 使用批量插入优化
userMapper.insertBatchSomeColumn(users);
}
/**
* 分批处理大数据量
*/
public void processLargeDataSet() {
int pageSize = 1000;
int offset = 0;
boolean hasMore = true;
while (hasMore) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.last("LIMIT " + pageSize + " OFFSET " + offset);
List<User> batch = userMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(batch)) {
hasMore = false;
} else {
// 处理当前批次数据
processBatch(batch);
offset += pageSize;
}
}
}
private void processBatch(List<User> batch) {
// 处理批次数据的业务逻辑
for (User user : batch) {
// 业务处理
}
}
}
6.2 索引优化策略
-- 创建合适的索引
CREATE INDEX idx_user_name ON user(name);
CREATE INDEX idx_user_email ON user(email);
CREATE INDEX idx_user_create_time ON user(create_time);
CREATE INDEX idx_user_status_time ON user(status, create_time);
-- 复合索引优化
CREATE INDEX idx_user_status_name ON user(status, name);
CREATE INDEX idx_user_status_create_time ON user(status, create_time);
6.3 事务优化
@Service
@Transactional
public class TransactionOptimizedService {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderMapper orderMapper;
/**
* 优化的事务处理
*/
public void createOrderWithUser() {
// 1. 先创建用户
User user = new User();
user.setName("测试用户");
评论 (0)