尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

ai全栈day06

ai全栈day06
📅 发布时间:2026/7/11 2:00:43

AI 全栈工程师学习日记:Spring Boot 自动配置、AOP 切面、MVC 流程与数据库设计实战

今天简直是 Spring 框架的"深度一日游"!从Spring Boot 自动配置的底层原理,到AOP 面向切面编程的优雅解耦;从Spring MVC 请求处理的完整链路,到数据库 ER 图与三范式的设计实践。如果说昨天是站在 Spring 门口参观,今天就是拿着放大镜研究它的五脏六腑。AutoConfiguration 的魔法不再神秘,AOP 的代理机制也终于理解了为什么this调用会失效。最爽的是亲手设计了一套电商数据库,从 ER 图到建表语句,体会到了架构设计的美感。

Spring BootAOPSpring MVC数据库设计自动配置

8h

今日学习

06/70

总进度

8

学习阶段

目录

  • 一、Spring Boot 自动配置原理:魔法背后的条件注解
  • 二、Spring AOP 面向切面编程:横切关注点的优雅解耦
  • 三、Spring MVC 请求处理流程:从浏览器到 Controller 的完整旅程
  • 四、数据库设计实战:ER 图、三范式与索引设计
  • 五、今日踩坑记录
  • 六、收获总结与学习感悟
  • 七、明日学习计划

一、Spring Boot 自动配置原理:魔法背后的条件注解

初学 Spring Boot 时最震撼的体验就是:加一个依赖、写几行配置,整个功能就自动跑起来了。这种"约定大于配置"的魔法是怎么实现的?今天终于揭开了@SpringBootApplication的神秘面纱。

1.1 @SpringBootApplication 的三层拆解

@SpringBootApplication其实是一个"组合注解",它等同于以下三个注解的叠加:

Java@SpringBootApplication 源码本质

@SpringBootConfiguration // 本质就是 @Configuration,标记这是配置类 @EnableAutoConfiguration // 开启自动配置的核心 @ComponentScan( // 组件扫描,默认扫描当前包及子包 excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)} ) public @interface SpringBootApplication {} // 等价写法 @Configuration @EnableAutoConfiguration @ComponentScan public class MyApplication { ... }

1.2 @EnableAutoConfiguration 的工作流程

自动配置的入口是@EnableAutoConfiguration,它通过@Import导入了一个关键类AutoConfigurationImportSelector,整个加载流程如下:

  1. 读取候选配置清单:从所有依赖 jar 包的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中读取全限定类名列表
  2. 条件过滤:对每个候选配置类,检查其上的条件注解(如@ConditionalOnClass),判断是否满足激活条件
  3. 去重与排序:通过@AutoConfigureBefore/@AutoConfigureAfter控制配置顺序
  4. 注册 Bean:将最终筛选出的配置类注册到 Spring 容器

Spring Boot 2.7+ 的配置文件变化

Spring Boot 2.7 之前,自动配置类清单写在META-INF/spring.factories中,键为org.springframework.boot.autoconfigure.EnableAutoConfiguration。2.7 之后引入了新的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,每行一个全限定类名,更加直观。Spring Boot 3.x 已经完全迁移到新格式。

1.3 条件注解:自动配置的"智能开关"

条件注解是自动配置的"灵魂",它们决定了在什么情况下才加载某个 Bean:

条件注解作用示例场景
@ConditionalOnClass类路径存在指定类时才生效检测到DataSource.class才配置数据源
@ConditionalOnMissingClass类路径不存在指定类时才生效用户未引入 ShardingJDBC 时才配置默认数据源
@ConditionalOnBean容器中存在指定 Bean 时才生效存在RedisConnectionFactory才配置 RedisTemplate
@ConditionalOnMissingBean容器中不存在指定 Bean 时才生效用户未自定义ObjectMapper时才创建默认的
@ConditionalOnProperty指定配置属性满足条件时才生效spring.datasource.url存在时才初始化连接池
@ConditionalOnWebApplication是 Web 应用时才生效只在 Web 环境下配置 DispatcherServlet

1.4 手写一个自定义 Starter

