当前位置: 首页 > 知识库问答 >
问题:

基于实例类型Spring搜索不同字段的规范/谓词

习洲
2023-03-14

假设我有一个抽象类Product,我还有两个实体用不同的字段扩展它:ProductA和ProductB。我正在使用Product repository,使用Specification和SpecificationBuilder,并使用一些常用字段来搜索它们。我想完成的是检测它是什么类型的产品(A或B),并在此基础上使用不同的搜索查询。

例如,ProductA有库存,ProductB有功能,因此如果我插入搜索条件quantity并尝试对产品回购进行规范,我希望api搜索ProductA。这种形式的搜索有可能吗?这是我当前的代码:

public interface ProductRepo extends JpaRepository<Product, Integer>,JpaSpecificationExecutor<Product>{

}

public class ProductSpecificationBuilder {
 public final ProductSpecificationBuilder with(final String orPredicate, final String key
                , final String operation, Object value, final String prefix, final String suffix) {
            System.out.println("called "+value);
            //Fixing boolean value

            if (!key.equals("visible") || key.equals("modified")) {
                System.out.println("not equal visible");
            SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0));
            if (op != null) {
                if (op == SearchOperation.EQUALITY) { // the operation may be complex operation
                    final boolean startWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX);
                    final boolean endWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX);
                    System.out.println("prefix "+startWithAsterisk+" "+endWithAsterisk);
                    if (startWithAsterisk && endWithAsterisk) {
                        op = SearchOperation.CONTAINS;
                    } else if (startWithAsterisk) {
                        op = SearchOperation.ENDS_WITH;
                    } else if (endWithAsterisk) {
                        op = SearchOperation.STARTS_WITH;
                    }
                }else if (op == SearchOperation.LIKE) {
                    System.out.println("we got like in builder");
                }
                params.add(new SearchCriteria(orPredicate, key, op, value));
            }
            }
            return this;
        }
}
public class ProductSpecification implements Specification<Product>{
@Override
    public Predicate toPredicate(final Root<Product> root, final CriteriaQuery<?> query, final CriteriaBuilder builder) {
        //TODO JOIN class for nested search for promotion
        switch (criteria.getOperation()) {
        case EQUALITY:
            return builder.equal(root.get(criteria.getKey()), criteria.getValue());
        case NEGATION:
            return builder.notEqual(root.get(criteria.getKey()), criteria.getValue());
        case GREATER_THAN:
            return builder.greaterThan(root.get(criteria.getKey()), criteria.getValue().toString());
        case LESS_THAN:

            return builder.lessThan(root.get(criteria.getKey()), criteria.getValue().toString());
        case LIKE:
            return builder.like(root.get(criteria.getKey()), criteria.getValue().toString());
        case STARTS_WITH:
            return builder.like(root.get(criteria.getKey()), criteria.getValue() + "%");
        case ENDS_WITH:
            return builder.like(root.get(criteria.getKey()), "%" + criteria.getValue());
        case CONTAINS:
            return builder.like(root.get(criteria.getKey()), "%" + criteria.getValue() + "%");
        default:
            return null;
        }
    }
}
public enum SearchOperation {
    EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS;

    public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~","@"};

    public static final String OR_PREDICATE_FLAG = "'";

    public static final String ZERO_OR_MORE_REGEX = "*";

    public static final String OR_OPERATOR = "OR";

    public static final String AND_OPERATOR = "AND";

    public static final String LEFT_PARANTHESIS = "(";

    public static final String RIGHT_PARANTHESIS = ")";

    public static SearchOperation getSimpleOperation(final char input) {
        switch (input) {
        case ':':
            return EQUALITY;
        case '!':
            return NEGATION;
        case '>':
            return GREATER_THAN;
        case '<':
            return LESS_THAN;
        case '~':
            return LIKE;
        case '@':{

            return CONTAINS;
        }
        default:
            return null;
        }
    }
}

和我的产品类

@Entity
@Table(name = "products")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Product {


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    @Column(name = "id")
    private int id;
    -----Common fields----
}
public class ProductA extends Product{ int stock; }
public class ProductB extends Product{ int featured; }

如果您有任何问题,请随时提出。任何形式的帮助都将不胜感激!谢谢您。

共有1个答案

潘文乐
2023-03-14

据我所知,没有“好方法”可以做到这一点(如果您想保留类型),但您可以采用以下解决方案之一:

