ไลบรารีสำหรับการสร้างและเรียกใช้คิวรีขั้นสูงและไดนามิกโดยใช้ JPA ใน Spring Boot
Map<String, String>
เพื่อรองรับจุดสิ้นสุด GET ด้วยพารามิเตอร์แบบสอบถามผ่าน jpa-search-helper คอนโทรลเลอร์ของคุณ* จะสามารถรับคำขอเช่นนี้:
โหมด 1 :
curl -X GET
' https://myexampledomain.com/persons?
firstName=Biagio
&lastName_startsWith=Toz
&birthDate_gte=19910101
&country_in=IT,FR,DE
&address_eq=Via Roma 1,Via Milano/,1,20 West/,34th Street
&company.name_in=Microsoft,Apple,Google
&company.employees_between=500,5000 '
โหมด 2 :
curl -X POST -H " Content-type: application/json " -d ' {
"filter" : {
"operator": "and", // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator": "eq",
"key": "firstName",
"value": "Biagio"
},
{
"operator": "or",
"filters": [
{
"operator": "startsWith",
"key": "lastName",
"value": "Toz",
"options": {
"ignoreCase": true
}
},
{
"operator": "endsWith",
"key": "lastName",
"value": "ZZI",
"options": {
"ignoreCase": true,
"trim" : true
}
}
]
},
{
"operator": "in",
"key": "company.name",
"values": ["Microsoft", "Apple", "Google"]
},
{
"operator": "or",
"filters": [
{
"operator": "gte",
"key": "birthDate",
"value": "19910101"
},
{
"operator": "lte",
"key": "birthDate",
"value": "20010101"
}
]
},
{
"operator": "between",
"key" : "company.employees",
"values": [500, 5000],
"options": {
"negate": true
}
}
]
},
"options": {
"pageSize": 10,
"pageOffset": 0,
"sortKey": "birthDate",
"sortDesc": false
}
} ' ' https://myexampledomain.com/persons '
..คุณจะทำอย่างไร? อ่านเรื่องนี้เลย!
* โปรดทราบ: ไลบรารีไม่ได้เปิดเผยตัวควบคุม/จุดสิ้นสุด HTTP แต่มีเพียงพื้นที่เก็บข้อมูลที่จะสร้างและดำเนินการแบบสอบถามเท่านั้น
ตัวช่วยค้นหา JPA | รองเท้าบูทสปริง | ชวา |
---|---|---|
[v0.0.1 - v2.1.1] | 3.2.x | [17 - 23] |
[v3.0.0 - v3.2.2] | 3.3.x | [17 - 23] |
[v3.3.0 - ล่าสุด] | 3.4.x | [17 - 23] |
<dependency>
<groupId>app.tozzi</groupId>
<artifactId>jpa-search-helper</artifactId>
<version>3.3.0</version>
</dependency>
implementation 'app.tozzi:jpa-search-helper:3.3.0
@Searchable
เริ่มต้นด้วยการใช้คำอธิบายประกอบ @Searchable
กับฟิลด์ใน Domain Model ของคุณ หรืออีกวิธีหนึ่งคือเอนทิตี JPA ของคุณ ที่คุณต้องการให้พร้อมสำหรับการค้นหา หากคุณมีเขตข้อมูลที่คุณต้องการให้ค้นหาได้ภายในวัตถุอื่น ๆ ให้ใส่คำอธิบายประกอบสิ่งเหล่านี้ด้วย @NestedSearchable
@ Data
public class Person {
@ Searchable
private String firstName ;
@ Searchable
private String lastName ;
@ Searchable ( entityFieldKey = "dateOfBirth" )
private Date birthDate ;
@ Searchable
private String country ;
@ Searchable
private String fillerOne ;
@ Searchable
private String fillerTwo ;
@ NestedSearchable
private Company company ;
@ Data
public static class Company {
@ Searchable ( entityFieldKey = "companyEntity.name" )
private String name ;
@ Searchable ( entityFieldKey = "companyEntity.employeesCount" )
private int employees ;
}
}
คำอธิบายประกอบช่วยให้คุณระบุ:
คุณสมบัติหลัก:
entityFieldKey
: ชื่อของฟิลด์ที่กำหนดบนเอนทิตี bean (ไม่ต้องระบุหากใช้คำอธิบายประกอบบนเอนทิตีบีน) หากไม่ได้ระบุคีย์จะเป็นชื่อฟิลด์
targetType
: ประเภทออบเจ็กต์ที่ได้รับการจัดการตามเอนทิตี หากไม่ได้ระบุ librariy จะพยายามรับมันตามประเภทฟิลด์ (เช่น ฟิลด์จำนวนเต็มที่ไม่มีคำจำกัดความประเภทเป้าหมายจะเป็น INTEGER
) หากไม่มีประเภทที่เข้ากันได้กับประเภทที่ได้รับการจัดการ จะมีการจัดการเป็นสตริง ประเภทที่ได้รับการจัดการ:
STRING
, INTEGER
, DOUBLE
, FLOAT
, LONG
, BIGDECIMAL
, BOOLEAN
, DATE
, LOCALDATE
, LOCALDATETIME
, LOCALTIME
, OFFSETDATETIME
, OFFSETTIME
, ZONEDDATETIME
คุณสมบัติการตรวจสอบ:
datePattern
: เฉพาะสำหรับประเภทเป้าหมาย DATE
, LOCALDATE
, LOCALDATETIME
, LOCALTIME
, OFFSETDATETIME
, OFFSETTIME
, ZONEDDATETIME
กำหนดรูปแบบวันที่ที่จะใช้maxSize, minSize
: ความยาวสูงสุด/ต่ำสุดของค่าmaxDigits, minDigits
: สำหรับประเภทตัวเลขเท่านั้น จำนวนหลักสูงสุด/ต่ำสุดregexPattern
: รูปแบบ regexdecimalFormat
: สำหรับประเภทตัวเลขทศนิยมเท่านั้น ค่าเริ่มต้น #.##
อื่น:
sortable
: หากเป็นเท็จ ฟิลด์นี้สามารถนำมาใช้ในการค้นหาได้ แต่ไม่สามารถใช้สำหรับการเรียงลำดับได้ ค่าเริ่มต้น: จริงtrim
: ใช้ตัดแต่งtags
: มีประโยชน์หากฟิลด์รูปแบบโดเมนสามารถสอดคล้องกับฟิลด์เอนทิตีหลายฟิลด์ (ตัวอย่างมีให้ดูเพิ่มเติมด้านล่าง)allowedFilters
: ตัวกรองที่อนุญาตโดยเฉพาะnotAllowedFilters
: ไม่อนุญาตให้ใช้ตัวกรองlikeFilters
: อนุญาตเหมือนตัวกรอง ( ประกอบด้วย , beginningWith , สิ้นสุดด้วย ) ค่าเริ่มต้น: จริงดำเนินการต่อตัวอย่างคลาสเอนทิตีของเรา:
@ Entity
@ Data
public class PersonEntity {
@ Id
private Long id ;
@ Column ( name = "FIRST_NAME" )
private String firstName ;
@ Column ( name = "LAST_NAME" )
private String lastName ;
@ Column ( name = "BIRTH_DATE" )
private Date dateOfBirth ;
@ Column ( name = "COUNTRY" )
private String country ;
@ Column ( name = "FIL_ONE" )
private String fillerOne ;
@ Column ( name = "FIL_TWO" )
private String fillerTwo ;
@ OneToOne
private CompanyEntity companyEntity ;
}
@ Entity
@ Data
public class CompanyEntity {
@ Id
private Long id ;
@ Column ( name = "NAME" )
private String name ;
@ Column ( name = "COUNT" )
private Integer employeesCount ;
}
JPASearchRepository
พื้นที่เก็บข้อมูล Spring JPA ของคุณต้องขยาย JPASearchRepository<YourEntityClass>
@ Repository
public interface PersonRepository extends JpaRepository < PersonEntity , Long >, JPASearchRepository < PersonEntity > {
}
ในผู้จัดการของคุณ หรือในบริการของคุณ หรือที่ใดก็ตามที่คุณต้องการใช้พื้นที่เก็บข้อมูล:
โหมด 1 : กำหนดแผนที่ <filter_key#options, value> :
// ...
@ Autowired
private PersonRepository personRepository ;
public List < Person > advancedSearch () {
// Pure example, in real use case it is expected that these filters can be passed directly by the controller
Map < String , String > filters = new HashMap <>();
filters . put ( "firstName_eq" , "Biagio" );
filters . put ( "lastName_startsWith#i" , "Toz" ); // ignore case
filters . put ( "birthDate_gte" , "19910101" );
filters . put ( "country_in" , "IT,FR,DE" );
filters . put ( "company.name_eq#n" , "Bad Company" ); // negation
filters . put ( "company.employees_between" , "500,5000" );
filters . put ( "fillerOne_null#n" , "true" ); // not null
filters . put ( "fillerTwo_empty" , "true" ); // empty
// Without pagination
List < PersonEntity > fullSearch = personRepository . findAll ( filters , Person . class );
filters . put ( "birthDate_sort" : "ASC" ); // sorting key and sorting order
filters . put ( "_limit" , "10" ); // page size
filters . put ( "_offset" , "0" ); // page offset
// With pagination
Page < PersonEntity > sortedAndPaginatedSearch = personRepository . findAllWithPaginationAndSorting ( filters , Person . class );
// ...
}
// ...
โหมด 2 : แทนที่จะใช้แผนที่ คุณจะต้องใช้ JPASearchInput
ดังที่แสดงไว้ที่นี่ เพื่อความง่ายในรูปแบบ JSON
{
"filter" : {
"operator" : " and " , // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator" : " eq " ,
"key" : " firstName " ,
"value" : " Biagio "
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " startsWith " ,
"key" : " lastName " ,
"value" : " Toz " ,
"options" : {
"ignoreCase" : true
}
},
{
"operator" : " endsWith " ,
"key" : " lastName " ,
"value" : " ZZI " ,
"options" : {
"ignoreCase" : true ,
"trim" : true
}
}
]
},
{
"operator" : " in " ,
"key" : " company.name " ,
"values" : [ " Microsoft " , " Apple " , " Google " ]
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " gte " ,
"key" : " birthDate " ,
"value" : " 19910101 "
},
{
"operator" : " lte " ,
"key" : " birthDate " ,
"value" : " 20010101 "
}
]
},
{
"operator" : " empty " ,
"key" : " fillerOne " ,
"options" : {
"negate" : true
}
},
{
"operator" : " between " ,
"key" : " company.employees " ,
"values" : [ 500 , 5000 ],
"options" : {
"negate" : true
}
}
]
},
"options" : {
"pageSize" : 10 ,
"pageOffset" : 0 ,
"sortKey" : " birthDate " ,
"sortDesc" : false
}
}
ผ่าน โหมด 2 คุณสามารถจัดการตัวกรองที่ซับซ้อนด้วย AND
, OR
และ NOT
(ดูภายหลัง)
InvalidFieldException
InvalidValueException
JPASearchException
ชื่อตัวกรอง | รหัสห้องสมุด | โหมดที่รองรับ |
---|---|---|
และ | และ | 1, 2 |
หรือ | หรือ | 2 |
ไม่ | ไม่ | 2 |
เมื่อใช้ โหมด 1 ตัวกรองทั้งหมดจะสร้างการค้นหา AND
โดยเฉพาะ
หากต้องการใช้ตัวดำเนินการอื่น OR
และ NOT
คุณต้องใช้ โหมด 2
ชื่อตัวกรอง | รหัสห้องสมุด | SQL | โหมดที่รองรับ | ค่าที่ต้องการ |
---|---|---|---|---|
เท่ากับ | สมการ | sql_col = วาล | 1,2 | ใช่ |
ประกอบด้วย | ประกอบด้วย | sql_col เช่น '%val%' | 1,2 | ใช่ |
ใน | ใน | sql_col ใน (val1, val2, ..., valN) | 1,2 | ใช่ |
เริ่มต้นด้วย | เริ่มต้นด้วย | sql_col เช่น 'วาล%' | 1,2 | ใช่ |
ลงท้ายด้วย | สิ้นสุดด้วย | sql_col เช่น '%val' | 1,2 | ใช่ |
มากกว่า | gt | sql_col > วาล | 1,2 | ใช่ |
มากกว่าหรือเท่ากับ | รับ | sql_col >= วาล | 1,2 | ใช่ |
น้อยกว่า | lt | sql_col < วาล | 1,2 | ใช่ |
น้อยกว่าหรือเท่ากับ | อินเตอร์เน็ตไร้สาย | sql_col <= วาล | 1,2 | ใช่ |
ระหว่าง | ระหว่าง | sql_col ระหว่าง val1 และ val2 | 1,2 | ใช่ |
โมฆะ | โมฆะ | sql_col เป็นโมฆะ | 1,2 | เลขที่ |
ว่างเปล่า | ว่างเปล่า | sql_collection_col เป็นโมฆะ | 1,2 | เลขที่ |
โหมด 1
คำอธิบายตัวเลือก | รหัสห้องสมุด |
---|---|
ละเว้นกรณี | #ฉัน |
การปฏิเสธ | #น |
ตัดแต่ง | #ที |
ต้องเพิ่มคีย์ตัวเลือกต่อท้ายตัวกรอง เช่น ?firstName_eq#i=Biagio หรือ ?firstName_eq#i#n=Biagio
โหมด 2
คำอธิบายตัวเลือก | คีย์ไลบรารี (แอตทริบิวต์ Java) |
---|---|
ละเว้นกรณี | ละเว้นกรณี |
การปฏิเสธ | ปฏิเสธ |
ตัดแต่ง | เล็ม |
สำหรับแต่ละตัวกรอง คุณสามารถกำหนด options
ได้
{
// ...
{
"operator" : " eq " ,
"key" : " firstName " ,
"value" : " Biagio " ,
"options" : {
"ignoreCase" : true ,
"trim" : false ,
"negate" : true
}
}
// ...
}
วัตถุจาวา:
@ Data
public static class JPASearchFilterOptions {
private boolean ignoreCase ;
private boolean trim ;
private boolean negate ;
}
ชื่อตัวกรอง | สำคัญ | ค่าคงที่ |
---|---|---|
ขีดจำกัด (ขนาดหน้า) | ขีด จำกัด | |
ออฟเซ็ต (หมายเลขหน้า) | ชดเชย | |
เรียงลำดับ | เรียงลำดับ | ASC, DESC |
โหมด 1 : เช่น ?firstName_sort=DESC&_limit=10&_offset=0
โหมด 2 : options
รูทค่า:
{
"filter" : {
// ...
},
"options" : {
"sortKey" : " firstName " ,
"sortDesc" : true ,
"pageSize" : 10 ,
"pageOffset" : 1
}
}
วัตถุจาวา:
@ Data
public static class JPASearchOptions {
private String sortKey ;
private Boolean sortDesc = false ;
private Integer pageSize ;
private Integer pageOffset ;
private List < String > selections ;
}
,
: เช่น ?myField_in=test1,test2 --> ค่าที่จะค้นหา: [" test1 ", " test2 "]/,
: เช่น ?myField_in=test1,test2/,test3 --> ค่าที่ต้องการค้นหา: [" test1 ", " test2,test3 "] @Projectable
เริ่มต้นด้วยการใช้คำอธิบายประกอบ @Projectable
กับฟิลด์ใน Domain Model ของคุณหรือเอนทิตี JPA ของคุณ ที่คุณต้องการให้พร้อมสำหรับการเลือก หากคุณมีฟิลด์ที่คุณต้องการให้สามารถเลือกได้ภายในวัตถุอื่น ๆ ให้ใส่คำอธิบายประกอบสิ่งเหล่านี้ด้วย @NestedProjectable
@ Data
public class Person {
@ Searchable
private String firstName ;
@ Projectable
@ Searchable
private String lastName ;
@ Projectable ( entityFieldKey = "dateOfBirth" )
@ Searchable ( entityFieldKey = "dateOfBirth" )
private Date birthDate ;
@ Searchable
private String country ;
@ Searchable
private String fillerOne ;
@ Searchable
private String fillerTwo ;
@ NestedProjectable
@ NestedSearchable
private Company company ;
@ Data
public static class Company {
@ Searchable ( entityFieldKey = "companyEntity.name" )
private String name ;
@ Projectable ( entityFieldKey = "companyEntity.employeesCount" )
@ Searchable ( entityFieldKey = "companyEntity.employeesCount" )
private int employees ;
}
}
คำอธิบายประกอบช่วยให้คุณระบุ:
คุณสมบัติหลัก:
entityFieldKey
: ชื่อของฟิลด์ที่กำหนดบนเอนทิตี bean (ไม่ต้องระบุหากใช้คำอธิบายประกอบบนเอนทิตี bean) หากไม่ได้ระบุคีย์จะเป็นชื่อฟิลด์JPASearchRepository
พื้นที่เก็บข้อมูล Spring JPA ของคุณต้องขยาย JPAProjectionRepository<YourEntityClass>
@ Repository
public interface PersonRepository extends JpaRepository < PersonEntity , Long >, JPASearchRepository < PersonEntity >, JPAProjectionRepository < PersonEntity > {
}
ในผู้จัดการของคุณ หรือในบริการของคุณ หรือที่ใดก็ตามที่คุณต้องการใช้พื้นที่เก็บข้อมูล:
โหมด 1 : กำหนด (หรือเพิ่มแผนที่ที่ใช้สำหรับการค้นหาโหมด 1) แผนที่:
,
// ...
@ Autowired
private PersonRepository personRepository ;
public List < Person > advancedSearch () {
// Pure example, in real use case it is expected that these filters can be passed directly by the controller
Map < String , String > filters = new HashMap <>();
filters . put ( "firstName_eq" , "Biagio" );
filters . put ( "lastName_startsWith#i" , "Toz" ); // ignore case
filters . put ( "birthDate_gte" , "19910101" );
filters . put ( "country_in" , "IT,FR,DE" );
filters . put ( "company.name_eq#n" , "Bad Company" ); // negation
filters . put ( "company.employees_between" , "500,5000" );
filters . put ( "fillerOne_null#n" , "true" ); // not null
filters . put ( "fillerTwo_empty" , "true" ); // empty
// Selections
filters . put ( "selections" , "lastName,birthDate,company.employees" );
// Without sorting
List < Map < String , Object >> result = personRepository . projection ( filters , Person . class , PersonEntity . class );
filters . put ( "birthDate_sort" : "ASC" ); // sorting key and sorting order
// With sorting
List < Map < String , Object >> sortedAndPaginatedSearch = personRepository . projectionWithSorting ( filters , Person . class , PersonEntity . class );
// ... convert the list of maps into your model
}
// ...
โหมด 2 : แทนที่จะใช้แผนที่ คุณจะต้องใช้ JPASearchInput
ดังที่แสดงไว้ที่นี่ เพื่อความง่ายในรูปแบบ JSON
{
"filter" : {
"operator" : " and " , // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator" : " eq " ,
"key" : " firstName " ,
"value" : " Biagio "
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " startsWith " ,
"key" : " lastName " ,
"value" : " Toz " ,
"options" : {
"ignoreCase" : true
}
},
{
"operator" : " endsWith " ,
"key" : " lastName " ,
"value" : " ZZI " ,
"options" : {
"ignoreCase" : true ,
"trim" : true
}
}
]
},
{
"operator" : " in " ,
"key" : " company.name " ,
"values" : [ " Microsoft " , " Apple " , " Google " ]
},
{
"operator" : " or " ,
"filters" : [
{
"operator" : " gte " ,
"key" : " birthDate " ,
"value" : " 19910101 "
},
{
"operator" : " lte " ,
"key" : " birthDate " ,
"value" : " 20010101 "
}
]
},
{
"operator" : " empty " ,
"key" : " fillerOne " ,
"options" : {
"negate" : true
}
},
{
"operator" : " between " ,
"key" : " company.employees " ,
"values" : [ 500 , 5000 ],
"options" : {
"negate" : true
}
}
]
},
"options" : {
"pageSize" : 10 ,
"pageOffset" : 0 ,
"sortKey" : " birthDate " ,
"sortDesc" : false ,
"selections" : [
" lastName " ,
" birthDate " ,
" company.employees "
]
}
}
สำหรับทั้งสองโหมด การฉายภาพจะส่งกลับผลลัพธ์ List<Map<String, Object>> โดยที่ โครงสร้างแผนที่และคีย์จะสะท้อนถึงโครงสร้างเอนทิตี (เพื่อให้ชัดเจน toJson(entityList) == toJson(mapList) )
หมายเหตุ 1:
ระวัง: การฉายภาพเริ่มต้นจะบังคับให้ความสัมพันธ์เข้าร่วมทั้งหมดเป็น LEFT JOIN หากคุณไม่ต้องการให้มีพฤติกรรมเช่นนี้ ให้เลือกใช้วิธีการเก็บข้อมูล (วิธีการที่มีส่วนต่อท้าย 'Classic') ที่ช่วยให้คุณสามารถแก้ไขเฉพาะความสัมพันธ์ที่คุณต้องการแก้ไขเท่านั้น
หมายเหตุ 2:
การฉายภาพ ไม่ว่าคุณต้องการหรือไม่ก็ตาม จะแยกฟิลด์ที่แสดงถึงคีย์หลักของเอนทิตี (หรือเอนทิตีที่เกี่ยวข้อง) เสมอ
หมายเหตุ 3:
ไม่สนับสนุนการแบ่งหน้า
InvalidFieldException
JPASearchException
เป็นไปได้ที่จะบังคับให้เข้าร่วมด้วยการดึงข้อมูลเพื่ออนุญาตให้ Hibernate (หรือกรอบงาน JPA ของคุณ) ดำเนินการค้นหาเดียวสำหรับความสัมพันธ์ที่กำหนดไว้ในเอนทิตี สิ่งนี้เป็นไปได้โดยไม่มีการแบ่งหน้า :
// ...
Map < String , JoinFetch > fetches = Map . of ( "companyEntity" , JoinFetch . LEFT );
personRepository . findAll ( filters , Person . class , fetches );
// ...
หากคุณมีโมเดลโดเมนที่เป็นผลมาจากการแปลงหลายเอนทิตี คุณสามารถระบุแผนที่ (สตริง สตริง) ได้อย่างชัดเจน ซึ่งคีย์แสดงถึงชื่อของฟิลด์โมเดลโดเมน และค่าคือชื่อของฟิลด์ของ เอนทิตีที่จะค้นหา:
// ...
Map < String , String > entityFieldMap = Map . of ( "company" , "companyEntity.name" );
// Without pagination
personRepository . findAll ( filters , Person . class , fetches , entityFieldMap );
// With pagination
personRepository . findAllWithPaginationAndSorting ( filters , Person . class , entityFieldMap );
// ...
กรณีพิเศษอีกกรณีหนึ่งอาจเป็นกรณีที่วัตถุสามารถทำซ้ำได้ภายใน Domain Model เพื่อเป็นตัวแทนของเอนทิตีหลายชิ้น ทางออกสำหรับการค้นหา:
@ Entity
public class CoupleEntity {
@ Id
private Long id ;
@ Column ( name = "p1_fn" )
private String p1FirstName ;
@ Column ( name = "p1_ln" )
private String p1LastName ;
@ Column ( name = "p2_fn" )
private String p2FirstName ;
@ Column ( name = "p2_ln" )
private String p2LastName ;
}
@ Data
public class Couple {
@ NestedSearchable
private Person p1 ;
@ NestedSearchable
private Person p2 ;
@ Data
public static class Person {
@ Searchable ( tags = {
@ Tag ( fieldKey = "p1.firstName" , entityFieldKey = "p1FirstName" ),
@ Tag ( fieldKey = "p2.firstName" , entityFieldKey = "p2FirstName" ),
})
private String firstName ;
@ Searchable ( tags = {
@ Tag ( fieldKey = "p1.lastName" , entityFieldKey = "p1LastName" ),
@ Tag ( fieldKey = "p2.lastName" , entityFieldKey = "p2LastName" ),
})
private String lastName ;
}
}
curl - request GET
- url ' https://www.myexampledomain.com/couples?
p1.firstName_iEq=Romeo
&p2.firstName_iEq=Giulietta '
โปรดทราบ: ไลบรารีนี้ไม่เปิดเผยจุดสิ้นสุดใดๆ ดังนั้นจึงไม่มีตัวควบคุม มีโครงการตัวอย่างที่ครบถ้วนสมบูรณ์และครบถ้วนได้ที่นี่
ตัวควบคุม:
@ RestController
@ RequestMapping ( "/persons" )
public class PersonController {
@ Autowired
private PersonManager personManager ;
@ GetMapping ( produces = MediaType . APPLICATION_JSON_VALUE )
public List < Person > findPersons ( @ RequestParam Map < String , String > requestParams ) {
return personManager . find ( requestParams );
}
@ GetMapping ( path = "/projection" , produces = MediaType . APPLICATION_JSON_VALUE )
public List < Person > projection ( @ RequestParam Map < String , String > requestParams ) {
return personManager . projection ( requestParams );
}
}
บริการ/ผู้จัดการบีน:
@ Service
public class PersonManager {
@ Autowired
private PersonRepository personRepository ;
public List < Person > find ( Map < String , String > filters ) {
return personRepository . findAllWithPaginationAndSorting ( filters , Person . class ). stream (). map ( this :: toModel ). toList ();
}
public List < Person > projection ( Map < String , String > filters ) {
return personRepository . projection ( filters , Person . class , PersonEntity . class ). stream (). map ( this :: toModel ). toList ();
}
private static Person toModel ( PersonEntity personEntity ) {
// ...
}
private static Person toModel ( Map < String , Object > entityMap ) {
// ...
}
}
ขด:
curl - X GET
' http://localhost:8080/persons?
firstName=Biagio
&lastName_startsWith=Toz
&birthDate_gte=19910101
&country_in=IT,FR,DE
&company.name_in=Microsoft,Apple
&company.employees_between=500,5000 '
หรือ
curl - X GET
' http://localhost:8080/persons/projection?
firstName=Biagio
&lastName_startsWith=Toz
&birthDate_gte=19910101
&country_in=IT,FR,DE
&company.name_in=Microsoft,Apple
&company.employees_between=500,5000
&selections=firstName,birthDate '
ตัวควบคุม:
@ RestController
@ RequestMapping ( "/persons" )
@ Validated
public class PersonController {
@ Autowired
private PersonManager personManager ;
@ PostMapping ( produces = MediaType . APPLICATION_JSON_VALUE , consumes = MediaType . APPLICATION_JSON_VALUE )
public List < Person > findPersons ( @ Valid @ RequestBody JPASearchInput input ) {
return personManager . find ( input );
}
}
@ PostMapping ( path = "/projection" , produces = MediaType . APPLICATION_JSON_VALUE , consumes = MediaType . APPLICATION_JSON_VALUE )
public List < Person > projection ( @ Valid @ RequestBody JPASearchInput input ) {
return personManager . projection ( input );
}
}
บริการ/ผู้จัดการบีน:
@ Service
public class PersonManager {
@ Autowired
private PersonRepository personRepository ;
public List < Person > find ( JPASearchInput input ) {
return personRepository . findAllWithPaginationAndSorting ( input , Person . class ). stream (). map ( this :: toModel ). toList ();
}
public List < Person > find ( JPASearchInput input ) {
return personRepository . projection ( input , Person . class , PersonEntity . class ). stream (). map ( this :: toModel ). toList ();
}
private static Person toModel ( PersonEntity entity ) {
// ...
}
private static Person toModel ( Map < String , Object > entityMap ) {
// ...
}
}
ขด:
curl -X POST -H " Content-type: application/json " -d ' {
"filter" : {
"operator": "and", // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator": "eq",
"key": "firstName",
"value": "Biagio"
},
{
"operator": "or",
"filters": [
{
"operator": "startsWith",
"key": "lastName",
"value": "Toz",
"options": {
"ignoreCase": true
}
},
{
"operator": "endsWith",
"key": "lastName",
"value": "ZZI",
"options": {
"ignoreCase": true,
"trim" : true
}
}
]
},
{
"operator": "in",
"key": "company.name",
"values": ["Microsoft", "Apple", "Google"]
},
{
"operator": "or",
"filters": [
{
"operator": "gte",
"key": "birthDate",
"value": "19910101"
},
{
"operator": "lte",
"key": "birthDate",
"value": "20010101"
}
]
},
{
"operator": "between",
"key" : "company.employees",
"values": [500, 5000],
"options": {
"negate": true
}
}
]
},
"options": {
"pageSize": 10,
"pageOffset": 0,
"sortKey": "birthDate",
"sortDesc": false
}
} ' ' http://localhost:8080/persons '
หรือ
curl -X POST -H " Content-type: application/json " -d ' {
"filter" : {
"operator": "and", // the first filter must contain a root operator: AND, OR or NOT
"filters" : [
{
"operator": "eq",
"key": "firstName",
"value": "Biagio"
},
{
"operator": "or",
"filters": [
{
"operator": "startsWith",
"key": "lastName",
"value": "Toz",
"options": {
"ignoreCase": true
}
},
{
"operator": "endsWith",
"key": "lastName",
"value": "ZZI",
"options": {
"ignoreCase": true,
"trim" : true
}
}
]
},
{
"operator": "in",
"key": "company.name",
"values": ["Microsoft", "Apple", "Google"]
},
{
"operator": "or",
"filters": [
{
"operator": "gte",
"key": "birthDate",
"value": "19910101"
},
{
"operator": "lte",
"key": "birthDate",
"value": "20010101"
}
]
},
{
"operator": "between",
"key" : "company.employees",
"values": [500, 5000],
"options": {
"negate": true
}
}
]
},
"options": {
"sortKey": "birthDate",
"sortDesc": false,
"selections" : [
"birthDate",
"firstName",
"lastName"
]
}
} ' ' http://localhost:8080/persons/projection '