当前位置: 首页 > ai >正文

SpringBoot学习日记 Day5:解锁企业级开发核心技能

一、前言:从玩具项目到生产系统

经过前四天的学习,我们已经能够开发基础功能了。但要让应用真正具备生产价值,还需要掌握数据库高级操作、事务控制、缓存优化等企业级开发技能。今天就来攻克这些关键知识点!

二、JPA进阶:让数据库操作更高效

1. 复杂查询的三种实现方式

方式一:方法名派生查询

public interface UserRepository extends JpaRepository<User, Long> {// 根据姓名模糊查询+年龄范围List<User> findByUsernameContainingAndAgeBetween(String name, Integer minAge, Integer maxAge);// 统计大于某年龄的用户数Long countByAgeGreaterThan(Integer age);
}

方式二:@Query注解(JPQL)

@Query("SELECT u FROM User u WHERE u.dept.id = :deptId AND u.status = :status")
List<User> findUsersByDeptAndStatus(@Param("deptId") Long deptId, @Param("status") Integer status);

方式三:原生SQL查询

@Query(value = "SELECT * FROM users WHERE reg_time > :date", nativeQuery = true)
List<User> findRecentUsers(@Param("date") Date date);

2. 分页查询最佳实践

@GetMapping("/users")
public PageResult<User> getUsers(@RequestParam(defaultValue = "1") Integer page,@RequestParam(defaultValue = "10") Integer size,@RequestParam(required = false) String name) {Pageable pageable = PageRequest.of(page - 1, size, Sort.by("createTime").descending());Page<User> userPage;if (StringUtils.isEmpty(name)) {userPage = userRepository.findAll(pageable);} else {userPage = userRepository.findByUsernameContaining(name, pageable);}return PageResult.success(userPage);
}

3. 关联关系实战(用户-部门示例)

实体类配置:

@Entity
@Data
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;@ManyToOne@JoinColumn(name = "dept_id")private Department department;
}@Entity
@Data
public class Department {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;@OneToMany(mappedBy = "department")private List<User> users;
}

查询优化建议:

1. 使用@EntityGraph解决N+1查询问题:

@EntityGraph(attributePaths = {"department"})
List<User> findAllWithDepartment();

2. 延迟加载时注意事务范围:

// 在Service层方法上添加事务注解
@Transactional(readOnly = true)
public User getUserDetail(Long id) {User user = userRepository.findById(id).orElseThrow();// 此时可以安全访问延迟加载的关联对象System.out.println(user.getDepartment().getName());return user;
}

三、事务管理:数据一致性的守护者

1. 声明式事务基础

@Service
@RequiredArgsConstructor
public class OrderService {private final OrderRepository orderRepository;private final UserRepository userRepository;@Transactionalpublic void createOrder(OrderDTO dto) {// 扣减用户余额User user = userRepository.findById(dto.getUserId()).orElseThrow(() -> new BusinessException("用户不存在"));user.setBalance(user.getBalance() - dto.getAmount());userRepository.save(user);// 创建订单Order order = new Order();// 设置订单属性...orderRepository.save(order);// 如果这里抛出异常,上面所有操作都会回滚}
}

2. 事务传播行为实验

传播行为说明适用场景
REQUIRED(默认)当前有事务则加入,没有则新建大多数业务方法
REQUIRES_NEW总是新建事务,挂起当前事务日志记录等独立操作
NESTED在当前事务嵌套子事务部分需要独立回滚的子流程

测试用例:

@Transactional
public void parentMethod() {// 操作1...childMethod();  // 测试不同传播行为// 操作2...
}@Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod() {// 子方法操作...
}

四、Redis缓存:性能加速器

1. 集成Redis三步走

第一步:添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

第二步:配置连接

spring:redis:host: localhostport: 6379password: database: 0

第三步:启用缓存

@SpringBootApplication
@EnableCaching
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}

2. 缓存注解实战

@Service
public class UserService {// 缓存查询结果@Cacheable(value = "user", key = "#id")public User getUserById(Long id) {return userRepository.findById(id).orElseThrow();}// 更新时清除缓存@CacheEvict(value = "user", key = "#user.id")public User updateUser(User user) {return userRepository.save(user);}// 条件缓存@Cacheable(value = "user", key = "#name", unless = "#result == null")public User getUserByName(String name) {return userRepository.findByUsername(name);}
}

五、文件操作:用户头像上传实战

1. 配置文件上传

spring:servlet:multipart:max-file-size: 2MBmax-request-size: 10MB

