CrudRepository的findBy方法签名用于元组列表

8

我有一个实体类,像这样:

@Entity
@Table(name = "CUSTOMER")
class Customer{
    @Id
    @Column(name = "Id")
    Long id;
    @Column(name = "EMAIL_ID")
    String emailId;
    @Column(name = "MOBILE")
    String mobile;
}

如何使用CrudRepository Spring Data JPA编写findBy方法来处理以下查询?

select * from customer where (email, mobile) IN (("a@b.c","8971"), ("e@f.g", "8888"))

我期望得到类似以下的内容:
List<Customer> findByEmailMobileIn(List<Tuple> tuples);

我希望从给定的一组数据中获取客户列表。

1
我认为你只能这样做:List<Customer> findByEmailInAndMobileIn(List<String> emails, List<String> mobiles); - Andrei Sfat
很遗憾。你觉得我应该在这种情况下使用Spring JdbcTemplate吗? - Rajesh
这并不是悲伤,而是spring-data框架的设计方式。你给它一个自定义对象,但是spring data无法理解你的意思。我不确定是否可以使用@Query注解来指定你想要的SQL语句,并与你的Tuple类一起使用。你需要去验证一下。 - Andrei Sfat
@Rajesh,你能用常规的@Query注释解决这个问题吗?我有一个复杂的本地查询,它使用了基于元组的检查,但是它会抛出一个错误。 - theprogrammer
2个回答

9

我认为可以使用org.springframework.data.jpa.domain.Specification来完成这个任务。您可以传递一个元组列表,并按照以下方式处理它们(不要担心Tuple不是实体,但您需要定义这个类):

public class CustomerSpecification implements Specification<Customer> {

    // names of the fields in your Customer entity
    private static final String CONST_EMAIL_ID = "emailId";
    private static final String CONST_MOBILE = "mobile";

    private List<MyTuple> tuples;

    public ClaimSpecification(List<MyTuple> tuples) {
        this.tuples = tuples;
    }

    @Override
    public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        // will be connected with logical OR
        List<Predicate> predicates = new ArrayList<>();

        tuples.forEach(tuple -> {
            List<Predicate> innerPredicates = new ArrayList<>();
            if (tuple.getEmail() != null) {
                 innerPredicates.add(cb.equal(root
                     .<String>get(CONST_EMAIL_ID), tuple.getEmail()));
            }
            if (tuple.getMobile() != null) {
                 innerPredicates.add(cb.equal(root
                     .<String>get(CONST_MOBILE), tuple.getMobile()));
            }
            // these predicates match a tuple, hence joined with AND
            predicates.add(andTogether(innerPredicates, cb));
        });

        return orTogether(predicates, cb);
    }

    private Predicate orTogether(List<Predicate> predicates, CriteriaBuilder cb) {
        return cb.or(predicates.toArray(new Predicate[0]));
    }

    private Predicate andTogether(List<Predicate> predicates, CriteriaBuilder cb) {
        return cb.and(predicates.toArray(new Predicate[0]));
    }
}

你的代码库应该扩展接口JpaSpecificationExecutor<Customer>
然后使用元组列表构建一个规范,并将其传递给方法customerRepo.findAll(Specification<Customer>) - 它会返回客户列表。

2
@dimirsen-z 这将生成许多AND子句的OR。如何生成类似所请求的查询?where (email, mobile) IN (("a@b.c","8971"), ("e@f.g", "8888")) - Lucas Soares

2
也许使用投影更加清晰:
@Entity
@Table(name = "CUSTOMER")
class CustomerQueryData {
    @Id
    @Column(name = "Id")
    Long id;
    @OneToOne
    @JoinColumns(@JoinColumn(name = "emailId"), @JoinColumn(name = "mobile"))
    Contact contact;
}

联系人实体:
@Entity
@Table(name = "CUSTOMER")
class Contact{
    @Column(name = "EMAIL_ID")
    String emailId;
    @Column(name = "MOBILE")
    String mobile;
}

在指定实体后,仓库:

CustomerJpaProjection extends Repository<CustomerQueryData, Long>, QueryDslPredicateExecutor<CustomerQueryData> {
    @Override
    List<CustomerQueryData> findAll(Predicate predicate);
 }

仓库调用:

ArrayList<Contact> contacts = new ArrayList<>();
contacts.add(new Contact("a@b.c","8971"));
contacts.add(new Contact("e@f.g", "8888"));
customerJpaProjection.findAll(QCustomerQueryData.customerQueryData.contact.in(contacts));

未经测试的代码。


不明白为什么QueryDslPredicateExecutor没有得到更多的关注。这使得一个相当棘手的用例成为可能。感谢分享! - Rainer

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