【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

 Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)

8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)

10、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理

12、【源码】SpringBoot事务注册原理

13、【源码】Spring Data JPA原理解析之事务注册原理

14、【源码】Spring Data JPA原理解析之事务执行原理

15、【源码】SpringBoot编程式事务使用及执行原理

16、【源码】Spring事务之传播特性的详解

17、【源码】Spring事务之事务失效及原理

18、【源码】Spring Data JPA原理解析之Hibernate EventListener使用及原理

19、【源码】Spring Data JPA原理解析之Auditing执行原理

20、Spring Data JPA使用及实现原理总结

前言

在前一篇文章

【源码】Spring Data JPA原理解析之Repository的自动注入(二)-CSDN博客

分享了Repository bean的创建。Respository的bean是一个通过ProxyFactory创建的动态代理对象,该代理对象实现了自定义的Repository接口、Repository.class以及TransactionProxy.class接口,且代理的target为SimpleJpaRepository对象。

在开发中,只需自定义实现JpaRepository接口,就可以实现CRUD、findBy+属性名称+条件查询等许多查询功能,核心在于使用了动态代理,其中的save、delete、find等功能的实现就在SimpleJpaRepository。

本篇分享一下SimpleJpaRepository的原理,在开始介绍之前,先了解一下动态代理是如何执行而后调用执行到SimpleJpaRepository。

动态代理方法拦截

Spring框架的两大核心概念:IOC(控制反转,把创建对象过程交给Spring进行管理)和AOP(面向切面,不修改源代码进行功能增强)。前面讲解的两篇Repository的自动注入使用的是Spring的IOC,而Repository的代理对象使用的是AOP。AOP是一种编程思想,是基于动态代理实现。

限于篇幅,此处只分享核心的实现。

Respository的bean是一个通过ProxyFactory创建的动态代理对象,ProxyFactory的父类的是ProxyCreatorSupport。在ProxyCreatorSupport的构造方法中,会new一个DefaultAopProxyFactory()。

2.1 DefaultAopProxyFactory

DefaultAopProxyFactory为默认的AOP代理创建工厂,代码如下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// config.isOptimize()与config.isProxyTargetClass()默认返回都是false,可以通过config进行配置修改
		// 需要优化 || 强制cglib || 没有实现接口
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			// 倘若目标Class本身就是个接口,或者它已经是个JDK的代理类(Proxy的子类。所有的JDK代理类都是此类的子类),
			// 用JDK的动态代理。Proxy.isProxyClass(targetClass)方法判断targetClass是否是Proxy通过
			// getProxyClass()或newProxyInstance()创建的
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 实用CGLIB代理方式
			// ObjenesisCglibAopProxy是CglibAopProxy的子类。Spring4.0之后提供的
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			// 否则采用JDK得动态代理。(一般都有实现接口)
			return new JdkDynamicAopProxy(config);
		}
	}

	/**
	 * 判断该代理对象是否没有用户提供的代理接口,是返回true
	 * 
	 * 如果它没有实现过接口(ifcs.length == 0)||
	 * 仅仅实现了一个接口,但是这个接口却是SpringProxy类型的。返回false
	 * 其中SpringProxy:一个标记接口。Spring AOP产生的所有的代理类都是它的子类
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}

}

该类对外只有一个createAopProxy()方法,返回一个AopProxy。对于Repository,在创建ProxyFactory之后,调用setInterfaces(),添加实现的接口,所以上面的hasNoUserSuppliedProxyInterfaces()方法将返回false。所以createAopProxy()方法返回的是JdkDynamicAopProxy对象。

2.2 JdkDynamicAopProxy

JdkDynamicAopProxy的核心代码如下:

/**
 * 实现AopProxy和InvocationHandler,自己本身是代理回调的处理器
 */
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	/**
	 * 真正创建JDK动态代理实例
	 */
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		// 寻找接口,到最终代理的接口就是这里返回的所有接口(除了自己的接口,还有Spring默认的一些接口)
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		//查找被代理的接口中是否定义了equals、hashCode方法
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// 第三个参数传的this,处理器就是自己,表示当前类是InvocationHandler类型的,当调用代理对象的任何方法的时候
		// 都会被被当前类的 invoke 方法处理。new一个代理对象
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

	/**
	 * 当在程序中调用代理对象的任何方法,最终都会被invoke方法处理
	 */
	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//旧的代理对象
		Object oldProxy = null;
		//用来标记是否需要将代理对象暴露在ThreadLocal中
		boolean setProxyContext = false;
		//获取目标源
		TargetSource targetSource = this.advised.targetSource;
		//目标对象
		Object target = null;

		try {
			// 处理equals方法
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
			// 处理hashCode方法
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}
			/**
             * 处理DecoratingProxy
             */
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			// 方法来源于 Advised 接口,代理对象默认情况下会实现 Advised 接口,可以通过代理对象来动态向代理对象中添加通知等
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			//是否需要在threadLocal中暴露代理对象
			if (this.advised.exposeProxy) {
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// 获取当前方法的拦截器链
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// 拦截器链为空的情况下,表示这个方法上面没有找到任何增强的通知,那么会直接通过反射调用目标对象
			if (chain.isEmpty()) {
				// 获取方法请求的参数(有时候方法中有可变参数,所谓可变参数就是带有省略号(...)这种格式的参数,
				// 传入的参数类型和这种类型不一样的时候,会通过下面的adaptArgumentsIfNecessary方法进行转换)
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				//通过反射直接调用目标方法
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// 创建一个方法调用器(包含了代理对象、目标对象、调用的方法、参数、目标类型、方法拦截器链)
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// 通过拦截器链一个个调用最终到目标方法的调用
				retVal = invocation.proceed();
			}

			// 根据方法返回值的类型,做一些处理,比如方法返回的类型为自己,则最后需要将返回值置为代理对象
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				retVal = proxy;
			}
			// 方法的返回值类型returnType为原始类型(即int、byte、double等这种类型的) && retVal为null,
			// 此时如果将null转换为原始类型会报错,所以此处直接抛出异常
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			// 目标对象不为null && 目标源不是静态的
			//所谓静态的,你可以理解为是否是单例的
			// isStatic为true,表示目标对象是单例的,同一个代理对象中所有方法共享一个目标对象
			// isStatic为false的时候,通常每次调用代理的方法,target对象是不一样的,所以方法调用万之后需要进行释放,可能有些资源清理,连接的关闭等操作
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// 需要将旧的代理再放回到上线文中
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

    // 省略其他

}

