Spring Boot 3.3.3升级后findByEmail方法返回Optional.empty问题排查
问题原因及解决方案
问题背景
Spring Boot 2.5.x升级至3.3.3后,UserRepository的findByEmail(String email)方法始终返回Optional.empty,但findAll()能正常返回包含目标邮箱的用户记录;实体类User的email字段使用了@Convert(converter = QueryableStringEncryptor.class)做字段加密。
核心原因
Spring Data JPA 3.x版本对属性转换器(Attribute Converter)的查询参数处理逻辑发生了变更:
- 在2.x版本中,Spring Data会自动将Repository查询方法的参数,通过实体字段上的
@Convert转换器进行转换(即把明文邮箱加密后再去数据库匹配)。 - 3.x版本中,当Repository同时继承
QuerydslPredicateExecutor时,Spring Data的参数转换逻辑优先级或触发条件改变,导致findByEmail方法的明文参数未经过加密转换器处理,直接以明文去匹配数据库中存储的加密值,自然无法找到对应记录。
解决方法
方法1:显式使用@Query手动处理参数加密
在findByEmail方法上添加@Query注解,通过SpEL表达式调用Spring容器中的加密转换器,确保查询时用加密后的值匹配数据库:
@RepositoryRestResource(exported = false) public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> { Stream<UserDashboardSearchProjection> findByEmailIsNotNull(); Optional<User> findById(Long userId); @Query("SELECT u FROM User u WHERE u.email = #{@queryableStringEncryptor.encrypt(:email)}") Optional<User> findByEmail(@Param("email") String email); }
前提:
QueryableStringEncryptor需被注册为Spring Bean。
方法2:移除QuerydslPredicateExecutor(若业务不需要)
如果项目未用到QuerydslPredicateExecutor的功能,直接从Repository继承列表中移除该接口,Spring Data JPA会恢复默认的参数转换逻辑,findByEmail方法会自动将参数通过转换器加密后查询。
方法3:自定义Querydsl Predicate处理参数加密
若必须保留QuerydslPredicateExecutor,需在构建查询Predicate时手动对邮箱参数加密:
// 业务代码中调用Querydsl查询的示例 @Autowired private UserRepository userRepository; @Autowired private QueryableStringEncryptor encryptor; public Optional<User> findUserByEmail(String email) { String encryptedEmail = encryptor.encrypt(email); QUser qUser = QUser.user; return userRepository.findOne(qUser.email.eq(encryptedEmail)); }
方法4:检查转换器的@Converter注解配置
确保QueryableStringEncryptor类上的@Converter注解设置了autoApply = true,让Spring自动将转换器应用到所有匹配类型的字段查询中:
@Converter(autoApply = true) public class QueryableStringEncryptor implements AttributeConverter<String, String> { @Override public String convertToDatabaseColumn(String attribute) { // 加密逻辑实现 } @Override public String convertToEntityAttribute(String dbData) { // 解密逻辑实现 } }
内容的提问来源于stack exchange,提问作者Ballazx
相关产品推荐
相关产品推荐

