如何通过方法名将多个And和Or组合起来

64
我正在尝试迁移应用程序。我正在从Hibernate迁移到Spring Data JPA。
尽管Spring Data JPA提供了简单的查询构建方法,但我在创建同时使用AndOr运算符的查询方法上遇到了困难。
方法名 - findByPlan_PlanTypeInAndSetupStepIsNullOrStepupStepIs(...) 当它转换为查询时,前两个表达式被组合在一起,并且执行为[(exp1 and exp2) or (exp3)],而所需的是](exp1) and (exp2 or exp3)]
请问有人能告诉我是否可以通过Spring Data JPA实现这个目标吗?
5个回答

60
我同意Oliver关于长而难读的方法名的观点,但是为了论证的目的,你仍然可以通过使用等效性来实现所需的结果。
A /\ (B \/ C) <=> (A /\ B) \/ (A /\ C)
A and (B or C) <=> (A and B) or (A and C)

所以在你的情况下,应该是这样的:
findByPlan_PlanTypeInAndSetupStepIsNullOrPlan_PlanTypeInAndStepupStepIs(...)

3
这基本上可行,但是如何避免在这种方法中出现重复变量?(如果可能的话....) - Trinimon
是的,这个方法可行... 看起来你无法避免重复的参数。我使用了一个门面方法来隐藏笨拙的方法签名。 - kiedysktos
是的,那个有效了。 很好地运用了原则。 - Usama Majid

23

目前不可能,未来也不会。即使可能,对于更复杂的查询,您也不希望在方法名称中人为地挤入所有查询复杂性。这不仅因为难以消化实际上正在查询的内容,而且从客户端代码的角度来看:您希望使用表达性强的方法名称,而在简单的 findByUsername(…) 的情况下,查询派生允许您创建这样的名称。

对于更复杂的内容,您只需将查询复杂性提升到调用代码中,并建议使用语义上表达查询内容的可读方法名称,并将查询复杂性保留在手动声明的查询中,使用@Query、命名查询或类似方式。


18

使用类似于

findByFirstElementAndCriteriaOrSecondElementAndCriteria

就像(第一个条件 and 条件)或者(第二个条件 and 条件)--> 条件 and (第一个条件或第二个条件)


3
它可以工作,但最大的缺点是您必须将相同的条件作为参数传递多次以在查询中使用。在这种情况下,您需要传递两个变量Criteria,它们包含相同的内容: findByFirstElementAndCriteriaOrSecondElementAndCriteria(Element element1, Criteria criteria1, Element element2, Criteria criteria2) //criteria1.equals(criteria2) -> true - thorinkor

14

选项1:您可以使用命名查询(参见使用JPA命名查询):

@Entity
@NamedQuery(name = "User.findByEmailAddress",
  query = "select u from User u where u.emailAddress = ?1")
public class User {

}

public interface UserRepository extends JpaRepository<User, Long> {

   User findByEmailAddress(String emailAddress);
}

选项2: 使用@Query编写自定义查询(参见使用@Query

public interface UserRepository extends JpaRepository<User, Long> {

   @Query("select u from User u where u.emailAddress = ?1")
   User findByEmailAddress(String emailAddress);
}

谢谢回复!使用NamedQuery或Query的唯一劣势是当同一repo类中存在多个连接查询时,会使该类看起来非常混乱:( - Preethi Ramanujam
3
但是,您的方法名称不会变得混乱且难以阅读吗?从方法名称派生查询对于简单查询很方便 - 在某个时候您应该切换到@Query或类似工具。 - fateddy

1
假设你在Spring Repository类中有这个方法:
Page<SystemEntity>  findByNameContainsAndFlagIsFalseOrFamilyContainsAndFlagIsFalseOrEmailContainsAndFlagIsFalse(String name, String family ,String email, Pageable pageable); 

一个好的做法是将大查询拆分为多个部分,然后以这种方式使用它们:
Page<SystemEntity> findByNameContainsAndFlagIsFalse(String name, Pageable pageable); 

Page<SystemEntity> findByFamilyContainsAndFlagIsFalse(String family, Pageable pageable); 

Page<SystemEntity> findByEmailContainsAndFlagIsFals(String email, Pageable pageable); 


// Combine results from above methods
default Page<SystemEntity> findByPrimaryLogicalCondition(String name, String family ,String email, Pageable pageable){
    Page<SystemEntity> pageName = findByNameContainsAndFlagIsFalse(name, pageable);
    Page<SystemEntity> pageFamily = findByFamilyContainsAndFlagIsFalse(family, pageable);
    Page<SystemEntity> pageEmail = findByEmailContainsAndFlagIsFals(email, pageable);
    
    // Combine the results from both queries
    List<SystemEntity> combinedResults = new ArrayList<>(pageName.getContent());
    combinedResults.addAll(pageFamily.getContent());
    combinedResults.addAll(pageEmail.getContent());
    
    return new PageImpl<>(combinedResults, pageable, combinedResults.size());
}

这种方法允许您在需要时重复使用较小的方法,并使您的代码更具模块化和可维护性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接