引言
在现代Java企业级应用开发中,数据库访问层的设计与实现是系统架构中的关键环节。Spring Boot作为当前主流的微服务框架,结合MyBatis Plus这一强大的ORM工具,能够极大地提升开发效率和代码质量。本文将深入探讨Spring Boot与MyBatis Plus的集成实践,涵盖从基础配置到高级特性的完整技术栈。
1. Spring Boot + MyBatis Plus 集成基础
1.1 环境准备与依赖配置
在开始开发之前,我们需要搭建合适的开发环境。Spring Boot + MyBatis Plus的集成主要通过Maven或Gradle构建工具来实现。
<dependencies>
<!-- Spring Boot Web 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>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</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
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. MyBatis Plus 核心特性详解
2.1 基础CRUD操作
MyBatis Plus通过继承BaseMapper接口,自动提供常见的增删改查操作。以下是典型的实体类和Mapper定义:
// 实体类
@TableName("user_info")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String username;
private String email;
private Integer age;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// getter和setter方法
}
// Mapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
// MyBatis Plus自动提供CRUD方法
}
2.2 条件构造器使用
MyBatis Plus提供了强大的条件构造器,可以灵活构建复杂的查询条件:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> queryUsers() {
// 使用QueryWrapper构建复杂查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "username", "email")
.eq("age", 25)
.like("username", "张")
.between("create_time", startDate, endDate)
.orderByDesc("create_time");
return userMapper.selectList(wrapper);
}
public Page<User> queryUsersPage(int current, int size) {
// 分页查询
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("create_time");
return userMapper.selectPage(page, wrapper);
}
}
3. 代码生成器实战
3.1 自动化代码生成配置
MyBatis Plus提供了便捷的代码生成器,可以快速生成实体类、Mapper、Service等代码:
public class CodeGenerator {
public static void main(String[] args) {
// 全局配置
GlobalConfig config = new GlobalConfig();
config.setAuthor("Your Name");
config.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
config.setFileOverride(true);
config.setActiveRecord(true);
config.setEnableCache(false);
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL);
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("password");
// 策略配置
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", "department"); // 指定生成的表
// 包配置
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");
// 整体配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(config);
autoGenerator.setDataSource(dataSourceConfig);
autoGenerator.setStrategy(strategy);
autoGenerator.setPackageInfo(packageConfig);
autoGenerator.execute();
}
}
3.2 生成代码结构示例
通过代码生成器,我们可以得到如下标准的项目结构:
com.example.demo
├── entity
│ └── User.java
├── mapper
│ └── UserMapper.java
├── service
│ ├── UserService.java
│ └── impl
│ └── UserServiceImpl.java
└── controller
└── UserController.java
4. 多数据源配置实践
4.1 多数据源配置方案
在企业级应用中,往往需要同时操作多个数据库。Spring Boot结合MyBatis Plus可以轻松实现多数据源配置:
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.primary",
sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory primarySqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(primaryDataSource());
return bean.getObject();
}
}
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.secondary",
sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(secondaryDataSource());
return bean.getObject();
}
}
4.2 数据源切换注解实现
为了在不同业务场景中灵活切换数据源,我们可以创建自定义注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "primary";
}
// 数据源切换AOP实现
@Component
@Aspect
public class DataSourceAspect {
@Around("@annotation(dataSource)")
public Object switchDataSource(ProceedingJoinPoint point, DataSource dataSource) throws Throwable {
String dataSourceType = dataSource.value();
try {
DynamicDataSourceContextHolder.setDataSourceType(dataSourceType);
return point.proceed();
} finally {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
}
// 动态数据源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
5. 分页查询优化策略
5.1 MyBatis Plus分页插件配置
分页查询是数据库访问中的高频操作,MyBatis Plus提供了完善的分页支持:
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor pagination = new PaginationInnerInterceptor();
// 设置最大单页限制数量,默认500条,-1表示无限制
pagination.setMaxLimit(1000L);
// 分页合理化
pagination.setOverflow(true);
return pagination;
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor());
return interceptor;
}
}
5.2 高效分页查询实践
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public Page<User> getUserPage(int current, int size) {
// 使用MyBatis Plus内置分页功能
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("create_time");
return userMapper.selectPage(page, wrapper);
}
public Page<User> getUserPageWithCondition(int current, int size, String keyword) {
// 带条件的分页查询
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(keyword)) {
wrapper.like("username", keyword).or().like("email", keyword);
}
wrapper.orderByDesc("create_time");
return userMapper.selectPage(page, wrapper);
}
}
5.3 分页查询性能优化
// 使用自定义分页查询优化
public Page<User> optimizedUserQuery(int current, int size, String sortField, String sortOrder) {
Page<User> page = new Page<>(current, size);
// 预先计算总记录数,避免重复查询
QueryWrapper<User> countWrapper = new QueryWrapper<>();
if (StringUtils.hasText(sortField)) {
countWrapper.orderBy(true, "desc".equalsIgnoreCase(sortOrder), sortField);
}
// 使用select方法只查询需要的字段
QueryWrapper<User> selectWrapper = new QueryWrapper<>();
selectWrapper.select("id", "username", "email", "create_time");
if (StringUtils.hasText(sortField)) {
selectWrapper.orderBy(true, "desc".equalsIgnoreCase(sortOrder), sortField);
}
return userMapper.selectPage(page, selectWrapper);
}
6. 事务管理最佳实践
6.1 Spring事务注解使用
在企业级应用中,合理的事务管理至关重要:
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private DepartmentMapper departmentMapper;
public void createUserWithDepartment(User user, Department department) {
// 保存用户
userMapper.insert(user);
// 保存部门
departmentMapper.insert(department);
// 模拟业务逻辑
if (user.getAge() < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
@Transactional(readOnly = true)
public User getUserById(Long id) {
return userMapper.selectById(id);
}
}
6.2 事务传播行为控制
@Service
public class BusinessService {
@Autowired
private UserService userService;
@Autowired
private LogService logService;
// 使用REQUIRED传播行为
@Transactional(propagation = Propagation.REQUIRED)
public void processUserOperation(User user) {
try {
// 执行用户操作
userService.createUserWithDepartment(user, new Department());
// 记录日志
logService.logUserOperation(user);
} catch (Exception e) {
// 异常时回滚所有操作
throw new RuntimeException("用户操作失败", e);
}
}
// 使用REQUIRES_NEW传播行为
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logUserOperation(User user) {
// 独立的事务,即使外层事务回滚也不会影响此操作
Log log = new Log();
log.setUserId(user.getId());
log.setOperation("用户创建");
log.setCreateTime(LocalDateTime.now());
// 保存日志...
}
}
7. 性能优化与监控
7.1 SQL执行监控
@Component
public class SqlMonitorInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(SqlMonitorInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
try {
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
if (duration > 1000) { // 超过1秒的查询记录日志
logger.warn("SQL执行时间过长: {}ms", duration);
}
return result;
} finally {
// 记录执行信息
String sql = getSql(invocation);
logger.info("执行SQL: {}, 耗时: {}ms", sql, System.currentTimeMillis() - startTime);
}
}
private String getSql(Invocation invocation) {
// 提取SQL语句的实现
return "SQL extraction logic";
}
}
7.2 缓存策略实施
@Service
public class CachedUserService {
@Autowired
private UserMapper userMapper;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userMapper.selectById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userMapper.updateById(user);
}
@Cacheable(value = "userPage", key = "#current + '_' + #size")
public Page<User> getUserPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, null);
}
}
8. 安全性考虑
8.1 SQL注入防护
MyBatis Plus通过参数化查询有效防止SQL注入:
@Service
public class SecureUserService {
@Autowired
private UserMapper userMapper;
public List<User> searchUsers(String keyword) {
// 安全的参数化查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("username", keyword); // 自动处理特殊字符
return userMapper.selectList(wrapper);
}
public User getUserByCondition(Long id, String email) {
// 多条件查询的安全处理
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("id", id).eq("email", email);
return userMapper.selectOne(wrapper);
}
}
8.2 敏感数据处理
@TableName("user_info")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String username;
@JsonIgnore // 隐藏敏感字段
private String password;
@JsonSerialize(using = MaskedEmailSerializer.class) // 敏感信息脱敏
private String email;
// 其他字段...
}
9. 测试策略
9.1 单元测试实践
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserServiceTest {
@Autowired
private UserService userService;
@Autowired
private UserMapper userMapper;
@Test
void testCreateUser() {
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
user.setAge(25);
// 执行业务逻辑
userService.createUserWithDepartment(user, new Department());
// 验证结果
User savedUser = userMapper.selectById(user.getId());
assertNotNull(savedUser);
assertEquals("testuser", savedUser.getUsername());
}
@Test
void testPageQuery() {
Page<User> page = userService.getUserPage(1, 10);
assertNotNull(page);
assertTrue(page.getRecords().size() > 0);
}
}
9.2 集成测试配置
@TestPropertySource(locations = "classpath:application-test.yml")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void testUserApi() {
ResponseEntity<List<User>> response = restTemplate.exchange(
"/api/users",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<User>>() {}
);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
}
}
10. 部署与运维
10.1 生产环境配置优化
# application-prod.yml
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
mybatis-plus:
configuration:
cache-enabled: true
lazy-loading-enabled: false
aggressive-lazy-loading: false
map-underscore-to-camel-case: true
10.2 监控指标收集
@Component
public class MyBatisPlusMetrics {
private final MeterRegistry meterRegistry;
public MyBatisPlusMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@EventListener
public void handleSqlExecution(SqlExecutionEvent event) {
Timer.Sample sample = Timer.start(meterRegistry);
// 记录SQL执行时间
Timer timer = Timer.builder("mybatis.sql.execution")
.description("MyBatis SQL execution time")
.register(meterRegistry);
timer.record(event.getDuration(), TimeUnit.MILLISECONDS);
}
}
结语
通过本文的详细介绍,我们全面掌握了Spring Boot与MyBatis Plus在企业级应用开发中的最佳实践。从基础配置到高级特性,从性能优化到安全防护,每一个环节都体现了现代化Java开发的理念和方法。
在实际项目中,建议根据具体业务需求灵活运用这些技术点,同时要注重代码的可维护性和扩展性。随着业务的发展,我们还可以进一步集成更多的中间件和技术栈,构建更加完善的微服务架构。
记住,技术选型只是手段,解决业务问题才是最终目标。合理使用Spring Boot + MyBatis Plus这套组合拳,能够帮助我们快速构建出高效、稳定、易维护的数据库访问层,为企业的数字化转型提供坚实的技术基础。

评论 (0)