Spring Data JPA 深入与性能优化教程
第一章:实体映射详解
1.1 关联关系
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "payment_id", unique = true)
private Payment payment;
}
@Entity
public class OrderItem {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
private Integer quantity;
}
1.2 FetchType 策略
| FetchType |
行为 |
风险 |
| LAZY(推荐) |
使用时才加载 |
需事务内访问,否则 LazyInitializationException |
| EAGER |
立即加载 |
可能导致 N+1 或 Cartesian 积 |
1.3 Cascade 级联操作
@OneToMany(mappedBy = "order",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
orphanRemoval = true)
private List<OrderItem> items;
第二章:N+1 问题与解决方案
2.1 问题示例
List<User> users = userRepository.findAll(); // 1 条 SQL: SELECT * FROM users
for (User user : users) {
System.out.println(user.getOrders().size()); // 10 条 SQL: SELECT * FROM orders WHERE user_id=?
}
2.2 解决方案
public interface UserRepository extends JpaRepository<User, Long> {
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();
}
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();
@Entity
public class User {
@BatchSize(size = 100)
@OneToMany(mappedBy = "user")
private List<Order> orders;
}
第三章:审计与乐观锁
3.1 自动审计
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
@Entity
public class Product extends BaseEntity {
}
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class JpaConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of(SecurityContextHolder.getContext()
.getAuthentication().getName());
}
}
3.2 乐观锁
@Entity
public class Product {
@Version
private Long version;
}
第四章:性能最佳实践
4.1 批量操作
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
@Autowired
private JdbcTemplate jdbcTemplate;
public void batchInsert(List<Product> products) {
jdbcTemplate.batchUpdate(
"INSERT INTO products(name, price, stock) VALUES (?, ?, ?)",
products, 100,
(ps, product) -> {
ps.setString(1, product.getName());
ps.setDouble(2, product.getPrice());
ps.setInt(3, product.getStock());
}
);
}
4.2 只读优化
@Transactional(readOnly = true)
public List<Product> findAllProducts() {
return productRepository.findAll();
}
4.3 DTO 投影
public interface ProductSummary {
String getName();
Double getPrice();
}
@Query("SELECT p.name AS name, p.price AS price FROM Product p")
List<ProductSummary> findSummaries();
@Query("SELECT new com.example.dto.ProductDto(p.name, p.price) FROM Product p")
List<ProductDto> findDtos();
思考题
- 为什么在多对一关联中推荐使用
FetchType.LAZY?
@Transactional(readOnly = true) 在 JPA 中的具体优化是什么?
- 如何避免批量处理中的 OOM(内存溢出)?
- JPA 的 Persistence Context 和一级缓存有什么关系?