为了彻底理解 Starter 机制,我动手写了一个极简的日志拦截 Starter——引入依赖后自动为所有 Controller 方法添加执行耗时打印。

JavaLogInterceptorAutoConfiguration.java

package com.example.autoconfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @ConditionalOnProperty(prefix = "log.interceptor", name = "enabled", havingValue = "true", matchIfMissing = true) public class LogInterceptorAutoConfiguration { @Bean public LogInterceptor logInterceptor() { return new LogInterceptor(); } @Bean public LogInterceptorConfigurer logInterceptorConfigurer(LogInterceptor interceptor) { return new LogInterceptorConfigurer(interceptor); } }

JavaLogInterceptor.java

import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LogInterceptor implements HandlerInterceptor { private static final String START_TIME = "startTime"; @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) { req.setAttribute(START_TIME, System.currentTimeMillis()); return true; } @Override public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) { long start = (Long) req.getAttribute(START_TIME); long cost = System.currentTimeMillis() - start; System.out.printf("[%s %s] 耗时: %dms%n", req.getMethod(), req.getRequestURI(), cost); } }

PlainMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.example.autoconfig.LogInterceptorAutoConfiguration

Starter 命名规范

官方 Starter 命名格式为spring-boot-starter-xxx,如spring-boot-starter-web。自定义 Starter 推荐命名为xxx-spring-boot-starter,如my-log-spring-boot-starter,以区分官方与第三方。

***

二、Spring AOP 面向切面编程:横切关注点的优雅解耦

AOP(Aspect-Oriented Programming)是 Spring 框架的两大基石之一。它的核心思想是:把散布在业务代码各处的横切关注点(日志、权限、事务、监控)提取出来,集中管理,让业务代码只关心业务本身。

2.1 AOP 核心概念速览

  • Aspect(切面):横切关注点的模块化,一个切面包含多个通知和切点
  • Join Point(连接点):程序执行过程中的某个点,如方法调用、异常抛出
  • Pointcut(切点):匹配连接点的表达式,决定通知在何处生效
  • Advice(通知):切面在特定连接点采取的行动,如前置通知、后置通知
  • Target(目标对象):被代理的原始对象
  • Proxy(代理):AOP 框架创建的对象,包含目标对象和通知逻辑

2.2 切点表达式:精准定位目标方法

切点表达式是 AOP 的"导航仪",最常用的execution表达式语法如下:

Java切点表达式示例

// 匹配 UserService 接口的所有方法 execution(* com.example.service.UserService.*(..)) // 匹配 service 包下所有类的所有方法 execution(* com.example.service.*.*(..)) // 匹配所有以 save 开头的方法,任意参数,返回任意类型 execution(* save*(..)) // 匹配所有 public 方法 execution(public * *(..)) // 匹配第一个参数为 String,后面任意参数的方法 execution(* *(String, ..)) // 组合切点:service 包 && 方法名以 find 开头 @Pointcut("execution(* com.example.service.*.*(..))") public void serviceLayer() {} @Pointcut("execution(* find*(..))") public void findMethods() {} @Pointcut("serviceLayer() && findMethods()") public void serviceFindOps() {}

2.3 五种通知类型与执行顺序

通知类型注解执行时机
前置通知@Before目标方法执行之前
后置通知@After目标方法执行之后(无论是否异常)
返回通知@AfterReturning目标方法正常返回后
异常通知@AfterThrowing目标方法抛出异常后
环绕通知@Around包裹目标方法,可控制是否执行

JavaLoggingAspect.java — 统一日志切面