2.2.1 使用ProxyFactory.getProxy()方法时,会先createAopProxy(),调用2.1中的DefaultAopProxyFactory.createAopProxy()方法,返回一个JdkDynamicAopProxy对象,然后调用JdkDynamicAopProxy.getProxy()方法。在上面的getProxy()方法中,执行Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)创建一个代理对象,并返回。其中第三个参数为this,即JdkDynamicAopProxy本身,因为JdkDynamicAopProxy实现了InvocationHandler接口;

2.2.2 当代理对象的方法被调用的时候,都会执行InvocationHandler.invoke()方法。结合2.2.1的分析,会执行JdkDynamicAopProxy.invoke()方法。该方法的前半部分用于处理特殊方法,如equals、hashCode等。

然后获取当前方法的拦截器链,如果没有拦截器,则反射执行原方法;如果有,则new一个ReflectiveMethodInvocation对象,并调用ReflectiveMethodInvocation.proceed()方法。

对于Repository接口类,在创建的时候添加了好几个拦截器,所以会new一个ReflectiveMethodInvocation对象,并调用ReflectiveMethodInvocation.proceed()方法。

2.3 ReflectiveMethodInvocation

ReflectiveMethodInvocation的核心代码如下:

/**
 * Joinpoint的唯一实现子类,维护代理对象、目标对象、执行方法、方法可用的所有拦截器。在proceed()方法中,遍历所有可用的拦截器,
 * 并挨个匹配执行,如果不能匹配,则递归执行proceed()方法,执行下一个拦截器
 */
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

	//代理对象
	protected final Object proxy;

	//目标对象
	@Nullable
	protected final Object target;

	//调用的方法
	protected final Method method;

	//调用方法的参数
	protected Object[] arguments;

	//目标类的class对象
	@Nullable
	private final Class<?> targetClass;

	/**
	 * Lazily initialized map of user-specific attributes for this invocation.
	 */
	@Nullable
	private Map<String, Object> userAttributes;

	//需要动态核对的MethodInterceptor和InterceptorAndDynamicMethodMatcher列表
	protected final List<?> interceptorsAndDynamicMethodMatchers;

	// 当前使用的拦截器在集合中的下标
	private int currentInterceptorIndex = -1;

	/**
	 * 保存基本信息
	 */
	protected ReflectiveMethodInvocation(
			Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
			@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

		this.proxy = proxy;
		this.target = target;
		this.targetClass = targetClass;
		//获取桥接方法
		this.method = BridgeMethodResolver.findBridgedMethod(method);
		this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
	}

	/**
	 * 执行下一个拦截器
	 * @return
	 * @throws Throwable
	 */
	@Override
	@Nullable
	public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			// 开始时,currentInterceptorIndex等于-1。当切面通知无法处理该代理方法时,会调用proceed()方法,
			// 直到currentInterceptorIndex的值等于this.interceptorsAndDynamicMethodMatchers.size() - 1,
			// 此时会执行invokeJoinpoint()方法。进而执行子类CglibAopProxy.CglibMethodInvocation.invokeJoinpoint()方法
			return invokeJoinpoint();
		}

		// 挨个的执行切面通知的方法
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		// 判断拦截器是否是InterceptorAndDynamicMethodMatcher(动态拦截器),
		// 动态拦截器要根据方法的参数的值来判断拦截器是否需要执行
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			// 检查方法的动态匹配,如果能够匹配,则执行对应的方法拦截器,如果切面有参数值,则对参数值进行赋值
			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			} else {
				// 如果方法不能动态匹配,则递归执行proceed()
				return proceed();
			}
		}
		else {
			// 如果拦截器是一个普通的方法拦截器,则执行调用拦截器的invoke()方法
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

	/**
	 * 通过反射,调用执行目标对象的方法
	 */
	@Nullable
	protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

    // 省略其他

}

