引言
在现代Java企业级应用开发中,Spring Boot与MyBatis Plus的组合已经成为主流技术栈。Spring Boot凭借其简化配置、快速启动的特点,而MyBatis Plus则通过强大的CRUD操作和代码生成能力,为开发者提供了高效的数据库访问解决方案。本文将从代码生成开始,深入探讨如何构建一个高性能、可维护的数据库访问层,涵盖从基础配置到性能优化的完整实践流程。
一、环境准备与基础配置
1.1 项目依赖配置
首先,我们需要在pom.xml中添加必要的依赖:
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus -->
<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>
<!-- Test -->
<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/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
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
mybatis-plus:
configuration:
# 开启驼峰命名转换
map-underscore-to-camel-case: true
# 日志配置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
二、代码生成器实践
2.1 MyBatis Plus代码生成器配置
MyBatis Plus提供了强大的代码生成器,可以快速生成实体类、Mapper接口、Service层等代码:
@Configuration
public class CodeGenerator {
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("developer");
gc.setOpen(false);
gc.setSwagger2(true); // 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("password");
mpg.setDataSource(ddsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("user");
pc.setParent("com.example.demo");
mpg.setPackageInfo(pc);
// 策略配置
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_info"); // 需要生成的表名
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.execute();
}
}
2.2 生成的代码结构
通过代码生成器,我们得到以下结构:
// 实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user_info")
public class UserInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
}
// Mapper接口
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
// Service接口
public interface UserInfoService extends IService<UserInfo> {
}
// Service实现类
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
}
三、基础CRUD操作实践
3.1 基本查询操作
MyBatis Plus提供了丰富的查询方法:
@Service
public class UserInfoService {
@Autowired
private UserInfoMapper userInfoMapper;
// 根据ID查询
public UserInfo getUserById(Long id) {
return userInfoMapper.selectById(id);
}
// 查询所有用户
public List<UserInfo> getAllUsers() {
return userInfoMapper.selectList(null);
}
// 条件查询
public List<UserInfo> getUsersByCondition(String username, String email) {
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(username)) {
wrapper.like("username", username);
}
if (StringUtils.hasText(email)) {
wrapper.like("email", email);
}
return userInfoMapper.selectList(wrapper);
}
// 分页查询
public IPage<UserInfo> getUsersByPage(int current, int size) {
Page<UserInfo> page = new Page<>(current, size);
return userInfoMapper.selectPage(page, null);
}
}
3.2 动态条件构建
// 复杂条件查询示例
public IPage<UserInfo> getUsersWithComplexCondition(UserQueryDTO queryDTO) {
Page<UserInfo> page = new Page<>(queryDTO.getCurrent(), queryDTO.getSize());
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
// 时间范围查询
if (queryDTO.getStartTime() != null && queryDTO.getEndTime() != null) {
wrapper.between("create_time", queryDTO.getStartTime(), queryDTO.getEndTime());
}
// 状态查询
if (queryDTO.getStatus() != null) {
wrapper.eq("status", queryDTO.getStatus());
}
// 关键词搜索
if (StringUtils.hasText(queryDTO.getKeyword())) {
wrapper.like("username", queryDTO.getKeyword())
.or()
.like("email", queryDTO.getKeyword());
}
// 排序
if (queryDTO.getSortField() != null) {
if (queryDTO.getSortOrder() == SortOrder.DESC) {
wrapper.orderByDesc(queryDTO.getSortField());
} else {
wrapper.orderByAsc(queryDTO.getSortField());
}
}
return userInfoMapper.selectPage(page, wrapper);
}
四、SQL优化策略
4.1 索引优化实践
良好的索引设计是性能优化的基础:
-- 创建用户表时的索引优化
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`email` varchar(100) NOT NULL COMMENT '邮箱',
`status` tinyint(4) DEFAULT '1' COMMENT '状态',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
KEY `idx_email` (`email`),
KEY `idx_status_create_time` (`status`,`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 避免N+1查询问题
// 错误示例:N+1查询
public List<UserInfo> getUsersWithOrders() {
List<UserInfo> users = userInfoMapper.selectList(null);
// 每次循环都会执行一次查询
for (UserInfo user : users) {
user.setOrders(orderService.getOrdersByUserId(user.getId()));
}
return users;
}
// 正确示例:使用关联查询
public List<UserInfo> getUsersWithOrdersOptimized() {
// 使用自定义SQL或MyBatis的关联查询
return userInfoMapper.selectUserWithOrders();
}
4.3 SQL执行分析
// 配置日志输出,监控慢SQL
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 设置日志实现
Configuration configuration = new Configuration();
configuration.setLogImpl(StdOutImpl.class);
configuration.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}
}
五、缓存策略实现
5.1 Redis集成配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
spring:
redis:
host: localhost
port: 6379
timeout: 2000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
5.2 缓存注解使用
@Service
public class UserInfoService {
@Autowired
private UserInfoMapper userInfoMapper;
@Cacheable(value = "user", key = "#id")
public UserInfo getUserById(Long id) {
return userInfoMapper.selectById(id);
}
@CacheEvict(value = "user", key = "#user.id")
public void updateUser(UserInfo user) {
userInfoMapper.updateById(user);
}
@CacheEvict(value = "user", allEntries = true)
public void clearUserCache() {
// 清空所有用户缓存
}
}
5.3 自定义缓存策略
@Component
public class UserCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String USER_KEY_PREFIX = "user:";
private static final int CACHE_EXPIRE_TIME = 3600; // 1小时
public UserInfo getUserFromCache(Long userId) {
String key = USER_KEY_PREFIX + userId;
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return (UserInfo) cached;
}
// 缓存未命中,查询数据库
UserInfo user = userInfoMapper.selectById(userId);
if (user != null) {
redisTemplate.opsForValue().set(key, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
}
return user;
}
public void updateUserInCache(UserInfo user) {
String key = USER_KEY_PREFIX + user.getId();
redisTemplate.opsForValue().set(key, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
}
}
六、事务管理最佳实践
6.1 声明式事务配置
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private OrderMapper orderMapper;
public void createUserWithOrder(UserInfo user, Order order) {
// 保存用户
userInfoMapper.insert(user);
// 保存订单,关联用户ID
order.setUserId(user.getId());
orderMapper.insert(order);
// 模拟业务异常
if (user.getUsername().equals("error")) {
throw new RuntimeException("模拟业务异常");
}
}
}
6.2 事务传播行为控制
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private PaymentService paymentService;
// 使用REQUIRED传播行为
@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Order order) {
// 保存订单
orderMapper.insert(order);
// 处理支付,使用同一个事务
paymentService.processPayment(order.getId(), order.getAmount());
}
// 使用REQUIRES_NEW传播行为
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment(Long orderId, BigDecimal amount) {
// 处理支付逻辑
Payment payment = new Payment();
payment.setOrderId(orderId);
payment.setAmount(amount);
payment.setCreateTime(LocalDateTime.now());
// 保存支付记录
}
}
七、性能监控与调优
7.1 数据库连接池监控
@Configuration
public class DataSourceMonitorConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
// 配置参数
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_db");
dataSource.setUsername("root");
dataSource.setPassword("password");
// 监控配置
dataSource.setMaximumPoolSize(20);
dataSource.setMinimumIdle(5);
dataSource.setConnectionTimeout(30000);
dataSource.setIdleTimeout(600000);
dataSource.setMaxLifetime(1800000);
return dataSource;
}
}
7.2 SQL性能分析
@Component
public class SqlPerformanceMonitor {
private static final Logger logger = LoggerFactory.getLogger(SqlPerformanceMonitor.class);
@EventListener
public void handleSqlExecution(SqlExecutionEvent event) {
long executionTime = event.getExecutionTime();
String sql = event.getSql();
if (executionTime > 1000) { // 超过1秒的SQL记录日志
logger.warn("Slow SQL detected: {} - Execution time: {}ms", sql, executionTime);
}
}
}
7.3 数据库查询优化建议
// 优化前:全表扫描
public List<UserInfo> getActiveUsers() {
return userInfoMapper.selectList(new QueryWrapper<UserInfo>().eq("status", 1));
}
// 优化后:使用索引
public List<UserInfo> getActiveUsersOptimized() {
// 确保在status字段上有索引
return userInfoMapper.selectList(new QueryWrapper<UserInfo>().eq("status", 1));
}
// 分页查询优化
public IPage<UserInfo> getActiveUsersPaginated(int current, int size) {
Page<UserInfo> page = new Page<>(current, size);
// 使用索引字段进行排序
page.setOrders(OrderItem.asc("create_time"));
return userInfoMapper.selectPage(page,
new QueryWrapper<UserInfo>().eq("status", 1));
}
八、异常处理与日志记录
8.1 统一异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
logger.warn("Business exception: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(e.getCode(), e.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
logger.error("Unexpected error occurred", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("500", "Internal server error"));
}
}
@Data
@AllArgsConstructor
public class ErrorResponse {
private String code;
private String message;
}
8.2 操作日志记录
@Service
public class OperationLogService {
@Autowired
private OperationLogMapper operationLogMapper;
public void logOperation(String operation, String description) {
OperationLog log = new OperationLog();
log.setOperation(operation);
log.setDescription(description);
log.setOperator(getCurrentUserName());
log.setCreateTime(LocalDateTime.now());
operationLogMapper.insert(log);
}
private String getCurrentUserName() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null ? authentication.getName() : "anonymous";
}
}
九、安全与权限控制
9.1 数据权限控制
@Component
public class DataPermissionInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(ExecutorWrapper executor, MappedStatement mappedStatement) {
// 添加数据权限过滤条件
String sql = executor.getSql();
if (sql.contains("user_info")) {
// 根据用户角色添加过滤条件
String permissionFilter = buildPermissionFilter();
// 将过滤条件添加到SQL中
}
}
private String buildPermissionFilter() {
// 构建数据权限过滤条件
return " AND create_user_id = #{currentUserId}";
}
}
9.2 SQL注入防护
// 使用MyBatis Plus的安全查询方法
public List<UserInfo> getUsersBySafeCondition(String username) {
// 使用QueryWrapper的参数化查询,防止SQL注入
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.like("username", username);
return userInfoMapper.selectList(wrapper);
}
// 避免使用字符串拼接
public List<UserInfo> getUsersByUnsafeCondition(String condition) {
// 不推荐:容易造成SQL注入
// String sql = "SELECT * FROM user_info WHERE username LIKE '%" + condition + "%'";
// 推荐:使用参数化查询
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.like("username", condition);
return userInfoMapper.selectList(wrapper);
}
十、总结与最佳实践
10.1 核心要点回顾
通过本文的详细介绍,我们掌握了Spring Boot + MyBatis Plus的最佳实践:
- 代码生成器:提高开发效率,减少重复代码
- SQL优化:合理设计索引,避免N+1问题
- 缓存策略:结合Redis实现高效的缓存管理
- 事务控制:正确使用事务传播行为保证数据一致性
- 性能监控:建立完善的监控体系及时发现性能瓶颈
10.2 实践建议
// 综合最佳实践示例
@Service
@Transactional
@CacheConfig(cacheNames = "user")
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
@Cacheable(key = "#id")
public UserInfo getUserById(Long id) {
return userInfoMapper.selectById(id);
}
@CacheEvict(key = "#user.id")
public void updateUser(UserInfo user) {
userInfoMapper.updateById(user);
}
// 高性能查询示例
public IPage<UserInfo> getUsersByPage(int current, int size, String keyword) {
Page<UserInfo> page = new Page<>(current, size);
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(keyword)) {
wrapper.like("username", keyword)
.or()
.like("email", keyword);
}
// 使用索引字段排序
page.setOrders(OrderItem.desc("create_time"));
return userInfoMapper.selectPage(page, wrapper);
}
}
10.3 持续改进方向
- 监控体系完善:建立更全面的性能监控指标
- 自动化测试:增加单元测试和集成测试覆盖率
- 微服务架构:在分布式场景下的优化策略
- 容器化部署:Docker环境下的配置优化
通过以上实践,我们可以构建出一个高性能、可维护的数据库访问层,为企业的业务系统提供稳定可靠的数据支持。在实际项目中,建议根据具体需求灵活应用这些技术点,并持续关注新技术的发展,不断优化系统性能。

评论 (0)