引言
在现代Java企业级应用开发中,Spring Boot与MyBatis Plus的组合已经成为主流技术栈。Spring Boot以其"约定优于配置"的理念简化了Spring应用的初始搭建和开发过程,而MyBatis Plus则在MyBatis基础上提供了更强大的功能和更便捷的使用方式。本文将从零开始,详细讲解如何构建一个企业级应用,涵盖从项目初始化到核心功能实现的完整实践过程。
项目初始化与环境搭建
1.1 项目结构设计
在开始编码之前,我们需要规划一个清晰的项目结构。一个标准的企业级Spring Boot项目应该遵循以下目录结构:
src
├── main
│ ├── java
│ │ └── com.example
│ │ └── enterprise
│ │ ├── EnterpriseApplication.java
│ │ ├── config
│ │ ├── controller
│ │ ├── service
│ │ ├── mapper
│ │ ├── entity
│ │ ├── dto
│ │ └── exception
│ └── resources
│ ├── application.yml
│ ├── mapper
│ └── static
└── test
└── java
1.2 Maven依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>enterprise-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>enterprise-project</name>
<properties>
<java.version>11</java.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
</properties>
<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>${mybatis-plus.version}</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>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
1.3 配置文件设置
# application.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/enterprise_db?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
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.example.enterprise.entity
logging:
level:
com.example.enterprise.mapper: debug
代码生成器实战
2.1 MyBatis Plus代码生成器配置
代码生成器是提高开发效率的重要工具,它能够自动生成实体类、Mapper接口、Service层代码,减少重复劳动。
// CodeGenerator.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class CodeGenerator {
public static void main(String[] args) {
// 1. 全局配置
GlobalConfig config = new GlobalConfig();
config.setAuthor("Your Name")
.setOutputDir("D:\\workspace\\enterprise-project\\src\\main\\java")
.setFileOverride(true)
.setActiveRecord(true)
.setEnableCache(false)
.setBaseResultMap(true)
.setBaseColumnList(true)
.setIdType(IdType.AUTO)
.setSwagger2(true);
// 2. 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL)
.setUrl("jdbc:mysql://localhost:3306/enterprise_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai")
.setUsername("root")
.setPassword("password")
.setDriverName("com.mysql.cj.jdbc.Driver");
// 3. 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setRestControllerStyle(true)
.setInclude("user", "department", "role") // 需要生成的表
.setTablePrefix("t_"); // 表前缀
// 4. 包名配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.enterprise")
.setEntity("entity")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller");
// 5. 模板配置
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
// 6. 整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(config)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(packageConfig)
.setTemplate(templateConfig);
autoGenerator.execute();
}
}
2.2 生成的实体类示例
// User.java
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("t_user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String email;
private String phone;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Version
private Integer version;
@TableLogic
private Integer deleted;
}
核心功能实现
3.1 Mapper层设计
// UserMapper.java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.enterprise.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 自定义查询方法
User selectByUsername(String username);
// 多表关联查询
List<User> selectUserWithDepartment();
}
3.2 Service层实现
// UserService.java
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.enterprise.entity.User;
import java.util.List;
public interface UserService extends IService<User> {
User getUserByUsername(String username);
List<User> getUserListWithDepartment();
boolean changePassword(Long userId, String oldPassword, String newPassword);
}
// UserServiceImpl.java
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.enterprise.entity.User;
import com.example.enterprise.mapper.UserMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public User getUserByUsername(String username) {
return this.getOne(new QueryWrapper<User>().eq("username", username));
}
@Override
public List<User> getUserListWithDepartment() {
return this.baseMapper.selectUserWithDepartment();
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean changePassword(Long userId, String oldPassword, String newPassword) {
User user = this.getById(userId);
if (user == null || !user.getPassword().equals(oldPassword)) {
return false;
}
user.setPassword(newPassword);
return this.updateById(user);
}
}
3.3 Controller层设计
// UserController.java
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.enterprise.entity.User;
import com.example.enterprise.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getById(id);
}
@GetMapping("/username/{username}")
public User getUserByUsername(@PathVariable String username) {
return userService.getUserByUsername(username);
}
@GetMapping
public Page<User> getUserList(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String username) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (username != null && !username.isEmpty()) {
wrapper.like("username", username);
}
return userService.page(page, wrapper);
}
@PostMapping
public User createUser(@RequestBody User user) {
userService.save(user);
return user;
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
userService.updateById(user);
return user;
}
@DeleteMapping("/{id}")
public boolean deleteUser(@PathVariable Long id) {
return userService.removeById(id);
}
}
分页查询功能
4.1 分页查询实现
分页查询是企业级应用的核心功能之一,MyBatis Plus提供了完善的分页插件支持。
// 分页配置类
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
// 复杂分页查询示例
@GetMapping("/search")
public Page<User> searchUsers(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) Long departmentId,
@RequestParam(required = false) Integer status) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 复合查询条件
if (keyword != null && !keyword.isEmpty()) {
wrapper.like("username", keyword)
.or()
.like("email", keyword)
.or()
.like("phone", keyword);
}
if (departmentId != null) {
wrapper.eq("department_id", departmentId);
}
if (status != null) {
wrapper.eq("status", status);
}
// 排序
wrapper.orderByDesc("create_time");
return userService.page(page, wrapper);
}
4.2 自定义分页查询
// 自定义分页查询方法
public Page<User> selectUserPage(Page<User> page, String keyword) {
return this.selectPage(page, new QueryWrapper<User>()
.like("username", keyword)
.or()
.like("email", keyword)
.orderByDesc("create_time"));
}
// 复杂关联分页查询
public Page<User> selectUserWithDepartmentPage(Page<User> page) {
return this.selectPage(page, new QueryWrapper<User>()
.select("u.id", "u.username", "u.email", "d.name as department_name")
.from("t_user u")
.leftJoin("t_department d", "u.department_id = d.id")
.orderByDesc("u.create_time"));
}
事务管理最佳实践
5.1 事务注解使用
@Service
public class UserAccountService {
@Autowired
private UserService userService;
@Autowired
private AccountService accountService;
@Transactional(rollbackFor = Exception.class)
public void transferUserToAccount(Long userId, Long accountId) {
try {
// 1. 更新用户信息
User user = userService.getById(userId);
user.setAccountId(accountId);
userService.updateById(user);
// 2. 创建账户记录
Account account = new Account();
account.setUserId(userId);
account.setAccountId(accountId);
account.setCreateTime(LocalDateTime.now());
accountService.save(account);
// 3. 更新账户余额
accountService.updateBalance(accountId, 1000.0);
} catch (Exception e) {
// 事务会自动回滚
throw new RuntimeException("用户转移失败", e);
}
}
@Transactional(rollbackFor = Exception.class, timeout = 30)
public void batchUserImport(List<User> users) {
for (User user : users) {
userService.save(user);
}
}
}
5.2 事务传播行为
@Service
public class TransactionService {
@Autowired
private UserService userService;
// 默认REQUIRED传播行为
@Transactional
public void methodA() {
userService.save(new User());
methodB(); // 同一个事务中
}
// 事务传播行为测试
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 这个方法会在新的事务中执行
userService.updateById(new User());
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodC() {
// 不创建新事务,如果当前存在事务则加入,否则以非事务方式执行
userService.list();
}
}
性能优化策略
6.1 数据库连接池优化
# 数据库连接池配置
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
validation-timeout: 5000
leak-detection-threshold: 60000
6.2 缓存策略实现
// Redis配置
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
// 缓存服务实现
@Service
public class CachedUserService {
@Autowired
private UserService userService;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userService.getById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userService.updateById(user);
}
@Cacheable(value = "users", key = "#username")
public User getUserByUsername(String username) {
return userService.getUserByUsername(username);
}
}
6.3 SQL优化技巧
// 避免N+1查询问题
// 错误示例
@GetMapping("/users-with-departments")
public List<User> getUsersWithDepartments() {
List<User> users = userService.list();
for (User user : users) {
// 每次循环都查询数据库
Department department = departmentService.getById(user.getDepartmentId());
user.setDepartment(department);
}
return users;
}
// 正确示例 - 使用关联查询
@GetMapping("/users-with-departments")
public List<User> getUsersWithDepartments() {
return userService.getUserListWithDepartment();
}
// Mapper XML优化
// UserMapper.xml
<?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.enterprise.mapper.UserMapper">
<select id="selectUserWithDepartment" resultType="com.example.enterprise.entity.User">
SELECT u.id, u.username, u.email, d.name as department_name
FROM t_user u
LEFT JOIN t_department d ON u.department_id = d.id
WHERE u.deleted = 0
ORDER BY u.create_time DESC
</select>
</mapper>
异常处理机制
7.1 统一异常处理
// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
logger.warn("业务异常: {}", e.getMessage());
ErrorResponse error = new ErrorResponse(e.getCode(), e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ErrorResponse> handleValidationException(ValidationException e) {
logger.warn("参数验证异常: {}", e.getMessage());
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
logger.error("系统异常: ", e);
ErrorResponse error = new ErrorResponse("SYSTEM_ERROR", "系统内部错误");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
// 错误响应实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
private String code;
private String message;
private Long timestamp = System.currentTimeMillis();
}
7.2 自定义业务异常
// 业务异常类
public class BusinessException extends RuntimeException {
private String code;
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
public BusinessException(String code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
// getter和setter方法
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
// 使用示例
@Service
public class UserService {
public User getUserById(Long id) {
User user = this.getById(id);
if (user == null) {
throw new BusinessException("USER_NOT_FOUND", "用户不存在");
}
return user;
}
}
安全性最佳实践
8.1 权限控制实现
// 权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
String value();
String[] roles() default {};
}
// 权限拦截器
@Component
public class PermissionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
RequirePermission permission = method.getMethodAnnotation(RequirePermission.class);
if (permission != null) {
// 验证权限逻辑
if (!hasPermission(permission.value())) {
response.setStatus(HttpStatus.FORBIDDEN.value());
return false;
}
}
}
return true;
}
private boolean hasPermission(String permission) {
// 权限验证逻辑
return true;
}
}
// Controller使用权限注解
@RestController
@RequestMapping("/admin")
public class AdminController {
@RequirePermission("user:manage")
@DeleteMapping("/users/{id}")
public ResponseEntity<String> deleteUser(@PathVariable Long id) {
// 删除用户逻辑
return ResponseEntity.ok("删除成功");
}
}
8.2 数据加密处理
// 密码加密工具类
@Component
public class PasswordUtil {
private static final int HASH_ITERATIONS = 1024;
public static String encryptPassword(String password, String salt) {
return new SimpleHash("SHA-256", password, salt, HASH_ITERATIONS).toHex();
}
public static boolean verifyPassword(String password, String salt, String hashedPassword) {
String encrypted = encryptPassword(password, salt);
return encrypted.equals(hashedPassword);
}
public static String generateSalt() {
return UUID.randomUUID().toString().replace("-", "");
}
}
// 用户服务中的密码处理
@Service
public class UserService {
public User registerUser(String username, String password) {
// 生成盐值
String salt = PasswordUtil.generateSalt();
String encryptedPassword = PasswordUtil.encryptPassword(password, salt);
User user = new User();
user.setUsername(username);
user.setPassword(encryptedPassword);
user.setSalt(salt);
user.setCreateTime(LocalDateTime.now());
this.save(user);
return user;
}
}
测试策略
9.1 单元测试
// UserServiceTest.java
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserMapper userMapper;
@Test
public void testGetUserByUsername() {
// 准备测试数据
User expectedUser = new User();
expectedUser.setId(1L);
expectedUser.setUsername("testuser");
// 模拟Mapper行为
when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(expectedUser);
// 执行测试
User actualUser = userService.getUserByUsername("testuser");
// 验证结果
assertThat(actualUser).isNotNull();
assertThat(actualUser.getUsername()).isEqualTo("testuser");
}
@Test
public void testCreateUser() {
User user = new User();
user.setUsername("newuser");
user.setPassword("password");
// 测试保存
boolean result = userService.save(user);
assertThat(result).isTrue();
}
}
9.2 集成测试
// UserControllerIntegrationTest.java
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Sql(scripts = "/test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public class UserControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testGetUserById() {
ResponseEntity<User> response = restTemplate.getForEntity("/users/1", User.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getUsername()).isEqualTo("admin");
}
@Test
public void testCreateUser() {
User newUser = new User();
newUser.setUsername("testuser");
newUser.setEmail("test@example.com");
ResponseEntity<User> response = restTemplate.postForEntity("/users", newUser, User.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getUsername()).isEqualTo("testuser");
}
}
部署与监控
10.1 Docker部署
# Dockerfile
FROM openjdk:11-jre-slim
# 设置工作目录
WORKDIR /app
# 复制jar文件
COPY target/enterprise-project-0.0.1-SNAPSHOT.jar app.jar
# 暴露端口
EXPOSE 8080
# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]
10.2 监控配置
# Actuator监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,env,beans,httptrace
endpoint:
health:
show-details: always
metrics:
web:
server:
request:
autotime:
enabled: true
总结
通过本文的详细介绍,我们从项目初始化开始,逐步构建了一个完整的Spring Boot + MyBatis Plus企业级应用。涵盖了代码生成、分页查询、事务管理、性能优化、异常处理、安全性等多个核心主题。
在实际项目开发中,我们还需要关注以下几点:
- 代码规范:遵循统一的编码规范和命名约定
- 文档完善:提供详细的API文档和使用说明
- 持续集成:建立CI/CD流程,确保代码质量和部署效率
- 监控告警:建立完善的监控和告警机制
- 版本管理:合理的版本控制策略和发布流程
Spring Boot与MyBatis Plus的组合为Java企业级应用开发提供了强大的支持,通过合理的设计和最佳实践,我们可以构建出高性能、高可用、易维护的企业级应用系统。希望本文的内容能够为您的项目开发提供有价值的参考和指导。

评论 (0)