Spring-data-jpa基础

Java Persistence API(JPA) 是 Java 的 ORM 框架标准,它为管理关系数据提供了一个标准、基于面向对象的API,开发者可以用极简的代码即可实现对数据的访问和操作。JPA维护了一个 Persistence Context (持久化上下文),在持久化上下文中维护实体的生命周期。主要包含三个方面的内容:

  • ORM元数据。JPA支持annotion或xml两种形式描述对象-关系映射。
  • 实体操作API。实现对实体对象的CRUD操作。
  • 查询语言。约定了面向对象的查询语言JPQL(Java Persistence Query Language)

JPA核心接口

Repository 接口是 Spring Data 的一个核心接口, 是一个标记接口。业务接口继承Repository,则该接口会被IOC容器识别为一个Repository Bean注入到IOC容器中,只要遵循Spring Data的方法定义规范,就无需写实现类。

CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//保存单个实体
T save(T entity);
//保存集合
Iterable save(Iterable<? extends T> entities);
//根据id查找实体
T findOne(ID id);
//根据id判断实体是否存在
boolean exists(ID id);
//查询所有实体
Iterable findAll();
//查询实体数量
long count();
//根据Id删除实体
void delete(ID id);
//删除一个实体
void delete(T entity);
//删除一个实体的集合
void delete(Iterable<? extends T> entities);
//删除所有实体
void deleteAll();

PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法:

1
2
3
4
//排序
Iterable findAll(Sort sort);
//分页查询
Page findAll(Pageable pageable);

JpaRepository:实现了一组 JPA 规范相关的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
//查找所有实体
List findAll();
//排序、查找所有实体
List findAll(Sort sort);
//保存集合
List save(Iterable<? extends T> entities);
//执行缓存与数据库同步
void flush();
//强制执行持久化
T saveAndFlush(T entity);
//删除一个实体集合
void deleteInBatch(Iterable entities);

在实际开发中直接实现 JpaRepository 接口:

1
2
3
@Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
}

查询方式

JPA提供了多种查询方式实现:

  • JPA接口默认实现接口
  • 基于命名规则查询:find + [By + 属性名 + 查询条件] + [And/Or + 属性名 + 查询条件], 条件关键字如:
    • Like:模糊查询
    • In:集合包含查询
    • NotContaining:不包含查询
    • Containing:包含查询
    • OrderBy:排序
    • First/Top:限制结果数量
  • @Query 查询
    • 位置参数
    • 命名参数
    • SpEL表达

JPA接口默认实现接口

JpaRepository 实现了一些基本的默认 CRUD 方法:

1
2
3
4
5
List<T> findAll();
Page<T> findAll(Pageable pageable);
T getOne(ID id);
T S save(T entity);
void deleteById(ID id);

基于命名规则查询

find + [By + 属性名 + 查询条件] + [And/Or + 属性名 + 查询条件]

命名规则解析机制: Spring Data JPA会根据方法名称进行解析,它会根据方法名解析出查询条件和参数,将其转换为对应的JPQL查询。关键字与SQL对应关系如下:

Keyword JPQL snippet
And … where x.lastname = ?1 and x.firstname = ?2
Or … where x.lastname = ?1 or x.firstname = ?2
Is, Equals … where x.firstname = ?1
Between … where x.startDate between ?1 and ?2
LessThan … where x.age < ?1
LessThanEqual … where x.age ⇐ ?1
GreaterThan … where x.age > ?1
GreaterThanEqual … where x.age >= ?1
After … where x.startDate > ?1
Before … where x.startDate < ?1
IsNull … where x.age is null
IsNotNull, NotNull … where x.age not null
Like … where x.firstname like ?1
NotLike … where x.firstname not like ?1
StartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy … where x.age = ?1 order by x.lastname desc
Not … where x.lastname <> ?1
In … where x.age in ?1
NotIn … where x.age not in ?1
TRUE … where x.active = true
FALSE … where x.active = false
IgnoreCase … where UPPER(x.firstame) = UPPER(?1)

示例:

1
2
Order findByOrderId(Long orderId);
List<Order> findByUserId(Long userId);

@Query 注解查询

  • 位置参数
  • 命名参数
  • SpEL表达
1
2
3
4
5
6
7
8
9
10
11
// 位置参数
@Query("select count(o) from OrderEntity o where o.userId = ?1")
Long countWithFirstname(Long userId);

// 命名参数
@Query("select o from OrderEntity o where o.orderId = :orderId or o.userId = :userId")
List<OrderEntity> findByOrderIdAndUserId(@Param("orderId") Long orderId, @Param("userId") Long userId);

// SpEL表达
@Query("select o from OrderEntity o where o.userId = :#{#order.orderId}")
List<OrderEntity> findByOrderIdAsSpELExpression(@Param("order") OrderEntity order);

复杂条件查询

