Skip to content
DAILY QUOTE

“ ”

1. 实体类与 @TableId(type = IdType.AUTO):交出控制权 表面上看,这只是一行注解。但在系统设计中,这是**“主键生成策略”**的选择。 当你配置了 AUTO,意味着 Java 层面在执行插入操作时,完全不会去管 ID 是多少,直接把除了 ID 之外的数据扔给 MySQL。MySQL 收到后,利用其内部的自增锁机制生成 ID。 深层意义:这保证了在极高并发的场景下,ID 绝对不会重复。同时也让 Java 内存少操一份心,将发号器的职责下放给最擅长处理并发冲突的数据库引擎。

2. Mapper 层的黑魔法:动态代理

这是一个非常经典的面试题:为什么只写了一个接口(Interface),没有任何实现类,代码就能直接去查数据库? 因为 MyBatis 在底层使用了 Java 的**动态代理(Dynamic Proxy)**机制。

  • 接口的本质:它只是一份“菜单”,声明了有哪些方法(比如 selectUserById)。

  • 底层的运转:当 Spring 启动时,MyBatis 发现你打上了 @Mapper 注解,它会在内存中偷偷“动态生成”一个实现了这个接口的虚拟类。这个虚拟类里面的逻辑,就是去读取你对应的 XML 文件(或者注解上的 SQL),把 SQL 语句发给 MySQL,拿到结果后再组装成你要的实体类返回。你调用接口,实际上调用的是这个存在于内存中的代理对象。

3. Service 层的“接口 + 实现类”模式:解耦与 AOP 很多初学者觉得这一层纯属脱裤子放屁,直接写一个类不好吗?企业级开发坚持这种模式有两大原因:

  • 规范与解耦:接口是给 Controller 调用的规范。假设未来你的订单服务要从单体架构拆分成微服务,或者底层存储从 MySQL 换成了其他数据库,只要接口的定义不改变,调用方(Controller)的代码一行都不用改。

  • 为了 Spring 的 AOP(面向切面编程):Spring 管理事务(@Transactional)的底层原理也是动态代理。Spring 更倾向于基于接口来创建代理对象。如果你没有接口直接写类,虽然也能跑,但在某些复杂的事务嵌套场景下,容易出现代理失效的问题。

4. 读写分离的逻辑与内存缝合

  • 写入(Upsert):先查后写。判断记录是否存在,存在则执行 Update(数量 +1),不存在则执行 Insert。这保证了数据的幂等性。

  • 读取(内存缝合):在查询购物车列表时,你提到了“多表查询与代码层面的数据缝合提取”。在大型互联网项目中,极度反感在 MySQL 里写复杂的多表 JOIN。因为数据库的 CPU 和内存资源极其宝贵,且难以横向扩展。标准的做法就是:先单表查出购物车数据(拿到商品 ID),再单表查出商品数据,最后在 Service 层的 Java 代码里,用 for 循环或者 Stream API 将它们拼接成完整的视图对象(VO)返回给前端。让便宜的 Java 服务器去承担计算和拼接的压力,保护脆弱的数据库。