如何构建查询以在值范围内搜索?

问题描述 投票:0回答:2

我在使用条件生成器创建查询时遇到问题。我有一个

Employee
表,其中有一列
SALARY
,工资以这种格式保存:价值 + 货币(例如:“1000 欧元”)。我没有为此使用
@Embeddable
类。相反,我使用休眠转换器来存储和读取工资,这是我的金钱课程:

public class Money implements Comparable<Money> {
    private BigDecimal amount;
    private Currency currency;

    @Override
    public String toString() {
        return getAmount() + " " + getCurrency();
    }

    public static Money getMoneyFromString(String s) {
        String[] strings = s.split(" ");
        return new Money(new BigDecimal(strings[0]),Currency.getInstance(strings[1]));
    }

    @Override
    public int compareTo(Money o) {
        if (o.getCurrency() == null || !o.getCurrency().equals(this.currency))
            throw new IllegalArgumentException();

        return this.amount.compareTo(o.getAmount());
    }
}

这是我如何在实体类中保留此类的字段

    @NotNull
    @Convert(
            converter = MoneyConverter.class
    )
    @Column(name = "SALARY", length = 63)
    private Money salary;

问题是我不知道如何通过使用条件来构建查询来查找工资在该范围内的员工,例如:

  1. 起薪=“1000 欧元”
  2. 最终工资=“30000 欧元”
  3. 员工工资 =“1500 欧元”- 应该可以找到

我尝试过这样的:

public static Specification<Employee> isSalaryBetween(Money salaryStart, Money salaryEnd) {
        return (root, query, criteriaBuilder) -> {
            Predicate salaryBetween = criteriaBuilder.between(
                    root.get(Employee_.salary), salaryStart, salaryEnd);

            return criteriaBuilder.and(salaryBetween);
        };
    }

但这并没有返回正确的值。所以我也尝试了这样的事情:

    Predicate predicate = builder.between(
                root.get("salary").get("amount"), 
                minSalary.getAmount(), 
                maxSalary.getAmount()
            );

但这会触发异常:

org.springframework.dao.InvalidDataAccessApiUsageException: Basic paths cannot 
be dereferenced

我也尝试过使用某种功能,但是没有成功。

那么还有什么其他可能找到薪资在一定范围内的员工呢?实现这一目标的唯一方法是更改数据库架构吗?

java spring jpa spring-data-jpa criteria
2个回答
0
投票

你可以尝试这样的事情

public static Specification<Employee> isSalaryBetween(Money salaryStart, Money salaryEnd) {
    return (root, query, criteriaBuilder) -> {
        Join<Employee, Money> salaryJoin = root.join("salary");
        
        Predicate salaryBetween = criteriaBuilder.between(
                salaryJoin.get("amount"), salaryStart.getAmount(), salaryEnd.getAmount());

        return salaryBetween;
    };
}

你也可以尝试这个方法

return (root, query, criteriaBuilder) -> {
        Predicate salaryGreaterThanOrEqualToStart = criteriaBuilder.greaterThanOrEqualTo(
                root.get("salary"), salaryStart);
        Predicate salaryLessThanOrEqualToEnd = criteriaBuilder.lessThanOrEqualTo(
                root.get("salary"), salaryEnd);

        return criteriaBuilder.and(salaryGreaterThanOrEqualToStart, salaryLessThanOrEqualToEnd);
    }

如果您有示例 git 项目,请分享它会更容易


0
投票

解决了问题。感谢 Alex Chernyshev 向我解释 db 如何存储 BigDecimal。为了解决这个问题,我需要更改我的 Money 类,现在它看起来像这样:

@AllArgsConstructor
@NoArgsConstructor
@Embeddable
@Getter
@Setter
@ToString
public class Money {
    private Double amount;
}

我删除了货币字段,因为我真的认为我的项目中不需要它,并用 Double 替换了 BigDecimal (由于其他一些原因我选择了非原始类型,这些原因与问题无关)。另外,我认为拥有货币字段不会成为问题,因为我的查询现在看起来像这样:

public static Specification<Employee> isSalaryBetween(double salaryStart, double salaryEnd) {
        return (root, query, criteriaBuilder) -> {
            Expression<Double> salaryAmount = root.get(Employee_.salary).get("amount");
            Predicate isSalaryBetween = criteriaBuilder.between(salaryAmount, salaryStart, salaryEnd);

            return criteriaBuilder.and(isSalaryBetween);
        };
    }

我还认为我可以简单地使用我的元模型中的工资字段,如下所示:

Predicate isSalaryBetween = criteriaBuilder.between(root.get(Employee_.salary),
                    salaryStart, salaryEnd);

但即使我将方法参数更改为 Money,它也不会起作用。 Criteria 不允许您使用自定义类型进行查询,因此这就是我需要从 Money 类获取金额字段的原因。

不幸的是,我还没有找到如何使用数量的类型安全字段,但它正在工作。

我希望这可以帮助那些面临这样问题的人

© www.soinside.com 2019 - 2024. All rights reserved.