0.一般来说,我很反对在< code>Java中使用< code>switch和< code>case,但它有自己的位置...最简单的解决方案是在不同的情况下构建查询...它们是独立的案例(无意双关)。

从你的问题中,我假设你有一个比所呈现的结构更复杂的结构,所以接下来我有两个可能的解决方案给你。

1. 编程解决方案 您可以创建基于反射的解决方案。你可以有一个完整的解决方案来实现一些有趣的逻辑。主要是,您必须从此功能中“可搜索”的对象中获得一个列表。在循环中,可以使用反射来确定实体中的字段是否“可接受”。(例如,“股票”仅在PRODUCT_A中可接受,因此标准的创建将基于该实体。我绝对不建议使用这种方法,因为这可能会带来很多问题。(例如,如果在DTO中同时设置了库存特色,您将如何处理?

2.创建一个视图来构建您的搜索基本上您在Spring中创建了一个数据库视图和一个具有所有可能参数的实体。

原始表格:

 |TABLE_A   |     |TABLE_B   |
 |ID|FIELD_A|     |ID|FIELD_B|
 |__________|     |__________|
 | 1|EXAMPLE|     | 1|  STUFF|

查看:

 |COMBINED_VIEW                |
 |ID|FIELD_A|FIELD_B|ORIG_TABLE|
 |_____________________________|
 | 1|EXAMPLE|   null|   TABLE_A|
 | 1|   null|  STUFF|   TABLE_B|

创作:

SELECT 
 ID as id, FIELD_A as field_a, null as field_b, 'TABLE_A' as ORIG_TABLE
 FROM TABLE_A 
UNION ALL
SELECT 
 ID as id, null as field_a, FIELD_B as field_b, 'TABLE_B' as ORIG_TABLE
 FROM TABLE_B 

但要小心,如果您使用缓存,添加生成的id或将ORIG_TABLE包含在@Id映射中,则ID字段可能会弄乱您。

TL.DR.:第二种方法是你可以绘制出你需要的所有东西,(在我看来)这是一种可以接受的获得结果的方法。如果设置的问题参数比单个实体的可能多,您也不必考虑这一点。(例如:FIELD_A='example'和FIELD_ B='STUFF'根本没有结果。)虽然还有其他方法可以实现这一点,比如使用JPQL和左连接和构造函数映射,但我认为第二种方法是最清晰、最易维护的方法。

 类似资料:
  • 问题内容: 有什么方法可以根据类型在同一字段上搜索结果? 我的基本提升是这样的: 但是对于其他一些文档,我希望标题不太重要,因此我尝试使用type作为前缀: 但是,这根本没有助益。作为最后的选择,我可以使用Funcsion / Script Score bu来避免这种情况。 例如,假设文档仅包含字段。 问题答案: 实现此目的的一种简单方法是将OP中的查询重写为dis- max查询 。 elasti

  • 如果有一种方法我可以用JPA做到这一点,请帮助我! 提前致谢

  • 我想在Spring-Boot后端创建一个多字段搜索。如何使用实现这一点? 环境 前端的UI是Jquery DataTable。每列允许应用单个字符串搜索项。跨多个列的搜索词由联接。 看来QueryDSL是解决这个问题的更简单、更好的方法。我在用Gradle。我需要改变我的构造吗?

  • 我是使用谓词的新手,不确定我是否正确理解了它。我有一个抽象的类,其中每小时和薪水员工是分开创建的。我的问题依赖于我的类,我不确定如何检查它是否是每小时员工并返回真或假。 我需要为以下所有条件创建不同的谓词: 所有员工,仅每小时一次,仅限工资和全职。 到目前为止,我只是想先让“仅每小时”谓词正常工作,然后再找出剩下的谓词。我不确定在“p”后面加什么来检查它是哪种类型的员工。我目前拥有的是: 我还有一

  • 我有以下查询 http://172.22.23.169:9200/index1,index2,index3/_search?pretty 使用这个URL,我想查询Elasticsearch中的许多索引,并且只返回那些存在特定字段的文档。 是否可以在“exists”子句中放入一个字段列表,我在其中定义“field1”、“field2”或“fiedl3”是否存在于某个文档中,否则返回它,或者我必须编写

  • 我工作与elasticsearch和我wnat做这种类型的查询和我有字段1,字段2类型int和字段3 bolean和字段4字符串问题是elasticsearch将始终返回解析错误,因为他会尝试比较rxample字段3与100任何工作解决方案