1
2
3
4
5
6
7
Specification<OrderEntity> specification = (root, criteriaQuery, criteriaBuilder) -> {
Subquery subQuery = criteriaQuery.subquery(String.class);
Root from = subQuery.from(OrderEntity.class);
subQuery.select(from.get("userId")).where(criteriaBuilder.equal(from.get("orderId"), "123456"));
return criteriaBuilder.and(root.get("userId").in(subQuery));
};
return orderRepository.findAll(specification);

基于命名规则实现机制

Spring Data JPA在启动时会解析Repository接口中的方法名称,根据命名规则将方法名转换为JPQL查询,如果有@Query注解,则使用注解中定义的查询语句。在底层通过通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象来处理这些方法。

代理对象创建核心类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class JpaRepositoryFactory extends RepositoryFactorySupport {

private final EntityManager entityManager;
private final QueryExtractor extractor;
private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor;
private final CrudMethodMetadata crudMethodMetadata;

....

public JpaRepositoryFactory(EntityManager entityManager) {

Assert.notNull(entityManager, "EntityManager must not be null");

this.entityManager = entityManager;
this.extractor = PersistenceProvider.fromEntityManager(entityManager);
this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor();
this.entityPathResolver = SimpleEntityPathResolver.INSTANCE;
this.queryMethodFactory = new DefaultJpaQueryMethodFactory(extractor);
this.queryRewriterProvider = QueryRewriterProvider.simple();

addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor);
addRepositoryProxyPostProcessor((factory, repositoryInformation) -> {
if (isTransactionNeeded(repositoryInformation.getRepositoryInterface())) {
// 创建 AOP 代理
factory.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
}
});
.....
}
}

RepositoryFactorySupport:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {
private final Map<RepositoryInformationCacheKey, RepositoryInformation> repositoryInformationCache;
private final List<RepositoryProxyPostProcessor> postProcessors;

private Optional<Class<?>> repositoryBaseClass;
private @Nullable QueryLookupStrategy.Key queryLookupStrategyKey;
private List<QueryCreationListener<?>> queryPostProcessors;
private List<RepositoryMethodInvocationListener> methodInvocationListeners;
private NamedQueries namedQueries;
private ClassLoader classLoader;
private QueryMethodEvaluationContextProvider evaluationContextProvider;
private BeanFactory beanFactory;
private Lazy<ProjectionFactory> projectionFactory;

.....
}

QueryExecutorMethodInterceptor:

1
2
3
4
5
6
7
8
9
10
11
12
13
class QueryExecutorMethodInterceptor implements MethodInterceptor {

private final RepositoryInformation repositoryInformation;
private final Map<Method, RepositoryQuery> queries;
private final Map<Method, RepositoryMethodInvoker> invocationMetadataCache = new ConcurrentReferenceHashMap<>();
private final Map<Method, MethodParameter> returnTypeMap = new ConcurrentHashMap<>();
private final QueryExecutionResultHandler resultHandler;
private final NamedQueries namedQueries;
private final List<QueryCreationListener<?>> queryPostProcessors;
private final RepositoryInvocationMulticaster invocationMulticaster;

....
}

代理创建主要步骤

  • 配置启动方式:
1
2
3
@EnableJpaRepositories(
bootstrapMode = BootstrapMode.DEFERRED // 可选:DEFAULT, LAZY, DEFERRED
)
  • Repository 接口检测:

    • 扫描 @EnableJpaRepositories 注解配置的包路径
    • 找到继承了 Repository 接口的接口
  • 创建 RepositoryFactoryBean:

1
2
3
4
5
6
7
8
public class JpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID> 
extends RepositoryFactoryBeanSupport<T, S, ID> {

@Override
protected RepositoryFactorySupport createRepositoryFactory() {
return new JpaRepositoryFactory(entityManager);
}
}

代理实现的核心机制

  • 基础实现类:
1
2
3
4
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return SimpleJpaRepository.class;
}
  • 查询方法的解析和代理
1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
ValueExpressionDelegate valueExpressionDelegate) {

JpaQueryConfiguration queryConfiguration = new JpaQueryConfiguration(/*...*/);
return Optional.of(JpaQueryLookupStrategy.create(
entityManager,
queryMethodFactory,
key,
queryConfiguration
));
}
  • 方法拦截器:
1
2
3
4
5
6
7
8
public JpaRepositoryFactory(EntityManager entityManager) {
// ...
addRepositoryProxyPostProcessor((factory, repositoryInformation) -> {
if (isTransactionNeeded(repositoryInformation.getRepositoryInterface())) {
factory.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
}
});
}
  • 代理对象的创建过程: CrudMethodMetadata 代理创建
