spring boot - स्प्रिंग बूट और जेपीए: वैकल्पिक, बराबरी के मापदंड के साथ खोज प्रश्नों को लागू करना




spring-boot spring-data-jpa (3)

यह एक SSCCE है , अनुसंधान से पता चलता है, एक डुबकी नहीं है और विषय पर है !!!

स्प्रिंग बूट रेस्ट सर्विस और MySQL यहां। मेरी निम्नलिखित Profile इकाई है:

@Entity
@Table(name = "profiles")
public class Profile extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "profile_given_name")
    private String givenName;

    @Column(name = "profile_surname")
    private String surname;

    @Column(name = "profile_is_male")
    private Integer isMale;

    @Column(name = "profile_height_meters", columnDefinition = "DOUBLE")
    private BigDecimal heightMeters;

    @Column(name = "profile_weight_kilos", columnDefinition = "DOUBLE")
    private BigDecimal weightKilos;

    @Column(name = "profile_dob")
    private Date dob;

    // Getters, setters & ctor down here
}

मेरे पास एक ProfileController भी है और मैं एक GET समापन बिंदु को उजागर करना चाहता हूं जो मानदंडों की एक बड़ी श्रृंखला के आधार पर Profiles लिए खोज करने के लिए वास्तव में लचीला / मजबूत तरीका प्रदान करता है:

# Search for women between 1.2 and 1.8 meters tall.
GET /v1/profiles?isMale=0&heightMeters={"gt": 1.2, "lt": 1.8}

# Search for men born after Jan 1, 1990 who weigh less than 100 kg.
GET /v1/profiles?isMale=1&dob={"gt" : "1990-01-01 00:00:00"}&weightKilos={"lt": 100.0}

आदि।

तो यहाँ मेरा नियंत्रक है:

@RestController
@RequestMapping("/v1/profiles")
public class ProfileResource {
  @Autowired
  ProfileRepository profileRepository;

  @GetMapping
  public ResponseEntity<Set<Profile>> searchProfiles(@RequestParam(value = "isMale", required = false) String isMaleVal,
                                              @RequestParam(value = "heightMeters", required = false) String heightMetersVal,
                                              @RequestParam(value = "weightKilos", required = false) String weightKilosVal,
                                              @RequestParam(value = "dob", required = false) String dobVal) {

      Integer isMaleVal;
      BooleanCriteria isMaleCriteria;
      if(isMaleVal != null) {
        // Parse the value which could either be "0" for female, "1" for male or something like
        // ?isMale={0,1} to indicate

        // BooleanCriteria would store which values male, female or both) to include in the search
      }

      BigDecimal heighMeters;
      BigDecimalCriteria heightCriteria;
      if(heightMetersVal != null) {
        // Parse the value which like in the examples could be something like:
        // ?heightMeters={"gt" : "1.0"}

        // BigDecimalCriteria stores range information
      }

      BigDecimal heighMeters;
      BigDecimalCriteria weightCriteria;
      if(weightKilosVal != null) {
        // Parse the value which like in the examples could be something like:
        // ?weightKilos={"eq" : "100.5"}

        // BigDecimalCriteria stores range information
      }

      // Ditto for DOB and DateCriteria

      // TODO: How to pack all of these "criteria" POJOs into a
      // CrudRepository/JPQL query against the "profiles" table?
      Set<Profile> profiles = profileRepository.searchProfiles(
        isMaleCriteria, heightCriteria, weightCriteria, dobCriteria);
    }
}

मेरा कहना है कि, BigDecimalCriteria कुछ इस तरह होगा:

// Basically it just stores the (validated) search criteria that comes in over the wire
// on the controller method
public class BigDecimalCriteria {
  private BigDecimal lowerBound;
  private Boolean lowerBoundInclusive;
  private BigDecimal upperBound;
  private Boolean upperBoundInclusive;

  // Getters, setters, ctors, etc.
}

चूँकि ये सभी खोज मापदंड वैकल्पिक हैं (और इस प्रकार null हो सकते हैं), मैं इस पर अटका हुआ हूँ कि किस तरह से ProfileRepository क्वेरी को ProfileRepository में ProfileRepository :

public interface ProfileRepository extends CrudRepository<Profile,Long> {
  @Query("???")
  public Set<Profile> searchProfiles();
}

मैं @Query(...) ProfileRepository#searchProfiles के लिए इस तरह से @Query(...) को कैसे लागू कर सकता हूं जो मेरे सभी खोज मानदंडों (खोज के लिए सभी अनुमेय श्रेणियों और मानों को देखते हुए @Query(...) को सक्षम बनाता है, और किसी भी मापदंड को शून्य या वैकल्पिक होने देता है। ?

बेशक, अगर कोई निफ्टी थोड़ा पुस्तकालय है या अगर स्प्रिंग बूट / जेपीए के पास पहले से ही इसके लिए एक समाधान है, तो मैं सभी कान हूं!


