简体   繁体   中英

Spring JPA Property expressions find by intersection of items in a list

How can I write a JPA repository method with property expressions that check for the existence of multiple items, or properties on those items, in a list? I can lookup a single item in the list, see zip code below, but I'm trying to write a way to check for multiple zip codes, where each Person in the result set has both zip codes in their list of addresses.

@Repository
public interface PersonRepository extends CrudRepository<Person, Long> {
    // Works
    Set<Person> findAllByAddresses_ZipCode(String zip);

    // Doesn't work - does not return expected results
    Set<Person> findAllByAddresses_ZipCode_And_Addresses_ZipCode(String zip1, String zip2);
}

My current hack is to fetch two sets for 2 zip codes, then find the intersection of the two sets:

public @ResponseBody
    Iterable<Person> personsWithBothZipCodes(@PathVariable("zip1") String zip1,
                                             @PathVariable("zip2") String zip2) {

    Set<Person> a = personRepository.findAllByAddresses_ZipCode(zip1);
    Set<Person> b = personRepository.findAllByAddresses_ZipCode(zip2);

    // Only return results that contain both zip1 and zip2.
    a.retainAll(b);

    return a;
}

The entity looks like this:

@Entity
public class Person
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    // zipcode is a String property on an Address.
    @OneToMany(targetEntity = com.data.Address.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private List<Address> addresses = new ArrayList<Address>();
    ...
}

Is there a way to write this as part of the method header? Relevant docs

Yes just add the word In to your query

Set<Person> findAllByAddresses_ZipCodeIn(Set<String> zip);

Then in your controller you can do something like:

public @ResponseBody Iterable<Person> personsWithBothZipCodes(@PathVariable("zip1") String zip1, @PathVariable("zip2") String zip2) {

    Set<String> zipSet = new HashSet<>();
    zipSet.add(zip1);
    zipSet.add(zip2);

    Set<Person> a = personRepository.findAllByAddresses_ZipCodeIn(zipSet);

    return a;
}

Dont know if this will work but can try

Set<Person> findAllByAddresses_ZipCodeInAndZipCodeIn(Set<String> zip1, Set<String> zip2);

public @ResponseBody Iterable<Person> personsWithBothZipCodes(@PathVariable("zip1") String zip1, @PathVariable("zip2") String zip2) {

    Set<String> zipSet1 = new HashSet<>();
    zipSet1.add(zip1);

    Set<String> zipSet2 = new HashSet<>();
    zipSet2.add(zip2);

    Set<Person> a = personRepository.findAllByAddresses_ZipCodeInAndZipCodeIn(zipSet1, zipSet2);

    return a;
}

Maybe you can use JPQL query (as suggested in comments)?

@Query("from Person as person" +
" join person.addresses as address1 with address1.zipCode = ?1" +
" join person.addresses as address2 with address2.zipCode = ?2")
Set<Person> findByZipCodes(String zipCode1, String zipCode2);

Haven't really reproduced your case, but it should probably work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM