JPA 实体映射
JPA 用注解把类映射到表:@Entity、@Id、@GeneratedValue、@OneToMany 等。Hibernate 是主流实现。
@Entity
@Table(name = "orders")
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal total;
@Enumerated(EnumType.STRING)
private OrderStatus status;
}关联集合默认懒加载,遍历时每条触发一次查询。用 @EntityGraph 或 join fetch 一次性取出,避免性能灾难。
Repository
继承 JpaRepository 即可获得 CRUD;方法名自动推导查询,或写 @Query(JPQL/原生 SQL)。
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByStatus(OrderStatus status);
@Query("select o from Order o where o.total > :t")
List<Order> bigOrders(@Param("t") BigDecimal t);
}MyBatis 范式
MyBatis 把 SQL 写在 XML/Mapper 里,对 SQL 掌控更强,适合复杂查询与报表。与 JPA"全自动"互补。
@Mapper
public interface UserMapper {
@Select("select * from users where id = #{id}")
User selectById(@Param("id") Long id);
}CRUD 多、模型驱动选 JPA 提效;SQL 复杂、需精细控制执行计划选 MyBatis。一个项目可两者并存。
事务基础
@Transactional 声明方法在事务中运行,成功提交、抛异常回滚。默认只在 public 方法且由容器调用时生效。
@Transactional
public void placeOrder(OrderCmd cmd) {
orderRepo.save(order);
inventoryRepo.decrease(cmd.getSku(), cmd.getQty());
// 任一失败整体回滚
}事务传播
传播行为决定"被调用方法如何加入现有事务":REQUIRED(加入或新建)、REQUIRES_NEW(挂起开新)、NESTED 等。
| 传播 | 行为 |
|---|---|
| REQUIRED | 有则加入,无则新建(最常用) |
| REQUIRES_NEW | 挂起当前,开独立新事务 |
| SUPPORTS | 有则用,无则非事务 |
同类里方法 A 调 @Transactional 方法 B,事务不生效(代理未介入)。把 B 抽到另一个 Bean 或用 AOP 代理调用。
连接池 HikariCP
HikariCP 是 Spring Boot 默认连接池,快且稳。合理配置 maximumPoolSize,避免耗尽数据库或被拒绝。
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 3000数据库迁移
用 Flyway 或 Liquibase 把 schema 变更纳入版本管理,按序执行、可回滚,避免手工改库带来的环境漂移。
-- V1__create_order.sql
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
total DECIMAL(12,2) NOT NULL,
status VARCHAR(20) NOT NULL
);生产加索引用 CREATE INDEX CONCURRENTLY(PostgreSQL)等在线手段,避免锁表导致长时间不可写。
常见陷阱与 FAQ
Spring 事务基于代理,只能拦截 public 且经容器调用的方法。private/内部自调用都不会走代理。
事务在 Service 已提交,游离态实体再访问懒加载关联会抛 LazyInitializationException。用 DTO 投影或 @EntityGraph 在事务内取好数据。
取决于 DB 承载与并发。先小(如 CPU 核数*2+)再依据监控调整,避免超过数据库 max_connections。