आप वसंत डेटा में JpaSpecificationExecutor द्वारा विनिर्देशों के साथ जटिल प्रश्न प्राप्त कर सकते हैं। रिपॉजिटरी इंटरफ़ेस को JpaSpecificationExecutor<T> इंटरफ़ेस का विस्तार करना चाहिए ताकि हम नई Specification<T> ऑब्जेक्ट बनाकर अपने डेटाबेस प्रश्नों की शर्तों को निर्दिष्ट कर सकें।

चाल JpaSpecificationExecutor साथ संयोजन में विनिर्देश इंटरफ़ेस के उपयोग में है। यहाँ उदाहरण है:

@Entity
@Table(name = "person")
public class Person {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;

 @Column(name = "name")
 private String name;

 @Column(name = "surname")
 private String surname;

 @Column(name = "city")
 private String city;

 @Column(name = "age")
 private Integer age;

        ....

}

तब हम अपने भंडार को परिभाषित करते हैं:

public interface PersonRepository extends JpaRepository<Person, Long>, JpaSpecificationExecutor<Person> {

}

जैसा कि आप देख सकते हैं कि हमने एक और इंटरफ़ेस JpaSpecificationExecutor बढ़ाया है। यह इंटरफ़ेस विनिर्देश श्रेणी के माध्यम से खोज करने के तरीकों को परिभाषित करता है।

अब हमें क्या करना है कि हमारे विनिर्देशन को परिभाषित करें जो क्वेरी के लिए बाधाओं से युक्त Predicate को लौटा देगा (उदाहरण में PersonSpecification उस क्वेरी का चयन उस व्यक्ति से कर रहा है जहां नाम =? या (उपनाम =? और उम्र =?) ):

public class PersonSpecification implements Specification<Person> {

    private Person filter;

    public PersonSpecification(Person filter) {
        super();
        this.filter = filter;
    }

    public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq,
            CriteriaBuilder cb) {

        Predicate p = cb.disjunction();

        if (filter.getName() != null) {
            p.getExpressions()
                    .add(cb.equal(root.get("name"), filter.getName()));
        }

        if (filter.getSurname() != null && filter.getAge() != null) {
            p.getExpressions().add(
                    cb.and(cb.equal(root.get("surname"), filter.getSurname()),
                            cb.equal(root.get("age"), filter.getAge())));
        }

        return p;
    }
}

अब इसका उपयोग करने का समय आ गया है। निम्नलिखित कोड टुकड़ा दिखाता है कि हमारे द्वारा अभी बनाए गए विनिर्देश का उपयोग कैसे करें:

...

Person filter = new Person();
filter.setName("Mario");
filter.setSurname("Verdi");
filter.setAge(25);

Specification<Person> spec = new PersonSpecification(filter);

List<Person> result = repository.findAll(spec);

Here जीतूब में पूर्ण उदाहरण मौजूद है

इसके अलावा, आप विशिष्टता का उपयोग करके कोई भी जटिल प्रश्न बना सकते हैं


इसका उत्तर काफी आसान है और आप query-by-example का उपयोग वसंत में कर सकते हैं।

और इससे भी अधिक आपको अपने नियंत्रक में सभी Profile गुणों को सूचीबद्ध करने की आवश्यकता नहीं है, आप बस Profile को पैरामीटर के रूप में लेते हैं, वसंत इसका ख्याल रखेगा।

और जैसा कि आप अनुरोध परमेस को मान्य करना चाहते हैं, यहां बीन सत्यापनकर्ता के साथ एकीकरण करने के लिए ईयर है, उदाहरण के रूप में "दिया गया नाम" लें। इकाई में @Valid जोड़ें, और नियंत्रक में @Valid जोड़ें, यदि "दिया गया" अनुरोध अनुरोधों में नहीं है, तो आपको "खराब अनुरोध" प्रतिक्रिया मिलेगी।

यहाँ काम कर रहे कोड हैं:

@Entity
@Table(name = "profiles")
public class Profile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "profile_given_name")
    @NotNull
    private String givenName;

    @Column(name = "profile_surname")
    private String surname;

    @Column(name = "profile_is_male")
    private Integer isMale;

    @Column(name = "profile_height_meters", columnDefinition = "DOUBLE")
    private BigDecimal heightMeters;

    @Column(name = "profile_weight_kilos", columnDefinition = "DOUBLE")
    private BigDecimal weightKilos;

    @Column(name = "profile_dob")
    private Date dob;
}

ProfileResource

@RestController
@RequestMapping("/v1/profiles")
public class ProfileResource {
    @Autowired
    ProfileRepository profileRepository;

    @GetMapping
    public ResponseEntity<List<Profile>> searchProfiles(@Valid Profile profile) {
        List<Profile> all = profileRepository.findAll(Example.of(profile));
        return ResponseEntity.ok(all);
    }
}

ProfileRepository

public interface ProfileRepository extends JpaRepository<Profile, Long> {
}

इसके बाद GET /v1/profiles?isMale=0 भेजें GET /v1/profiles?isMale=0 कि आप चाहते हैं।


वसंत डेटा में "उदाहरण के द्वारा क्वेरी" देखें। लगता है कि आप क्या जरूरत के लिए बिल फिट ...

query-by-example





query-by-example