jpa-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJPA/Hibernate Patterns
JPA/Hibernate 设计模式
Use for data modeling, repositories, and performance tuning in Spring Boot.
适用于Spring Boot中的数据建模、仓库和性能调优。
Entity Design
实体设计
java
@Entity
@Table(name = "markets", indexes = {
@Index(name = "idx_markets_slug", columnList = "slug", unique = true)
})
@EntityListeners(AuditingEntityListener.class)
public class MarketEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String name;
@Column(nullable = false, unique = true, length = 120)
private String slug;
@Enumerated(EnumType.STRING)
private MarketStatus status = MarketStatus.ACTIVE;
@CreatedDate private Instant createdAt;
@LastModifiedDate private Instant updatedAt;
}Enable auditing:
java
@Configuration
@EnableJpaAuditing
class JpaConfig {}java
@Entity
@Table(name = "markets", indexes = {
@Index(name = "idx_markets_slug", columnList = "slug", unique = true)
})
@EntityListeners(AuditingEntityListener.class)
public class MarketEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String name;
@Column(nullable = false, unique = true, length = 120)
private String slug;
@Enumerated(EnumType.STRING)
private MarketStatus status = MarketStatus.ACTIVE;
@CreatedDate private Instant createdAt;
@LastModifiedDate private Instant updatedAt;
}启用审计功能:
java
@Configuration
@EnableJpaAuditing
class JpaConfig {}Relationships and N+1 Prevention
关系与N+1查询问题规避
java
@OneToMany(mappedBy = "market", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PositionEntity> positions = new ArrayList<>();- Default to lazy loading; use in queries when needed
JOIN FETCH - Avoid on collections; use DTO projections for read paths
EAGER
java
@Query("select m from MarketEntity m left join fetch m.positions where m.id = :id")
Optional<MarketEntity> findWithPositions(@Param("id") Long id);java
@OneToMany(mappedBy = "market", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PositionEntity> positions = new ArrayList<>();- 默认使用懒加载;必要时在查询中使用
JOIN FETCH - 避免在集合上使用;读取路径使用DTO投影
EAGER
java
@Query("select m from MarketEntity m left join fetch m.positions where m.id = :id")
Optional<MarketEntity> findWithPositions(@Param("id") Long id);Repository Patterns
仓库设计模式
java
public interface MarketRepository extends JpaRepository<MarketEntity, Long> {
Optional<MarketEntity> findBySlug(String slug);
@Query("select m from MarketEntity m where m.status = :status")
Page<MarketEntity> findByStatus(@Param("status") MarketStatus status, Pageable pageable);
}- Use projections for lightweight queries:
java
public interface MarketSummary {
Long getId();
String getName();
MarketStatus getStatus();
}
Page<MarketSummary> findAllBy(Pageable pageable);java
public interface MarketRepository extends JpaRepository<MarketEntity, Long> {
Optional<MarketEntity> findBySlug(String slug);
@Query("select m from MarketEntity m where m.status = :status")
Page<MarketEntity> findByStatus(@Param("status") MarketStatus status, Pageable pageable);
}- 使用投影实现轻量查询:
java
public interface MarketSummary {
Long getId();
String getName();
MarketStatus getStatus();
}
Page<MarketSummary> findAllBy(Pageable pageable);Transactions
事务处理
- Annotate service methods with
@Transactional - Use for read paths to optimize
@Transactional(readOnly = true) - Choose propagation carefully; avoid long-running transactions
java
@Transactional
public Market updateStatus(Long id, MarketStatus status) {
MarketEntity entity = repo.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Market"));
entity.setStatus(status);
return Market.from(entity);
}- 为服务方法添加注解
@Transactional - 读取路径使用以优化性能
@Transactional(readOnly = true) - 谨慎选择传播机制;避免长时间运行的事务
java
@Transactional
public Market updateStatus(Long id, MarketStatus status) {
MarketEntity entity = repo.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Market"));
entity.setStatus(status);
return Market.from(entity);
}Pagination
分页处理
java
PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
Page<MarketEntity> markets = repo.findByStatus(MarketStatus.ACTIVE, page);For cursor-like pagination, include in JPQL with ordering.
id > :lastIdjava
PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
Page<MarketEntity> markets = repo.findByStatus(MarketStatus.ACTIVE, page);对于类似游标分页的需求,在JPQL中结合排序添加条件。
id > :lastIdIndexing and Performance
索引与性能优化
- Add indexes for common filters (,
status, foreign keys)slug - Use composite indexes matching query patterns ()
status, created_at - Avoid ; project only needed columns
select * - Batch writes with and
saveAllhibernate.jdbc.batch_size
- 为常用筛选字段(、
status、外键)添加索引slug - 创建与查询模式匹配的复合索引(如)
status, created_at - 避免;仅投影所需列
select * - 使用和
saveAll实现批量写入hibernate.jdbc.batch_size
Connection Pooling (HikariCP)
连接池(HikariCP)
Recommended properties:
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.validation-timeout=5000For PostgreSQL LOB handling, add:
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true推荐配置属性:
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.validation-timeout=5000针对PostgreSQL的LOB处理,添加:
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=trueCaching
缓存处理
- 1st-level cache is per EntityManager; avoid keeping entities across transactions
- For read-heavy entities, consider second-level cache cautiously; validate eviction strategy
- 一级缓存基于EntityManager;避免跨事务保留实体
- 对于读密集型实体,谨慎考虑二级缓存;验证驱逐策略
Migrations
数据库迁移
- Use Flyway or Liquibase; never rely on Hibernate auto DDL in production
- Keep migrations idempotent and additive; avoid dropping columns without plan
- 使用Flyway或Liquibase;生产环境绝不依赖Hibernate自动DDL
- 保持迁移的幂等性和增量性;无计划时避免删除列
Testing Data Access
数据访问测试
- Prefer with Testcontainers to mirror production
@DataJpaTest - Assert SQL efficiency using logs: set and
logging.level.org.hibernate.SQL=DEBUGfor parameter valueslogging.level.org.hibernate.orm.jdbc.bind=TRACE
Remember: Keep entities lean, queries intentional, and transactions short. Prevent N+1 with fetch strategies and projections, and index for your read/write paths.
- 优先使用结合Testcontainers以镜像生产环境
@DataJpaTest - 通过日志断言SQL效率:设置和
logging.level.org.hibernate.SQL=DEBUG查看参数值logging.level.org.hibernate.orm.jdbc.bind=TRACE
注意:保持实体精简,查询目标明确,事务简短。通过抓取策略和投影规避N+1问题,为读写路径创建合适的索引。