在2.2.2中分析,Repository的方法调用时,会执行ReflectiveMethodInvocation.proceed()的方法,在该方法中,循环遍历所有的拦截器。

在拦截器的invoke(MethodInvocation invocation)方法中,传入的参数为this,即ReflectiveMethodInvocation本身。在拦截器中,如果需要执行下一个拦截器,需要再次调用invocation.proceed()方法。否则currentInterceptorIndex不会等于拦截器的个数减1,invokeJoinpoint()方法也将不会执行。

对于SimpleJpaRepository中实现的方法,会在执行完所有的拦截器之后,再次执行ReflectiveMethodInvocation.proceed()方法时,执行invokeJoinpoint()。在该方法中,执行target的方法,此处的target为SimpleJpaRepository对象。

SimpleJpaRepository

package org.springframework.data.jpa.repository.support;

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

	private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null";

	private final JpaEntityInformation<T, ?> entityInformation;
	private final EntityManager entityManager;
	private final PersistenceProvider provider;

	private @Nullable CrudMethodMetadata metadata;
	private @Nullable ProjectionFactory projectionFactory;
	private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;
	/**
	 * 在RepositoryFactorySupport类的getTargetRepositoryViaReflection() -> instantiateClass()方法调用实例化
	 * @param entityInformation Repository<T, ID>中的T的信息
	 * @param entityManager 系统的EntityManager对象
	 */
	public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {

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

		this.entityInformation = entityInformation;
		this.entityManager = entityManager;
		this.provider = PersistenceProvider.fromEntityManager(entityManager);
	}

	public SimpleJpaRepository(Class<T> domainClass, EntityManager entityManager) {
		this(JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager), entityManager);
	}

	@Override
	public void setRepositoryMethodMetadata(CrudMethodMetadata metadata) {
		this.metadata = metadata;
	}

	@Override
	public void setEscapeCharacter(EscapeCharacter escapeCharacter) {
		this.escapeCharacter = escapeCharacter;
	}

	@Override
	public void setProjectionFactory(ProjectionFactory projectionFactory) {
		this.projectionFactory = projectionFactory;
	}

	@Nullable
	protected CrudMethodMetadata getRepositoryMethodMetadata() {
		return metadata;
	}

	protected Class<T> getDomainClass() {
		return entityInformation.getJavaType();
	}

	/**
	 * 返回删除语句的sql。delete from %s x,其中%s为当前实体类的名称
	 * @return
	 */
	private String getDeleteAllQueryString() {
		return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName());
	}

	/**
	 * 返回count语句的sql。select count(*) from %s x,其中%s为当前实体类的名称
	 * @return
	 */
	private String getCountQueryString() {
		String countQuery = String.format(COUNT_QUERY_STRING, provider.getCountQueryPlaceholder(), "%s");
		return getQueryString(countQuery, entityInformation.getEntityName());
	}

	@Transactional
	@Override
	public void deleteById(ID id) {

		Assert.notNull(id, ID_MUST_NOT_BE_NULL);
		// 删除之前,会先调用findById()查询记录,如果存在,再执行delete方法
		findById(id).ifPresent(this::delete);
	}

	@Override
	@Transactional
	@SuppressWarnings("unchecked")
	public void delete(T entity) {

		Assert.notNull(entity, "Entity must not be null");
		// 如果是新实体,则不能删除。有很多实现,比如判断对应的entity定义的Id属性是否有值等
		if (entityInformation.isNew(entity)) {
			return;
		}

		// 获取用户自定义的实体类
		Class<?> type = ProxyUtils.getUserClass(entity);
		// 通过id从EntityManager查询记录是否存在
		T existing = (T) entityManager.find(type, entityInformation.getId(entity));

		// if the entity to be deleted doesn't exist, delete is a NOOP
		if (existing == null) {
			return;
		}
		// 获取实体并删除
		entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity));
	}

	@Override
	@Transactional
	public void deleteAllById(Iterable<? extends ID> ids) {

		Assert.notNull(ids, "Ids must not be null");
		// 循环执行deleteById
		for (ID id : ids) {
			deleteById(id);
		}
	}

	@Override
	@Transactional
	public void deleteAllByIdInBatch(Iterable<ID> ids) {

		Assert.notNull(ids, "Ids must not be null");

		if (!ids.iterator().hasNext()) {
			return;
		}

		// 如果是复合id
		if (entityInformation.hasCompositeId()) {

			List<T> entities = new ArrayList<>();
			// generate entity (proxies) without accessing the database.
			// 生成实体(代理)而不访问数据库
			ids.forEach(id -> entities.add(getReferenceById(id)));
			// 复合id删除时,拼接删除语句,并执行
			deleteAllInBatch(entities);
		} else {
			// 执行delete from %s x where %s in :ids
			String queryString = String.format(DELETE_ALL_QUERY_BY_ID_STRING, entityInformation.getEntityName(),
					entityInformation.getIdAttribute().getName());

			Query query = entityManager.createQuery(queryString);
			// 一些JPA提供程序要求ids是Collection类型,因此如果尚未转换,则必须进行转换
			// 查询语句的ids赋值
			if (Collection.class.isInstance(ids)) {
				query.setParameter("ids", ids);
			} else {
				Collection<ID> idsCollection = StreamSupport.stream(ids.spliterator(), false)
						.collect(Collectors.toCollection(ArrayList::new));
				query.setParameter("ids", idsCollection);
			}

			//  应用Query的hint。将添加的元数据中的hint信息添加到query中
			applyQueryHints(query);
			// 执行删除操作
			query.executeUpdate();
		}
	}

	@Override
	@Transactional
	public void deleteAll(Iterable<? extends T> entities) {

		Assert.notNull(entities, "Entities must not be null");

		for (T entity : entities) {
			delete(entity);
		}
	}

	/**
	 * 复合id删除时,拼接删除语句,并执行
	 */
	@Override
	@Transactional
	public void deleteAllInBatch(Iterable<T> entities) {

		Assert.notNull(entities, "Entities must not be null");

		if (!entities.iterator().hasNext()) {
			return;
		}
		// 拼接删除语句,并执行
		applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities,
				entityManager)
				.executeUpdate();
	}

	@Override
	@Transactional
	public void deleteAll() {

		for (T element : findAll()) {
			delete(element);
		}
	}

	@Override
	@Transactional
	public void deleteAllInBatch() {
		// 获取delete from %s x的查询对象
		Query query = entityManager.createQuery(getDeleteAllQueryString());
		// 设置hint信息
		applyQueryHints(query);
		// 执行query
		query.executeUpdate();
	}

	/**
	 * 通过主键查询对象
	 */
	@Override
	public Optional<T> findById(ID id) {

		Assert.notNull(id, ID_MUST_NOT_BE_NULL);
		// 获取Repository<T, ID>中定义的T的类型
		Class<T> domainType = getDomainClass();

		if (metadata == null) {
			return Optional.ofNullable(entityManager.find(domainType, id));
		}
		// 获取元数据中设置的锁类型。在Repository中通过@Lock(LockModeType.READ)添加的信息
		LockModeType type = metadata.getLockModeType();
		// 获取配置的hint信息
		Map<String, Object> hints = getHints();
		// 执行EntityManager.find()方法
		return Optional.ofNullable(type == null ? entityManager.find(domainType, id, hints) : entityManager.find(domainType, id, type, hints));
	}

	@Deprecated
	@Override
	public T getOne(ID id) {
		return getReferenceById(id);
	}

	@Deprecated
	@Override
	public T getById(ID id) {
		return getReferenceById(id);
	}

	/**
	 * 通过entityManager.getReference()获取实体
	 */
	@Override
	public T getReferenceById(ID id) {

		Assert.notNull(id, ID_MUST_NOT_BE_NULL);
		return entityManager.getReference(getDomainClass(), id);
	}

	@Override
	public boolean existsById(ID id) {

		Assert.notNull(id, ID_MUST_NOT_BE_NULL);

		if (entityInformation.getIdAttribute() == null) {
			return findById(id).isPresent();
		}

		// 返回用于简单计数查询的占位符。默认实现返回x。子类PersistenceProvider返回*。select count(*) from tb_table t
		String placeholder = provider.getCountQueryPlaceholder();
		String entityName = entityInformation.getEntityName();
		// 获取主键属性名称
		Iterable<String> idAttributeNames = entityInformation.getIdAttributeNames();
		// 回用于执行给定id属性的查询字符串。返回select count(*) from %s x where x.id1Name = :id1Name and x.id2Name = :id2Name
		String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames);

		TypedQuery<Long> query = entityManager.createQuery(existsQuery, Long.class);
		// 设置hint
		applyQueryHints(query);
		// 如果不是复合id
		if (!entityInformation.hasCompositeId()) {
			// 设置id的值
			query.setParameter(idAttributeNames.iterator().next(), id);
			// 如果有一条记录,说明存在对应id的记录,返回true
			return query.getSingleResult() == 1L;
		}
		// 如果是复合id
		for (String idAttributeName : idAttributeNames) {

			// 获取对应id的值
			Object idAttributeValue = entityInformation.getCompositeIdAttributeValue(id, idAttributeName);

			boolean complexIdParameterValueDiscovered = idAttributeValue != null
					&& !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass());
			// 发现复杂Id参数值
			if (complexIdParameterValueDiscovered) {
				// fall-back to findById(id) which does the proper mapping for the parameter.
				// 返回到findById(id),它为参数执行正确的映射
				// 执行findById获取记录,记录存在,返回true
				return findById(id).isPresent();
			}
			// 设置查询语句的参数
			query.setParameter(idAttributeName, idAttributeValue);
		}
		// 如果有一条记录,说明存在对应id的记录,返回true,否则返回false
		return query.getSingleResult() == 1L;
	}

	/**
	 * 获取所有数据,通过Criteria接口进行查询
	 */
	@Override
	public List<T> findAll() {
		return getQuery(null, Sort.unsorted()).getResultList();
	}

	@Override
	public List<T> findAllById(Iterable<ID> ids) {

		Assert.notNull(ids, "Ids must not be null");

		if (!ids.iterator().hasNext()) {
			return Collections.emptyList();
		}
		// 如果是复合id,轮询使用findById()逐条获取记录
		if (entityInformation.hasCompositeId()) {

			List<T> results = new ArrayList<>();
			// 遍历执行findById(),获取对象,加入到List集合中
			for (ID id : ids) {
				findById(id).ifPresent(results::add);
			}

			return results;
		}
		// 如果不是复合id,则使用in查询语句获取多条记录
		Collection<ID> idCollection = Streamable.of(ids).toList();
		// 通过Criteria接口中的Path.in(),一次批量查询多条语句
		ByIdsSpecification<T> specification = new ByIdsSpecification<>(entityInformation);
		// 通过给定的Specification和排序规则,创建一个TypedQuery对象,并添加hint、锁等信息
		TypedQuery<T> query = getQuery(specification, Sort.unsorted());
		// query参数赋值,并执行
		return query.setParameter(specification.parameter, idCollection).getResultList();
	}

	@Override
	public List<T> findAll(Sort sort) {
		return getQuery(null, sort).getResultList();
	}

	@Override
	public Page<T> findAll(Pageable pageable) {

		if (pageable.isUnpaged()) {
			return new PageImpl<>(findAll());
		}

		return findAll((Specification<T>) null, pageable);
	}

	@Override
	public Optional<T> findOne(Specification<T> spec) {

		try {
			// 最多获取两条记录,只获取其中的一条
			return Optional.of(getQuery(spec, Sort.unsorted()).setMaxResults(2).getSingleResult());
		} catch (NoResultException e) {
			return Optional.empty();
		}
	}

	@Override
	public List<T> findAll(Specification<T> spec) {
		return getQuery(spec, Sort.unsorted()).getResultList();
	}

	@Override
	public Page<T> findAll(Specification<T> spec, Pageable pageable) {

		TypedQuery<T> query = getQuery(spec, pageable);
		return pageable.isUnpaged() ? new PageImpl<>(query.getResultList())
				: readPage(query, getDomainClass(), pageable, spec);
	}

	@Override
	public List<T> findAll(Specification<T> spec, Sort sort) {
		return getQuery(spec, sort).getResultList();
	}

	/**
	 * 判断是否有符合Specification的记录
	 * @param spec the {@link Specification} to use for the existence check, ust not be {@literal null}.
	 * @return
	 */
	@Override
	public boolean exists(Specification<T> spec) {
		// 创建返回Integer类型的query
		CriteriaQuery<Integer> cq = this.entityManager.getCriteriaBuilder() //
				.createQuery(Integer.class) // 返回Integer类型
				.select(this.entityManager.getCriteriaBuilder().literal(1));
		// 从给定的Specification应用到给定的CriteriaQuery。如果有Specification,
		// 执行Specification.toPredicate(),并作为查询的where
		applySpecificationToCriteria(spec, getDomainClass(), cq);
		// 应用Repository中的方法元数据。将元数据中的@Lock、@QueryHint等信息添加到query中
		TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.entityManager.createQuery(cq));
		// 执行查询,查询结果记录数量为1,则返回true,否则返回false
		return query.setMaxResults(1).getResultList().size() == 1;
	}

	/**
	 * 通过Specification作为条件,执行删除操作
	 */
	@Override
	public long delete(Specification<T> spec) {

		CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
		CriteriaDelete<T> delete = builder.createCriteriaDelete(getDomainClass());

		if (spec != null) {
			Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder);

			if (predicate != null) {
				delete.where(predicate);
			}
		}

		return this.entityManager.createQuery(delete).executeUpdate();
	}

	@Override
	public <S extends T, R> R findBy(Specification<T> spec, Function<FetchableFluentQuery<S>, R> queryFunction) {

		Assert.notNull(spec, "Specification must not be null");
		Assert.notNull(queryFunction, "Query function must not be null");

		return doFindBy(spec, getDomainClass(), queryFunction);
	}

	private <S extends T, R> R doFindBy(Specification<T> spec, Class<T> domainClass,
			Function<FetchableFluentQuery<S>, R> queryFunction) {

		Assert.notNull(spec, "Specification must not be null");
		Assert.notNull(queryFunction, "Query function must not be null");

		ScrollQueryFactory scrollFunction = (sort, scrollPosition) -> {

			Specification<T> specToUse = spec;

			if (scrollPosition instanceof KeysetScrollPosition keyset) {
				KeysetScrollSpecification<T> keysetSpec = new KeysetScrollSpecification<>(keyset, sort, entityInformation);
				sort = keysetSpec.sort();
				specToUse = specToUse.and(keysetSpec);
			}

			TypedQuery<T> query = getQuery(specToUse, domainClass, sort);

			if (scrollPosition instanceof OffsetScrollPosition offset) {
				if(!offset.isInitial()) {
					query.setFirstResult(Math.toIntExact(offset.getOffset()) + 1);
				}
			}

			return query;
		};

		Function<Sort, TypedQuery<T>> finder = sort -> getQuery(spec, domainClass, sort);

		SpecificationScrollDelegate<T> scrollDelegate = new SpecificationScrollDelegate<>(scrollFunction,
				entityInformation);
		FetchableFluentQueryBySpecification<?, T> fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder,
				scrollDelegate, this::count, this::exists, this.entityManager, getProjectionFactory());

		return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
	}

	@Override
	public <S extends T> Optional<S> findOne(Example<S> example) {

		try {
			return Optional
					.of(getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted())
							.setMaxResults(2).getSingleResult());
		} catch (NoResultException e) {
			return Optional.empty();
		}
	}

	@Override
	public <S extends T> long count(Example<S> example) {
		return executeCountQuery(
				getCountQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType()));
	}

	@Override
	public <S extends T> boolean exists(Example<S> example) {

		Specification<S> spec = new ExampleSpecification<>(example, this.escapeCharacter);
		CriteriaQuery<Integer> cq = this.entityManager.getCriteriaBuilder() //
				.createQuery(Integer.class) //
				.select(this.entityManager.getCriteriaBuilder().literal(1));

		applySpecificationToCriteria(spec, example.getProbeType(), cq);

		TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.entityManager.createQuery(cq));
		return query.setMaxResults(1).getResultList().size() == 1;
	}

	@Override
	public <S extends T> List<S> findAll(Example<S> example) {
		return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted())
				.getResultList();
	}

	@Override
	public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
		return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), sort).getResultList();
	}

	@Override
	public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {

		ExampleSpecification<S> spec = new ExampleSpecification<>(example, escapeCharacter);
		Class<S> probeType = example.getProbeType();
		TypedQuery<S> query = getQuery(new ExampleSpecification<>(example, escapeCharacter), probeType, pageable);

		return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()) : readPage(query, probeType, pageable, spec);
	}

	@Override
	public <S extends T, R> R findBy(Example<S> example, Function<FetchableFluentQuery<S>, R> queryFunction) {

		Assert.notNull(example, "Sample must not be null");
		Assert.notNull(queryFunction, "Query function must not be null");

		ExampleSpecification<S> spec = new ExampleSpecification<>(example, escapeCharacter);
		Class<S> probeType = example.getProbeType();

		return doFindBy((Specification<T>) spec, (Class<T>) probeType, queryFunction);
	}

	@Override
	public long count() {

		TypedQuery<Long> query = entityManager.createQuery(getCountQueryString(), Long.class);

		applyQueryHintsForCount(query);

		return query.getSingleResult();
	}

	@Override
	public long count(@Nullable Specification<T> spec) {
		return executeCountQuery(getCountQuery(spec, getDomainClass()));
	}

	@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null");

		if (entityInformation.isNew(entity)) {
			entityManager.persist(entity);
			return entity;
		} else {
			return entityManager.merge(entity);
		}
	}

	@Transactional
	@Override
	public <S extends T> S saveAndFlush(S entity) {

		S result = save(entity);
		flush();

		return result;
	}

	@Transactional
	@Override
	public <S extends T> List<S> saveAll(Iterable<S> entities) {

		Assert.notNull(entities, "Entities must not be null");

		List<S> result = new ArrayList<>();

		for (S entity : entities) {
			result.add(save(entity));
		}

		return result;
	}

	@Transactional
	@Override
	public <S extends T> List<S> saveAllAndFlush(Iterable<S> entities) {

		List<S> result = saveAll(entities);
		flush();

		return result;
	}

	@Transactional
	@Override
	public void flush() {
		entityManager.flush();
	}

	@Deprecated
	protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, @Nullable Specification<T> spec) {
		return readPage(query, getDomainClass(), pageable, spec);
	}

	protected <S extends T> Page<S> readPage(TypedQuery<S> query, final Class<S> domainClass, Pageable pageable,
			@Nullable Specification<S> spec) {

		if (pageable.isPaged()) {
			query.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));
			query.setMaxResults(pageable.getPageSize());
		}

		return PageableExecutionUtils.getPage(query.getResultList(), pageable,
				() -> executeCountQuery(getCountQuery(spec, domainClass)));
	}

	protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Pageable pageable) {

		Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();
		return getQuery(spec, getDomainClass(), sort);
	}

	protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass,
			Pageable pageable) {

		Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();
		return getQuery(spec, domainClass, sort);
	}

	/**
	 * 通过给定的Specification和排序规则,创建一个TypedQuery对象
	 */
	protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Sort sort) {
		return getQuery(spec, getDomainClass(), sort);
	}

	/**
	 * 通过给定的Specification和排序规则,创建一个TypedQuery对象
	 */
	protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort) {
		// 获取一个CriteriaBuilder
		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		// 创建CriteriaQuery
		CriteriaQuery<S> query = builder.createQuery(domainClass);
		// 获取一个Root,定义Criteria查询的根对象
		Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
		// 查询root
		query.select(root);
		// 添加排序
		if (sort.isSorted()) {
			query.orderBy(toOrders(sort, root, builder));
		}
		// 创建Query,并添加hint、锁等信息
		return applyRepositoryMethodMetadata(entityManager.createQuery(query));
	}

	@Deprecated
	protected TypedQuery<Long> getCountQuery(@Nullable Specification<T> spec) {
		return getCountQuery(spec, getDomainClass());
	}

	protected <S extends T> TypedQuery<Long> getCountQuery(@Nullable Specification<S> spec, Class<S> domainClass) {

		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Long> query = builder.createQuery(Long.class);

		Root<S> root = applySpecificationToCriteria(spec, domainClass, query);

		if (query.isDistinct()) {
			query.select(builder.countDistinct(root));
		} else {
			query.select(builder.count(root));
		}

		// Remove all Orders the Specifications might have applied
		query.orderBy(Collections.emptyList());

		return applyRepositoryMethodMetadataForCount(entityManager.createQuery(query));
	}

	protected QueryHints getQueryHints() {
		return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata);
	}

	protected QueryHints getQueryHintsForCount() {
		return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata).forCounts();
	}

	/**
	 * 从给定的Specification应用到给定的CriteriaQuery。如果有Specification,
	 * 执行Specification.toPredicate(),并作为查询的where
	 */
	private <S, U extends T> Root<U> applySpecificationToCriteria(@Nullable Specification<U> spec, Class<U> domainClass,
			CriteriaQuery<S> query) {

		Assert.notNull(domainClass, "Domain class must not be null");
		Assert.notNull(query, "CriteriaQuery must not be null");
		// 获取一个Root。定义Criteria查询的根对象,Criteria查询的根定义为实体类型,它与SQL查询中的FROM子句类似
		Root<U> root = query.from(domainClass);
		// 如果没有Specification,直接返回root
		if (spec == null) {
			return root;
		}
		// 创建CriteriaBuilder,用于构造条件查询、复合选择、表达式、谓词和排序
		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		// 执行Specification的toPredicate()方法,如果有返回值,则作为where语句,加入到query中
		Predicate predicate = spec.toPredicate(root, query, builder);

		if (predicate != null) {
			query.where(predicate);
		}
		// 返回root
		return root;
	}

	/**
	 * 应用Repository中的方法元数据。将元数据中的@Lock、@QueryHint等信息添加到query中
	 */
	private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {

		if (metadata == null) {
			return query;
		}
		// 获取元数据中的锁信息
		LockModeType type = metadata.getLockModeType();
		TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);
		// 设置hint
		applyQueryHints(toReturn);
		// 返回query
		return toReturn;
	}

	/**
	 * 应用Query的hint。将添加的元数据中的hint信息添加到query中。
	 * Spring Data JPA 中的 Query Hint 是一种强大的工具,可帮助优化数据库查询并提高应用性能。
	 * 与直接控制执行不同,Query Hint 会影响优化器(Optimizer)的决策过程。
	 * 详见:https://springdoc.cn/spring-data-jpa-query-hints/
	 */
	private void applyQueryHints(Query query) {

		if (metadata == null) {
			return;
		}
		// 获取一个DefaultQueryHints对象,执行query的setHint
		getQueryHints().withFetchGraphs(entityManager).forEach(query::setHint);
		// 从metadata元数据中获取设置的hint,并调用query::setHint,设置对应的hint名称及hint值
		applyComment(metadata, query::setHint);
	}

	private <S> TypedQuery<S> applyRepositoryMethodMetadataForCount(TypedQuery<S> query) {

		if (metadata == null) {
			return query;
		}

		applyQueryHintsForCount(query);

		return query;
	}

	private void applyQueryHintsForCount(Query query) {

		if (metadata == null) {
			return;
		}

		getQueryHintsForCount().forEach(query::setHint);
		applyComment(metadata, query::setHint);
	}

	/**
	 * 获取hint信息
	 */
	private Map<String, Object> getHints() {

		Map<String, Object> hints = new HashMap<>();
		// 获取系统设置的hint
		getQueryHints().withFetchGraphs(entityManager).forEach(hints::put);
		// 获取类元数据中定义的hint
		if (metadata != null) {
			applyComment(metadata, hints::put);
		}

		return hints;
	}

	/**
	 * 从metadata元数据中获取设置的hint,并调用BiConsumer,设置对应的hint名称及hint值
	 */
	private void applyComment(CrudMethodMetadata metadata, BiConsumer<String, Object> consumer) {

		if (metadata.getComment() != null && provider.getCommentHintKey() != null) {
			consumer.accept(provider.getCommentHintKey(), provider.getCommentHintValue(this.metadata.getComment()));
		}
	}

	private ProjectionFactory getProjectionFactory() {

		if (projectionFactory == null) {
			projectionFactory = new SpelAwareProxyProjectionFactory();
		}

		return projectionFactory;
	}

	private static long executeCountQuery(TypedQuery<Long> query) {

		Assert.notNull(query, "TypedQuery must not be null");

		List<Long> totals = query.getResultList();
		long total = 0L;

		for (Long element : totals) {
			total += element == null ? 0 : element;
		}

		return total;
	}

	@SuppressWarnings("rawtypes")
	/**
	 * 通过Criteria接口中的Path.in(),一次批量查询多条语句
	 */
	private static final class ByIdsSpecification<T> implements Specification<T> {

		private static final long serialVersionUID = 1L;

		private final JpaEntityInformation<T, ?> entityInformation;

		@Nullable ParameterExpression<Collection<?>> parameter;

		ByIdsSpecification(JpaEntityInformation<T, ?> entityInformation) {
			this.entityInformation = entityInformation;
		}

		@Override
		public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
			// 获取主键的Path
			Path<?> path = root.get(entityInformation.getIdAttribute());
			parameter = (ParameterExpression<Collection<?>>) (ParameterExpression) cb.parameter(Collection.class);
			// 使用in语句批量查询
			return path.in(parameter);
		}
	}

	private static class ExampleSpecification<T> implements Specification<T> {

		private static final long serialVersionUID = 1L;

		private final Example<T> example;
		private final EscapeCharacter escapeCharacter;

		ExampleSpecification(Example<T> example, EscapeCharacter escapeCharacter) {

			Assert.notNull(example, "Example must not be null");
			Assert.notNull(escapeCharacter, "EscapeCharacter must not be null");

			this.example = example;
			this.escapeCharacter = escapeCharacter;
		}

		@Override
		public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
			return QueryByExamplePredicateBuilder.getPredicate(root, cb, example, escapeCharacter);
		}
	}
}