2. 实现上传接口

@PostMapping("/avatar/upload")
public Result<String> uploadAvatar(@RequestParam("file") MultipartFile file) {if (file.isEmpty()) {throw new BusinessException("请选择文件");}// 生成唯一文件名String fileName = UUID.randomUUID() + "." + FileUtil.getExtension(file.getOriginalFilename());// 保存文件Path path = Paths.get("uploads/avatars", fileName);try {Files.createDirectories(path.getParent());file.transferTo(path);} catch (IOException e) {throw new BusinessException("文件上传失败");}return Result.success("/avatars/" + fileName);
}

3. 文件下载实现

@GetMapping("/avatar/download/{filename:.+}")
public void downloadAvatar(@PathVariable String filename, HttpServletResponse response) throws IOException {Path path = Paths.get("uploads/avatars", filename);if (!Files.exists(path)) {response.sendError(404, "文件不存在");return;}response.setContentType("image/jpeg");Files.copy(path, response.getOutputStream());
}

六、今日成果:用户管理系统升级版

1. 数据库层:

   - 实现多表关联查询

   - 支持分页排序

   - 完善事务管理

2. 缓存层:

   - 高频查询结果缓存

   - 自动更新缓存策略

3. 文件操作:

   - 头像上传下载功能

   - 文件大小限制处理

4. API增强:

// 分页查询示例
GET /users?page=1&size=10&name=张&sort=age,desc// 头像上传
POST /avatar/upload// 带缓存的用户查询
GET /users/{id}

七、避坑指南

1. N+1查询问题:

   - 使用@EntityGraph或JOIN FETCH优化

   - 测试时开启SQL日志观察查询次数

2. 事务失效场景:

   - 方法必须是public

   - 自调用问题(AOP失效)

   - 异常类型默认只回滚RuntimeException

3. 缓存一致性:

   - 更新数据库后及时清除缓存

   - 考虑使用@CachePut更新缓存

4. 文件存储安全:

   - 校验文件类型(不要仅靠扩展名)

   - 限制上传目录权限

   - 考虑使用云存储服务

八、明日计划

1. 学习SpringBoot定时任务

2. 集成邮件发送功能

3. 实现系统监控端点

4. 探索AOP统一日志处理

思考题:在电商系统中,下单操作需要扣减库存、生成订单、扣减优惠券等多个步骤,该如何设计事务边界?欢迎评论区分享你的设计方案!

如果觉得这篇日记有帮助,请点赞收藏支持~完整代码示例可通过私信获取。在实际开发中遇到问题也欢迎留言讨论!

http://www.xdnf.cn/news/17165.html

相关文章:

  • 亚马逊采购风控突围:构建深度隐匿的环境安全体系
  • 剧本杀小程序系统开发:推动社交娱乐产业创新发展
  • TikTok Shop冷启动破局战:亚矩阵云手机打造爆款账号矩阵
  • 项目构想|文生图小程序
  • 人工智能2.0时代的人才培养和通识教育
  • 动手学深度学习(pytorch版):第一节——引言
  • Redis学习总结(持续更新)
  • 【45】C++函数重载是什么?函数重载需要注意什么?为什么C++支持函数重载,C语言不支持函数重载?C++和C语言代码之间如何相互调用?
  • 仓库管理系统-20-前端之记录管理的联表查询
  • 2025最新国内服务器可用docker源仓库地址大全(2025年8月更新)
  • 深入剖析Java线程:从基础到实战(上)
  • 上海一家机器人IPO核心零部件依赖外购, 募投计划频繁修改引疑
  • AI绘画:生成唐初李世民全身像提示词
  • idea工具maven下载报错:PKIX path building failed,配置忽略SSL检查
  • 打造交互界面 —— Popup 的艺术
  • 使用萤石云播放视频及主题模版配置
  • 设计模式 观察者模式
  • 软件测试中,pytest 的 yield 有什么作用?
  • Day32--动态规划--509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯
  • 第一个vue应用
  • 【性能测试】---测试工具篇
  • JavaSE---异常的经典面试题
  • Git `cherry-pick` 工具汇总
  • 数组指针-函数指针-回调函数
  • 大屏数据展示页面,数据可视化可以用到的框架和插件
  • docker启动出现Error response from daemon: Container的问题【已解决】
  • List、ArrayList 与顺序表
  • VSCode:基础使用 / 使用积累
  • shell基础之EOF的用法
  • React:受控组件和非受控组件