Spring Data 魔法实现的原理(以Spring Data JPA为例)

在Spring Data中,Spring Data提供了两大魔法:

  • 通过继承Repository接口获得数据库操作的常用方法
  • 通过方法名即可进行推导查询

我们下面将以Spring Data JPA为例深入理解这两大魔法的实现(下面的类都没有写包名,在Intellij IDEA下双击SHIFT,将类名复制进去可便捷查找):

1、Repository接口如何获得常用的数据库操作方法

在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类来提供的,那我们自定义的RepositoryPersonRepository)是如何获得SimpleJpaRepository中提供的实现的呢,

请看下面调用过程的分析:

1.1 @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;

}

1.2 JpaRepositoryFactoryBean

JpaRepositoryFactoryBean中创建JpaRepositoryFactoryRepositoryFactorySupport的子类)

protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
	
		JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
		jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
		jpaRepositoryFactory.setEscapeCharacter(escapeCharacter);

		if (queryMethodFactory != null) {
			jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
		}

		return jpaRepositoryFactory;
	}

1.3 JpaRepositoryFactory

JpaRepositoryFactory中:指定默认的repositoryBaseClass,为SimpleJpaRepository

public class JpaRepositoryFactory extends RepositoryFactorySupport {
  //...
  @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
      return SimpleJpaRepository.class;
    }
  //..
}  

1.4 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;
	}

2、如何实现根据方法名推导查询(Derived Query)

Spring Data JPA的另外一个神奇之处是可以根据实体接口中定义的方法名而自动进行查询。这个又是如何实现的呢,我们继续分析:

2.1 @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;

}

2.2 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(...)创建策略
    }
  //..
  }

2.3 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));
		
//...
}

2.4 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); //此处定义了
		}
	}
 //...
}

2.5 核心类分析

2.5.1 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");
  //..
  }
  //...
}

2.5.2 PartTree

通过此类将方法名字符串解析成树,而由简单的Part实例。接收实体类作为参数来验证每一个Part都是参考的都是实体类的属性。PartTree主要又SubjectPredicate组成:

public class PartTree implements Streamable<OrPart> {
  //...
	/**
	 * 如 "findDistinctUserByNameOrderByAge" 的subject是"DistinctUser".
	 */
	private final Subject subject;

	/**
	 * 如 findDistinctUserByNameOrderByAge" 的predicate是"NameOrderByAge",此处非JPA的Predicate
	 */
	private final Predicate predicate;
  //..
}

2.5.3 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); 
		}
    
  }
  
	//...
	}

2.5.4 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 {
    //...
  }
	
	}

2.5.5 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)
多谢大家支持。

你可能感兴趣的:(Spring魔法解密,spring,data,spring,data,jpa,Part,PartTree)