在Spring Data中,Spring Data提供了两大魔法:
我们下面将以Spring Data JPA为例深入理解这两大魔法的实现(下面的类都没有写包名,在Intellij IDEA下双击SHIFT,将类名复制进去可便捷查找):
在Spring Data JPA中我们将我们的实体Repository
继承了JpaRepository
接口便能获得大量的数据库操作方法,如:
public interface PersonRepository extends JpaRepository<Person, Long> { }
但JpaRepository
只是一个接口:
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
@Override
List<T> findAll();
@Override
List<T> findAll(Sort sort);
@Override
List<T> findAllById(Iterable<ID> ids);
@Override
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
它不具备任何实际的可执行代码,它的功能实现都是由SimpleJpaRepository
类来提供的,那我们自定义的Repository
(PersonRepository
)是如何获得SimpleJpaRepository
中提供的实现的呢,
请看下面调用过程的分析:
@EnableJpaRepositories
在@EnableJpaRepositories
中:指定默认的JpaRepositoryFactoryBean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {
/**
* Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to
* {@link JpaRepositoryFactoryBean}.
*
* @return
*/
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
}
JpaRepositoryFactoryBean
在JpaRepositoryFactoryBean
中创建JpaRepositoryFactory
(RepositoryFactorySupport
的子类)
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
jpaRepositoryFactory.setEscapeCharacter(escapeCharacter);
if (queryMethodFactory != null) {
jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
}
return jpaRepositoryFactory;
}
JpaRepositoryFactory
在JpaRepositoryFactory
中:指定默认的repositoryBaseClass
,为SimpleJpaRepository
public class JpaRepositoryFactory extends RepositoryFactorySupport {
//...
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return SimpleJpaRepository.class;
}
//..
}
RepositoryFactorySupport
RepositoryFactorySupport
:它是JpaRepositoryFactory
的父类,它通过JpaRepositoryFactory
中指定的SimpleJpaRepository
,将SimpleJpaRepository
代理给我们自定义的Repository
接口
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
//...
Object target = getTargetRepository(information); //1 target为SimpleJpaRepository
// Create proxy
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class); //2 repositoryInterface为我们自定义的Repository(PersonRepository)接口
//..
return repository;
}
Spring Data JPA的另外一个神奇之处是可以根据实体接口中定义的方法名而自动进行查询。这个又是如何实现的呢,我们继续分析:
@EnableJpaRepositories
@EnableJpaRepositories
中:指定默认的JpaRepositoryFactoryBean
和查询策略
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {
/**
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
* {@link Key#CREATE_IF_NOT_FOUND}.
*
* @return
*/
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
/**
* Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to
* {@link JpaRepositoryFactoryBean}.
*
* @return
*/
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
}
JpaRepositoryFactory
JpaRepositoryFactory
中:通过我们上面指定的queryLookupStrategy
获取查询策略
public class JpaRepositoryFactory extends RepositoryFactorySupport {
//...
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of(JpaQueryLookupStrategy.create(entityManager, queryMethodFactory, key, evaluationContextProvider,
escapeCharacter)); //通过JpaQueryLookupStrategy.create(...)创建策略
}
//..
}
JpaQueryLookupStrategy
JpaQueryLookupStrategy
:通过Key来创建不通的查询策略,推导查询由CreateQueryLookupStrategy
侧列实现
public final class JpaQueryLookupStrategy {
//...
public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
@Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, EscapeCharacter escape) {
Assert.notNull(em, "EntityManager must not be null!");
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!");
switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) {
case CREATE:
return new CreateQueryLookupStrategy(em, queryMethodFactory, escape);
case USE_DECLARED_QUERY:
return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider);
case CREATE_IF_NOT_FOUND: // 默认符合此条件
return new CreateIfNotFoundQueryLookupStrategy(em, queryMethodFactory,
new CreateQueryLookupStrategy(em, queryMethodFactory, escape), //推导查询由此策略实现
new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider));
default:
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
//...
}
CreateQueryLookupStrategy
CreateQueryLookupStrategy
:为JpaQueryLookupStrategy
的内部静态私有类,它指定了我们的查询方式为PartTreeJpaQuery
public final class JpaQueryLookupStrategy {
//...
private static class CreateQueryLookupStrategy extends AbstractQueryLookupStrategy {
private final EscapeCharacter escape;
public CreateQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
EscapeCharacter escape) {
super(em, queryMethodFactory);
this.escape = escape;
}
@Override
protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) {
return new PartTreeJpaQuery(method, em, escape); //此处定义了
}
}
//...
}
Part
方法名中将会被转换成查询的部分(Part
),实际的转换由Part.Type
来定义:
public class Part {
//...
/**
* The type of a method name part. Used to create query parts in various ways.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Michael Cramer
*/
public static enum Type {
BETWEEN(2, "IsBetween", "Between"),
IS_NOT_NULL(0, "IsNotNull", "NotNull"),
IS_NULL(0, "IsNull", "Null"),
LESS_THAN("IsLessThan", "LessThan"),
LESS_THAN_EQUAL("IsLessThanEqual", "LessThanEqual"),
GREATER_THAN("IsGreaterThan","GreaterThan"),
GREATER_THAN_EQUAL("IsGreaterThanEqual", "GreaterThanEqual"),
BEFORE("IsBefore","Before"),
AFTER("IsAfter", "After"),
NOT_LIKE("IsNotLike", "NotLike"),
LIKE("IsLike","Like"),
STARTING_WITH("IsStartingWith", "StartingWith", "StartsWith"),
ENDING_WITH("IsEndingWith","EndingWith", "EndsWith"),
IS_NOT_EMPTY(0, "IsNotEmpty", "NotEmpty"),
IS_EMPTY(0, "IsEmpty", "Empty"),
NOT_CONTAINING("IsNotContaining", "NotContaining", "NotContains"),
CONTAINING("IsContaining", "Containing", "Contains"),
NOT_IN("IsNotIn", "NotIn"),
IN("IsIn", "In"),
NEAR("IsNear", "Near"),
WITHIN("IsWithin", "Within"),
REGEX("MatchesRegex", "Matches", "Regex"),
EXISTS(0, "Exists"), TRUE(0, "IsTrue", "True"),
FALSE(0, "IsFalse", "False"),
NEGATING_SIMPLE_PROPERTY("IsNot", "Not"),
SIMPLE_PROPERTY("Is", "Equals");
//..
}
//...
}
PartTree
通过此类将方法名字符串解析成树,而由简单的Part
实例。接收实体类作为参数来验证每一个Part
都是参考的都是实体类的属性。PartTree
主要又Subject
和Predicate
组成:
public class PartTree implements Streamable<OrPart> {
//...
/**
* 如 "findDistinctUserByNameOrderByAge" 的subject是"DistinctUser".
*/
private final Subject subject;
/**
* 如 findDistinctUserByNameOrderByAge" 的predicate是"NameOrderByAge",此处非JPA的Predicate
*/
private final Predicate predicate;
//..
}
PartTreeJpaQuery
包含了一个解析了方法名的PartTree
,通过JpaQueryCreator
使用PartTree
构造JPA查询:
public class PartTreeJpaQuery extends AbstractJpaQuery {
private final PartTree tree; //包含一个PartTree
//...
private final QueryPreparer query;
private final QueryPreparer countQuery;
//...
private class QueryPreparer {
/**
* Creates a new {@link Query} for the given parameter values.
*/
public Query createQuery(JpaParametersParameterAccessor accessor) {
//...
if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) {
JpaQueryCreator creator = createCreator(accessor); //
criteriaQuery = creator.createQuery(getDynamicSort(accessor));
List<ParameterMetadata<?>> expressions = creator.getParameterExpressions();
parameterBinder = getBinder(expressions);
}
//...
}
protected JpaQueryCreator createCreator(@Nullable JpaParametersParameterAccessor accessor) {
EntityManager entityManager = getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
ResultProcessor processor = getQueryMethod().getResultProcessor();
ParameterMetadataProvider provider;
ReturnedType returnedType;
if (accessor != null) {
provider = new ParameterMetadataProvider(builder, accessor, escape);
returnedType = processor.withDynamicProjection(accessor).getReturnedType();
} else {
provider = new ParameterMetadataProvider(builder, parameters, escape);
returnedType = processor.getReturnedType();
}
return new JpaQueryCreator(tree, returnedType, builder, provider);
}
}
//...
}
AbstractQueryCreator
与子类JpaQueryCreator
它们通过PartTree
来构造JPA查询:
public abstract class AbstractQueryCreator<T, S> {
private final Optional<ParameterAccessor> parameters;
private final PartTree tree;
//...
/**
* 实际的查询构造逻辑。遍历PartTree
*
*
* @param tree must not be {@literal null}.
* @return
*/
@Nullable
private S createCriteria(PartTree tree) {
S base = null;
Iterator<Object> iterator = parameters.map(ParameterAccessor::iterator).orElse(Collections.emptyIterator());
for (OrPart node : tree) {
Iterator<Part> parts = node.iterator();
if (!parts.hasNext()) {
throw new IllegalStateException(String.format("No part found in PartTree %s!", tree));
}
S criteria = create(parts.next(), iterator); //使用子类JpaQueryCreator的create方法
while (parts.hasNext()) {
criteria = and(parts.next(), criteria, iterator); //使用子类JpaQueryCreator的and方法
}
base = base == null ? criteria : or(base, criteria);//使用子类JpaQueryCreator的or方法
}
return base;
}
//...
}
public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extends Object>, Predicate> {
private final CriteriaBuilder builder;
private final Root<?> root;
private final CriteriaQuery<? extends Object> query;
private final ParameterMetadataProvider provider;
private final ReturnedType returnedType;
private final PartTree tree;
private final EscapeCharacter escape;
//...
@Override
protected Predicate create(Part part, Iterator<Object> iterator) {
return toPredicate(part, root);
}
@Override
protected Predicate and(Part part, Predicate base, Iterator<Object> iterator) {
return builder.and(base, toPredicate(part, root));
}
@Override
protected Predicate or(Predicate base, Predicate predicate) {
return builder.or(base, predicate);
}
//..
private Predicate toPredicate(Part part, Root<?> root) {
return new PredicateBuilder(part, root).build();
}
/**
*
* 通过此builder从Part中构造JPA的Predicate,此类代码较多,专门脱出来
* @author Phil Webb
* @author Oliver Gierke
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private class PredicateBuilder {
//...
}
}
PredicateBuilder
@SuppressWarnings({ "unchecked", "rawtypes" })
private class PredicateBuilder {
private final Part part;
private final Root<?> root;
//...
/**
* 通过Part构造JPA的Predicate
*
* @return
*/
public Predicate build() {
PropertyPath property = part.getProperty();
Type type = part.getType();
switch (type) {
case BETWEEN:
ParameterMetadata<Comparable> first = provider.next(part);
ParameterMetadata<Comparable> second = provider.next(part);
return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression());
case AFTER:
case GREATER_THAN:
return builder.greaterThan(getComparablePath(root, part),
provider.next(part, Comparable.class).getExpression());
case GREATER_THAN_EQUAL:
return builder.greaterThanOrEqualTo(getComparablePath(root, part),
provider.next(part, Comparable.class).getExpression());
case BEFORE:
case LESS_THAN:
return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression());
case LESS_THAN_EQUAL:
return builder.lessThanOrEqualTo(getComparablePath(root, part),
provider.next(part, Comparable.class).getExpression());
case IS_NULL:
return getTypedPath(root, part).isNull();
case IS_NOT_NULL:
return getTypedPath(root, part).isNotNull();
case NOT_IN:
// cast required for eclipselink workaround, see DATAJPA-433
return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression()).not();
case IN:
// cast required for eclipselink workaround, see DATAJPA-433
return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression());
case STARTING_WITH:
case ENDING_WITH:
case CONTAINING:
case NOT_CONTAINING:
if (property.getLeafProperty().isCollection()) {
Expression<Collection<Object>> propertyExpression = traversePath(root, property);
ParameterExpression<Object> parameterExpression = provider.next(part).getExpression();
// Can't just call .not() in case of negation as EclipseLink chokes on that.
return type.equals(NOT_CONTAINING) ? isNotMember(builder, parameterExpression, propertyExpression)
: isMember(builder, parameterExpression, propertyExpression);
}
case LIKE:
case NOT_LIKE:
Expression<String> stringPath = getTypedPath(root, part);
Expression<String> propertyExpression = upperIfIgnoreCase(stringPath);
Expression<String> parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression());
Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter());
return type.equals(NOT_LIKE) || type.equals(NOT_CONTAINING) ? like.not() : like;
case TRUE:
Expression<Boolean> truePath = getTypedPath(root, part);
return builder.isTrue(truePath);
case FALSE:
Expression<Boolean> falsePath = getTypedPath(root, part);
return builder.isFalse(falsePath);
case SIMPLE_PROPERTY:
ParameterMetadata<Object> expression = provider.next(part);
Expression<Object> path = getTypedPath(root, part);
return expression.isIsNullParameter() ? path.isNull()
: builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression()));
case NEGATING_SIMPLE_PROPERTY:
return builder.notEqual(upperIfIgnoreCase(getTypedPath(root, part)),
upperIfIgnoreCase(provider.next(part).getExpression()));
case IS_EMPTY:
case IS_NOT_EMPTY:
if (!property.getLeafProperty().isCollection()) {
throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!");
}
Expression<Collection<Object>> collectionPath = traversePath(root, property);
return type.equals(IS_NOT_EMPTY) ? builder.isNotEmpty(collectionPath) : builder.isEmpty(collectionPath);
default:
throw new IllegalArgumentException("Unsupported keyword " + type);
}
}
}
我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html
主要包含目录有:
第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和批处理(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。