import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { // 定义切点:所有 Controller 层方法 @Pointcut("execution(* com.example.controller.*.*(..))") public void controllerLayer() {} @Before("controllerLayer()") public void logBefore(JoinPoint jp) { String method = jp.getSignature().toShortString(); Object[] args = jp.getArgs(); System.out.println("[前置] 调用: " + method + ", 参数: " + Arrays.toString(args)); } @AfterReturning(pointcut = "controllerLayer()", returning = "result") public void logAfterReturning(JoinPoint jp, Object result) { System.out.println("[返回] " + jp.getSignature().toShortString() + " 结果: " + result); } @AfterThrowing(pointcut = "controllerLayer()", throwing = "ex") public void logException(JoinPoint jp, Exception ex) { System.err.println("[异常] " + jp.getSignature().toShortString() + " 异常: " + ex.getMessage()); } // 环绕通知:最强大,通常用于性能监控 @Around("controllerLayer()") public Object logAround(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); String method = pjp.getSignature().toShortString(); try { Object result = pjp.proceed(); // 执行目标方法 long cost = System.currentTimeMillis() - start; System.out.println("[环绕] " + method + " 耗时: " + cost + "ms"); return result; } catch (Throwable t) { long cost = System.currentTimeMillis() - start; System.err.println("[环绕] " + method + " 异常中断, 已耗时: " + cost + "ms"); throw t; } } }

2.4 代理机制:JDK 动态代理 vs CGLIB

AOP 的实现依赖代理模式,Spring 提供了两种代理方式:

维度JDK 动态代理CGLIB
实现原理实现目标类的接口,生成接口代理继承目标类,生成子类代理
适用对象实现了接口的类没有实现接口的类
目标类要求必须有接口不能是 final 类,方法不能是 final
性能稍慢(反射调用)更快(方法索引直接调用)
Spring 默认策略目标有接口时优先使用无接口时自动 fallback

this 调用不走代理 — AOP 的经典陷阱

同一类中方法 A 调用方法 B(B 有 AOP 通知),B 的通知不会生效。原因:外部调用通过代理对象,包含通知逻辑;而内部this.B()调用的是目标对象本身,绕过了代理。解决:将 B 抽到另一个 Bean 中;或注入自身代理;或改用 AspectJ 编译期织入。

***

三、Spring MVC 请求处理流程:从浏览器到 Controller 的完整旅程

Spring MVC 是 Spring 框架的 Web 模块,也是目前 Java Web 开发的事实标准。理解它的请求处理流程,对排查接口问题、优化性能、扩展框架都至关重要。

3.1 DispatcherServlet:前端控制器的中央调度

Spring MVC 的核心是DispatcherServlet,它遵循"前端控制器"设计模式,所有请求都先经过它,再由它分发给后续的处理器。一个 HTTP 请求的完整处理链路如下:

  1. 请求到达:HTTP 请求进入DispatcherServlet.doDispatch()
  2. 查找处理器:通过HandlerMapping找到处理该请求的HandlerExecutionChain(包含 Controller 方法和拦截器链)
  3. 查找适配器:通过HandlerAdapter找到能执行该处理器的适配器(如RequestMappingHandlerAdapter)
  4. 参数解析:适配器调用HandlerMethodArgumentResolver解析请求参数(@RequestParam、@RequestBody等)
  5. 执行处理器:反射调用 Controller 方法,执行业务逻辑
  6. 处理返回值:通过HandlerMethodReturnValueHandler处理返回值,如@ResponseBody会用HttpMessageConverter序列化为 JSON
  7. 视图渲染/响应:如果是页面请求则通过ViewResolver解析视图;如果是 REST API 则直接返回 JSON
  8. 异常处理:过程中任何异常都会被HandlerExceptionResolver捕获,最终进入@ControllerAdvice处理

3.2 统一返回格式与全局异常处理

生产环境中,REST API 必须统一返回格式,方便前端处理和错误追踪。

JavaApiResponse.java — 统一返回体

public class ApiResponse<T> { private int code; // 业务状态码 0=成功 private String message; // 提示信息 private T data; // 数据 private long timestamp; // 时间戳 public static <T> ApiResponse<T> success(T data) { ApiResponse<T> r = new ApiResponse<>(); r.code = 0; r.message = "success"; r.data = data; r.timestamp = System.currentTimeMillis(); return r; } public static <T> ApiResponse<T> error(int code, String message) { ApiResponse<T> r = new ApiResponse<>(); r.code = code; r.message = message; r.timestamp = System.currentTimeMillis(); return r; } // getters & setters... }

JavaGlobalExceptionHandler.java — 全局异常处理器

import org.springframework.web.bind.annotation.*; @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ApiResponse<Void> handleBusiness(BusinessException ex) { return ApiResponse.error(ex.getCode(), ex.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public ApiResponse<Void> handleValidation(MethodArgumentNotValidException ex) { String msg = ex.getBindingResult().getFieldErrors().stream() .map(e -> e.getField() + ": " + e.getDefaultMessage()) .collect(Collectors.joining(", ")); return ApiResponse.error(400, msg); } @ExceptionHandler(Exception.class) public ApiResponse<Void> handleAll(Exception ex) { // 生产环境不要返回详细异常信息给客户端 return ApiResponse.error(500, "服务器内部错误"); } }

@ControllerAdvice vs @RestControllerAdvice

@RestControllerAdvice等于@ControllerAdvice + @ResponseBody,意味着所有异常处理方法都会将返回值序列化为 JSON/XML。在纯 REST API 项目中统一使用@RestControllerAdvice,避免每个方法都加@ResponseBody。

***

四、数据库设计实战:ER 图、三范式与索引设计

好的数据库设计是系统稳定的基石。今天从零开始设计了一套简化版电商数据库,经历了需求分析 → ER 图 → 范式规范化 → 建表 → 索引设计的完整流程。

4.1 需求分析与实体识别

简化版电商系统需要支持:用户注册登录、商品浏览、购物车、下单、支付、订单状态跟踪。

  • 用户(User):用户ID、手机号、密码、昵称、创建时间
  • 商品(Product):商品ID、名称、描述、价格、库存、分类ID
  • 分类(Category):分类ID、名称、父分类ID
  • 订单(Order):订单ID、用户ID、总金额、状态、创建时间
  • 订单项(OrderItem):订单项ID、订单ID、商品ID、数量、单价
  • 购物车(CartItem):购物车ID、用户ID、商品ID、数量

4.2 ER 图与关系梳理

实体之间的关系:

  • 用户 — 订单:1:N(一个用户有多个订单)
  • 订单 — 订单项:1:N(一个订单包含多个订单项)
  • 商品 — 订单项:1:N(一个商品出现在多个订单项中)
  • 分类 — 商品:1:N(一个分类下有多件商品)
  • 用户 — 购物车:1:N(一个用户有多条购物车记录)
  • 商品 — 购物车:1:N(一个商品被多个用户加入购物车)

4.3 三范式:规范化的艺术

三范式是关系型数据库设计的黄金法则,目的是消除数据冗余和更新异常。

范式核心要求解决什么问题
1NF每个字段都是原子的,不可再分消除重复组和可再分字段
2NF满足 1NF,且非主属性完全依赖主键消除部分依赖(主要针对联合主键)
3NF满足 2NF,且消除传递依赖消除间接依赖,避免数据冗余

反范式设计:适当冗余换取性能

三范式不是圣经。在高并发读场景中,适当的反范式设计(冗余字段)可以大幅减少 JOIN 查询,提升性能。例如:订单表中冗余total_amount(可通过订单项计算得出),以及user_phone(可从用户表查询),目的是避免订单查询时关联多张表。冗余的代价是数据一致性维护更复杂,需要根据业务场景权衡。

4.4 建表语句与索引设计

SQLecommerce_schema.sql

-- 用户表 CREATE TABLE `user` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID', `phone` VARCHAR(20) NOT NULL COMMENT '手机号', `password` VARCHAR(128) NOT NULL COMMENT '加密密码', `nickname` VARCHAR(50) DEFAULT '' COMMENT '昵称', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`), -- 手机号唯一 KEY `idx_created_at` (`created_at`) -- 按时间范围查询 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 商品表 CREATE TABLE `product` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(200) NOT NULL COMMENT '商品名称', `description` TEXT COMMENT '商品描述', `price` DECIMAL(10,2) NOT NULL COMMENT '售价', `stock` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '库存', `category_id` BIGINT UNSIGNED NOT NULL COMMENT '分类ID', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_category` (`category_id`), -- 按分类查询 KEY `idx_price` (`price`), -- 按价格排序/筛选 FULLTEXT KEY `ft_name` (`name`) -- 商品名称全文搜索 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'; -- 订单表 CREATE TABLE `order` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID', `total_amount` DECIMAL(12,2) NOT NULL COMMENT '订单总金额', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0待支付 1已支付 2已发货 3已完成 4已取消', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `paid_at` TIMESTAMP NULL DEFAULT NULL COMMENT '支付时间', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), -- 查询用户订单列表 KEY `idx_status_created` (`status`,`created_at`) -- 按状态+时间查询 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表'; -- 订单项表 CREATE TABLE `order_item` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `order_id` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', `product_id` BIGINT UNSIGNED NOT NULL COMMENT '商品ID', `quantity` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '数量', `unit_price` DECIMAL(10,2) NOT NULL COMMENT '下单时单价', PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`), -- 查询订单详情 KEY `idx_product_id` (`product_id`) -- 统计商品销量 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表';

4.5 索引设计原则总结

  • WHERE 条件字段:频繁用于过滤的字段必须建索引(如user_id、status)
  • ORDER BY / GROUP BY 字段:排序和分组字段建索引可避免 filesort
  • 联合索引最左前缀:(status, created_at)可以匹配WHERE status=?或WHERE status=? AND created_at>?,但不能匹配WHERE created_at>?
  • 避免索引泛滥:每个额外索引都会增加写操作开销,单表索引数建议不超过 5 个
  • 区分度低的字段不加单列索引:如性别(只有男女),加了也几乎没筛选效果

***

五、今日踩坑记录

坑 1:Spring Boot 3 中 WebMvcConfigurer 配置拦截器不生效

按照教程实现了WebMvcConfigurer.addInterceptors()注册拦截器,但请求时拦截器没有触发。原因:项目中同时引入了spring-boot-starter-webflux,Spring Boot 优先启用了 WebFlux 自动配置,而WebMvcConfigurer是 Servlet MVC 体系的接口,在 WebFlux 环境下不生效。解决方案:检查 pom.xml,确保没有同时引入 web 和 webflux;如果确实需要 WebFlux,拦截器应改用WebFilter实现。

坑 2:AOP 切点表达式写错导致所有方法都被拦截

本想只拦截 Controller 层方法,写了execution(* com.example.*.*(..)),结果 service、dao 层的方法也被拦截了,日志爆多。原因:com.example.*只匹配 example 下的直接子包(一级),但com.example.controller.*才是 controller 包下的类;更危险的是..和*的区别——..匹配任意层级子包,*只匹配一层。解决方案:明确指定execution(* com.example.controller..*.*(..)),用..确保只拦截 controller 包及其子包。

Java

// 错误:匹配 com.example 下所有一级子包的所有类 execution(* com.example.*.*(..)) // 正确:匹配 controller 包及其任意子包下的所有方法 execution(* com.example.controller..*.*(..))

坑 3:@Transactional 放在 private 方法上不生效

给 Service 的私有方法加了@Transactional,但事务没有回滚。原因:Spring AOP 的代理机制(无论是 JDK 还是 CGLIB)都只能拦截 public 方法。private 方法无法被代理对象重写/拦截,所以事务注解被完全忽略。解决方案:将方法改为 public;或者把事务逻辑抽到另一个 Service Bean 中调用。

坑 4:MySQL DECIMAL 精度丢失问题

订单金额字段用DECIMAL(10,2)存储,但在 Java 实体中用了double类型接收,发现某些金额(如 19.99)在计算后出现19.989999999999998。原因:double/float 是二进制浮点数,无法精确表示某些十进制小数。解决方案:Java 中金额计算必须用BigDecimal,且数据库实体字段类型要与数据库一致,全程避免使用 double/float 处理金额。

Java

// 错误:double 精度丢失 double a = 19.99, b = 0.01; System.out.println(a + b); // 可能输出 20.000000000000004 // 正确:BigDecimal 精确计算 BigDecimal a = new BigDecimal("19.99"); // 必须用字符串构造 BigDecimal b = new BigDecimal("0.01"); System.out.println(a.add(b)); // 精确输出 20.00

坑 5:联合索引顺序写反导致索引失效

订单表建了联合索引(created_at, status),查询WHERE status=1 AND created_at>'2026-07-01'时发现走了全表扫描。原因:联合索引遵循"最左前缀原则",索引是(created_at, status)意味着先按 created_at 排序,再按 status 排序。查询条件以status开头,跳过了最左列created_at,导致索引无法使用。解决方案:调整索引顺序为(status, created_at),或改写查询确保最左列被使用。

***

六、收获总结与学习感悟

今日收获

  1. Spring Boot 自动配置:理解了@SpringBootApplication的组合本质、条件注解的过滤机制、Starter 的加载流程,并手写了一个自定义 Starter。
  2. Spring AOP:掌握了五种通知类型、切点表达式语法、JDK/CGLIB 代理的区别,以及this调用不走代理的经典陷阱。
  3. Spring MVC:理清了 DispatcherServlet → HandlerMapping → HandlerAdapter → Controller → 异常处理的完整链路,实现了统一返回格式和全局异常处理。
  4. 数据库设计:从需求分析到 ER 图,从三范式到反范式设计,从建表到索引优化,完整走了一遍数据库架构设计流程。
  5. 工程意识:体会到了"约定大于配置"和"解耦"在实际项目中的巨大价值。

学习感悟

今天学 Spring Boot 自动配置的时候,我突然有种"原来如此"的通透感。以前觉得 Spring Boot 很神奇,加个依赖就能跑,现在明白了它不过是在启动时读了一堆配置类,用条件注解判断要不要加载。魔法消失了,但美感还在——那种把复杂逻辑藏在简单接口背后的设计美感。

AOP 给我的最大启发是"关注点分离"。以前写代码时日志、权限、事务和业务逻辑混在一起,读起来像一锅粥。AOP 就像一把锋利的手术刀,把横切关注点干净利落地切出去,业务代码瞬间变得清爽。当然代价是要理解代理机制,不然就会被this调用、事务不生效这类问题搞得头大。

数据库设计是今天最有"架构师感觉"的部分。画 ER 图的时候,我在纸上涂涂改改,思考实体之间的关系是 1:N 还是 M:N,哪些字段需要冗余,索引怎么建——这些决策会直接影响系统未来几年的性能和扩展性。三范式告诉我什么是"正确"的设计,反范式教会我"正确"不一定"合适"。

明天要进入数据库性能优化和 MyBatis 的学习了,第二阶段还剩最后一天,加油!

***

七、明日学习计划

Day 07 · 2026-07-06(周一)
  • 数据库性能优化:SQL 优化、慢查询分析、EXPLAIN 执行计划解读、索引优化进阶
  • MyBatis / MyBatis-Plus:ORM 映射、动态 SQL、分页插件、代码生成器
  • 连接池与事务:HikariCP 原理、Spring 声明式事务传播行为
  • 第二阶段项目启动:多模态智能写手 — 需求分析与架构设计

相关新闻

  • AI推理经济学:从基准测试到成本效益优化的实践指南
  • MAX77654与TM4C1294的嵌入式电源管理方案设计
  • STM32图形化配置与代码生成工具全解析

最新新闻

  • 工业级压电警报系统设计与STM32驱动优化
  • 北京宝珀回收价格查询和靠谱平台实测排行(2026年7月最新) - 嘉价奢侈品回收平台
  • Unity内嵌浏览器开发指南:从选型到双向通信的完整实践
  • 豆包API接入与OAuth2.0集成实战指南
  • 3DLMM+PEGA世界模型Seele:大模型Agent编排与三维场景理解实践
  • K-8065M 无刷电机磁钢粘接专用环氧胶-低温80°C固化-技术参数与选型

日新闻

  • OpenClaw本地部署:一键直连微信的私有化AI Agent实战指南
  • Kubernetes 系列【10】控制器:ReplicaSet(副本集)
  • 怎么寄快递才能便宜呢?2026年7月寄快递省钱攻略 - 生活情报姬

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号