实体类与@TableId
作用:
@TableId(type=IdType.AUTO)
private Long id;IdType.AUTO表示主键由数据库自增生成。
也就是说,Java在执行insert时,不主动给id赋值,而是把数据交给MYSQL,由MYSQL根据表中的AUTO_INCREMENT规则生成主键。
也就是把主键的生成交给了数据库。
优点:
- 简单,不需要Java自己维护 ID
- 单库单表场景下,能保证主键不重复
- MySQL 自增机制成熟,适合普通业务表
但如果时分布式系统、多库多表、高并发写入等场景,数据库自增ID可能遇到:
- 单点依赖数据库
- 分库分表时ID不好统一
- 暴露数据增长趋势
- 高并发下自增锁可能成为瓶颈
故在复杂场景优先考虑雪花算法、号段模式、Redis自增等其他ID生成方案。
Mapper层:查数据库实现
- 核心原因:动态代理
在MyBatis中,我们通常只写Mapper接口:
@Mapper
public interface UserMapper extends BaseMapper<User>{
}但没有实现类,程序却可以正常执行:
userMapper.selectById(1L);因为MyBatis地城使用了动态代理机制
- Mapper接口本质:
Mapper接口本身只是声明方法,他告诉MyBatis:这个接口里有哪些数据库操作方法,但真正的执行逻辑时MyBatis在运行时创建的代理对象完成的。
运行流程:
Spring 启动
↓
扫描到 @Mapper 接口
↓
MyBatis 为这个接口创建代理对象
↓
Controller / Service 调用 Mapper 方法
↓
代理对象拦截方法调用
↓
找到对应 SQL
↓
执行 SQL
↓
把结果封装成实体类返回Service层:接口与实现类
常规写法:
public interface UserService extends IService<User> {
}@Service
public class UserServiceImpl
extends ServiceImpl<UserMapper, User>
implements UserService {
}- 接口存在的意义:
分层规范: Controller不应该直接操作Mapper,而应该调用Service
Controller
↓
Service
↓
Mapper
↓
Database解耦: Controller依赖的是接口,
private final UserService userService;而不是直接依赖实现类
private final UserServiceImpl userServiceImpl;好处:以后实现方式变了,比如
1. 本地 Service 实现
2. 远程 RPC 实现
3. 缓存增强实现
4. 不同数据库实现只要UserService接口不变,Controller基本不用改
方便SpringAop与事务管理
Spring很多功能,比如
@Transactional
日志切面
权限校验
性能监控
缓存增强底层都依赖代理机制
Service是业务逻辑集中的地方,所以AOP通常加载Service层
@Transactional
public void createOrder() {
// 创建订单
// 扣减库存
// 写入日志
}Spring会为Service创建代理对象,在方法执行前后增加事务逻辑
读取设计
查询购物车列表,通常不只需要购物车表数据,还需要商品表数据。
购物车表可能有:
cart_id
user_id
product_id
quantity商品表可能有:
product_id
product_name
price
image
stock前端需要的是一个完整展示对象,比如:
商品名称
商品图片
商品价格
购买数量
小计金额这就需要把购物车比数据与商品数据组合起来。
- 使用SQL JOIN 直接使用多表查询:
SELECT c.id, c.quantity, p.name, p.price, p.image
FROM cart c
JOIN product p ON c.product_id = p.id
WHERE c.user_id = ?一次就能查出完整结果,但复杂JOIN会增加数据库压力,SQL维护成本可能变高。
- 代码缝合 也可以分两步查询:
1. 先查购物车表,拿到 product_id
2. 再查商品表,拿到商品信息
3. 在 Java Service 层组装成 VO 返回给前端流程:
查询购物车列表
↓
提取商品ID集合
↓
批量查询商品信息
↓
转成 Map
↓
遍历购物车数据
↓
组装 CartVO
↓
返回前端大型项目应减少复杂JOIN,原因:
1. 数据库 CPU 和内存资源比较宝贵
2. 复杂 JOIN 可能影响索引使用
3. 数据库不容易像 Java 服务一样水平扩展
4. 分库分表后跨库 JOIN 很麻烦
5. Service 层组装更方便加缓存、权限控制和业务逻辑将一部分计算和拼接压力交给Java应用服务器,减轻数据库压力。