SimpleJpaRepository实现了JpaRepositoryImplementation接口,它是CrudRepository的默认实现。

在自定义的Repository中,如果不实现JpaRepositoryImplementation接口,只是不能直接访问JpaRepositoryImplementation中提供的方法,而实际上是可以使用JpaRepositoryImplementation中的方法。如通过反射机制调用等。

SimpleJpaRepository中提供的CRUD的方法,要么是通过拼接SQL语句,要么是通过Criteria的查询,执行EntityManager对应方法实现。大部分核心代码都做了注释,对Criteria查询不清楚的,可以看

Spring Data JPA Criteria查询、部分字段查询_jpa查询某个字段-CSDN博客

小结

限于篇幅,本篇就分享到这里,这里做一个小结:

1)通过上一篇源码,确认了Repository是代理对象,其中的target为SimpleJpaRepository对象;

2)介绍了访问JpaRepositoryImplementation中的方法时,如何通过动态代理执行到SimpleJpaRepository;

3)分析SimpleJpaRepository源码,核心是通过拼接SQL语句、Criteria的查询,执行EntityManager对应方法实现对数据库表的操作;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,由于篇幅限制,我无法在这里提供整个项目的完整源代码。但我可以为您提供一些示例代码来演示如何实现根据不同的登录方式使用不同的数据库操作方式。 首先,您可以创建一个包含用户信息的数据库表,并在应用程序中创建对应的Java实体类。例如,以下代码显示了一个名为User的实体类: ```java @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String email; private String phone; private String password; // getters and setters } ``` 接下来,您可以为每种数据库操作方式创建相应的DAO接口。例如,以下代码显示了一个名为UserJdbcDao的JDBC DAO接口: ```java public interface UserJdbcDao { User findByUsernameAndPassword(String username, String password); } ``` 然后,您可以实现这些DAO接口。例如,以下代码显示了一个名为UserJdbcDaoImpl的JDBC DAO实现类: ```java @Repository public class UserJdbcDaoImpl implements UserJdbcDao { private final JdbcTemplate jdbcTemplate; @Autowired public UserJdbcDaoImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public User findByUsernameAndPassword(String username, String password) { String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{username, password}, new UserRowMapper()); } private static class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getLong("id")); user.setUsername(rs.getString("username")); user.setEmail(rs.getString("email")); user.setPhone(rs.getString("phone")); user.setPassword(rs.getString("password")); return user; } } } ``` 类似地,您可以创建相应的MyBatis DAO接口和Spring Data JPA DAO接口,并实现它们。 最后,您可以创建一个处理登录请求的控制器,并在其中根据用户输入的登录信息选择使用哪种DAO。例如,以下代码显示了一个名为LoginController的登录控制器: ```java @RestController @RequestMapping("/login") public class LoginController { private final UserJdbcDao userJdbcDao; private final UserMyBatisDao userMyBatisDao; private final UserRepository userRepository; @Autowired public LoginController(UserJdbcDao userJdbcDao, UserMyBatisDao userMyBatisDao, UserRepository userRepository) { this.userJdbcDao = userJdbcDao; this.userMyBatisDao = userMyBatisDao; this.userRepository = userRepository; } @PostMapping public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) { User user; if (isEmail(loginRequest.getUsername())) { user = userMyBatisDao.findByEmailAndPassword(loginRequest.getUsername(), loginRequest.getPassword()); } else if (isPhone(loginRequest.getUsername())) { user = userRepository.findByPhoneAndPassword(loginRequest.getUsername(), loginRequest.getPassword()); } else { user = userJdbcDao.findByUsernameAndPassword(loginRequest.getUsername(), loginRequest.getPassword()); } if (user == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } else { return ResponseEntity.ok(user); } } private boolean isEmail(String str) { // check if str is a valid email } private boolean isPhone(String str) { // check if str is a valid phone number } } ``` 在上面的代码中,LoginController使用了@Autowired注解注入了UserJdbcDao、UserMyBatisDao和UserRepository。然后,它使用isEmail()和isPhone()方法来判断用户输入的登录信息是用户名、邮箱还是手机号,并根据不同的情况选择使用不同的DAO。 当然,这只是一个简单的示例,您需要根据您的具体需求进行修改和完善。但是,希望这可以为您提供一些帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值