1
2
3
4
5
6
7
8
class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor {
CrudMethodMetadata getCrudMethodMetadata() {
ProxyFactory factory = new ProxyFactory();
factory.addInterface(CrudMethodMetadata.class);
factory.setTargetSource(new ThreadBoundTargetSource());
return (CrudMethodMetadata) factory.getProxy(this.classLoader);
}
}
  • 动态方法实现
    • 对于查询方法,生成对应的 JPQL 或 SQL
    • 对于默认方法,使用 SimpleJpaRepository 中的实现
    • 对于自定义查询(@Query),使用注解中定义的查询语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected RepositoryFragments getRepositoryFragments(
RepositoryMetadata metadata,
EntityManager entityManager,
EntityPathResolver resolver,
CrudMethodMetadata crudMethodMetadata) {

boolean isQueryDslRepository = QUERY_DSL_PRESENT
&& QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface());

if (isQueryDslRepository) {
....
QuerydslJpaPredicateExecutor<?> querydslJpaPredicateExecutor = new QuerydslJpaPredicateExecutor<>(
getEntityInformation(metadata.getDomainType()), entityManager, resolver, crudMethodMetadata);
invokeAwareMethods(querydslJpaPredicateExecutor);
return RepositoryFragments.just(querydslJpaPredicateExecutor);
}
return RepositoryFragments.empty();
}
  • QueryDSL支持
1
2
3
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
// 添加 QuerydslJpaPredicateExecutor 支持
}

小结

整个过程的工作流程:

  • 启动时扫描 Repository 接口
  • 为每个接口创建 JpaRepositoryFactory
  • 通过工厂创建代理对象:
    • 基础实现来自 SimpleJpaRepository
    • 查询方法通过方法名或注解解析
    • 添加事务、缓存等切面
  • 注入代理对象到使用处

实体注解

基础注解

@Entity

标记一个类为实体类,表示该类将映射到数据库表。

1
2
@Entity
public class Employee { ... }

@Table

指定实体类映射到的数据库表信息。

1
2
3
4
@Entity
@Table(name = "employees", schema = "hr",
uniqueConstraints = {@UniqueConstraint(columnNames = {"email", "company_id"})})
public class Employee { ... }

@Id

标记实体类的主键字段。

1
2
@Id
private Long id;

@GeneratedValue

指定主键的生成策略。

1
2
3
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

生成策略包括:

  • GenerationType.IDENTITY: 依赖数据库的自增长特性
  • GenerationType.SEQUENCE: 使用数据库序列生成主键
  • GenerationType.TABLE: 使用表来模拟序列
  • GenerationType.AUTO: 由持久性提供者自动选择合适的策略

@Column

指定字段映射到表中列的详细信息。

1
2
@Column(name = "user_name", length = 50, nullable = false, unique = true)
private String username;

主要属性:

  • name: 列名(默认为字段名)
  • length: 列长度
  • nullable: 是否允许为空
  • unique: 是否唯一约束
  • insertable: 插入时是否包含此字段
  • updatable: 更新时是否包含此字段
  • precision: 浮点数的精度
  • scale: 浮点数的小数位数

@Transient

标记字段不会被持久化到数据库。

1
2
@Transient
private String tempValue;

@Temporal

指定日期类型字段映射到的SQL日期类型。

1
2
3
4
5
@Temporal(TemporalType.DATE)
private Date birthDate;

@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;

类型包括:

  • TemporalType.DATE: 只包含日期信息
  • TemporalType.TIME: 只包含时间信息
  • TemporalType.TIMESTAMP: 包含日期和时间信息

有用的注解

@Version

用于实现乐观锁。

1
2
@Version
private Long version;

@Where

定义实体类的查询条件。

1
2
3
@Entity
@Where(clause = "deleted = false")
public class Document { ... }

@Formula (Hibernate特有) - 实体字段直接映射SQL表达式,替代重复计算逻辑

定义计算列。

1
2
@Formula("(SELECT COUNT(c.id) FROM comments c WHERE c.post_id = id)")
private Long commentCount;

@SQLRestriction:实体级原生SQL条件,简化动态查询

1
2
3
4
5
6
7
8
9
10
@Entity
@Table(name = "product")
@SQLRestriction("deleted = 0")
public class Product {
// ...其它属性

/**0: 未删除, 1: 已删除*/
@Column(columnDefinition = "int default 0")
private Integer deleted ;
}

@Filter: 参数化过滤条件,支持会话级控制

1
2
3
4
5
6
7
8
9
10
@Entity
@Table(name = "product")
@FilterDef(name = "filterByDeletedAndStock", parameters = {
@ParamDef(name = "state", type = Integer.class),
@ParamDef(name = "stock", type = Integer.class)
})
@Filters({
@Filter(name = "filterByDeletedAndStock", condition = "deleted=:state and stock >:stock")
})
public class Product {}

Spring-data-jpa基础
http://example.com/2025/06/10/spring-data-jpa基础/
作者
ares
发布于
2025年6月10日
许可协议