مكتبة لإنشاء وتشغيل الاستعلامات المتقدمة والديناميكية باستخدام 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 | التمهيد الربيع | جافا |
---|---|---|
[الإصدار 0.0.1 - الإصدار 2.1.1] | 3.2.x | [17 - 23] |
[الإصدار 3.0.0 - الإصدار 3.2.2] | 3.3.x | [17 - 23] |
[الإصدار 3.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
على الحقول الموجودة في نموذج المجال الخاص بك، أو بدلاً من ذلك كيان 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
: اسم الحقل المحدد في وحدة الكيان (لا يتم تحديده في حالة استخدام التعليق التوضيحي في وحدة الكيان). إذا لم يتم تحديد المفتاح سيكون اسم الحقل.
targetType
: نوع الكائن المُدار حسب الكيان. إذا لم يتم تحديده، فستحاول المكتبة الحصول عليه بناءً على نوع الحقل (على سبيل المثال، الحقل الصحيح بدون تعريف النوع المستهدف سيكون INTEGER
). إذا لم يكن هناك نوع متوافق مع تلك المُدارة، فستتم إدارتها كسلسلة. الأنواع المدارة:
STRING
، INTEGER
، DOUBLE
، FLOAT
، LONG
، BOOLEAN
BIGDECIMAL
، منطقي، DATE
، LOCALDATE
، LOCALDATETIME
، LOCALTIME
، OFFSETDATETIME
، OFFSETTIME
، ZONEDDATETIME
.خصائص التحقق:
datePattern
: فقط لأنواع الأهداف DATE
و LOCALDATE
و LOCALDATETIME
و LOCALTIME
و OFFSETDATETIME
و OFFSETTIME
و ZONEDDATETIME
. يحدد نمط التاريخ المطلوب استخدامه.maxSize, minSize
: الحد الأقصى/الحد الأدنى لطول القيمة.maxDigits, minDigits
: للأنواع الرقمية فقط. الحد الأقصى/الحد الأدنى لعدد الأرقام.regexPattern
: نمط regex.decimalFormat
: فقط للأنواع الرقمية العشرية. تقصير #.##
آخر:
sortable
: إذا كان خطأ، يمكن استخدام الحقل للبحث ولكن لا يمكن استخدامه للفرز. الافتراضي: صحيح.trim
: تطبيق تقليم.tags
: مفيد إذا كان حقل نموذج المجال يتوافق مع حقول كيانات متعددة (المثال متاح في الأسفل).allowedFilters
: المرشحات المسموح بها حصريًا.notAllowedFilters
: المرشحات غير المسموح بها.likeFilters
: مرشحات الإعجاب المسموح بها ( تحتوي على ، تبدأ مع ، تنتهي مع ). الافتراضي: صحيح.استمرارًا للمثال، فئات الكيانات لدينا:
@ 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 = val | 1,2 | نعم |
يتضمن | يتضمن | sql_col مثل '%val%' | 1,2 | نعم |
في | في | sql_col IN (val1، val2، ...، valN) | 1,2 | نعم |
يبدأ ب | يبدأ مع | sql_col مثل "val%" | 1,2 | نعم |
ينتهي ب | ينتهي مع | sql_col مثل '%val' | 1,2 | نعم |
أعظم من | GT | sql_col> فال | 1,2 | نعم |
أكبر من أو يساوي | gte | sql_col >= val | 1,2 | نعم |
أقل من | لتر | sql_col <فال | 1,2 | نعم |
أقل من أو يساوي | lte | sql_col <= val | 1,2 | نعم |
بين | بين | sql_col بين val1 وval2 | 1,2 | نعم |
باطل | باطل | sql_col فارغ | 1,2 | لا |
فارغ | فارغ | sql_collection_col فارغ | 1,2 | لا |
الوضع 1
وصف الخيار | مفتاح المكتبة |
---|---|
تجاهل الحالة | #أنا |
النفي | #ن |
تقليم | #ر |
يجب إلحاق مفاتيح الخيارات بالمرشح؛ على سبيل المثال ?firstName_eq#i=بياجيو أو ?firstName_eq#i#n=بياجيو
الوضع 2
وصف الخيار | مفتاح المكتبة (سمات جافا) |
---|---|
تجاهل الحالة | تجاهل حالة |
النفي | نفي |
تقليم | تقليم |
من الممكن تحديد 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 ;
}
اسم الفلتر | مفتاح | القيم الثابتة |
---|---|---|
الحد (حجم الصفحة) | حد | |
الإزاحة (رقم الصفحة) | إزاحة | |
نوع | نوع | أسك، ديسك |
الوضع 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
على الحقول الموجودة في نموذج المجال الخاص بك، أو بدلاً من ذلك كيان 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
: اسم الحقل المحدد في وحدة الكيان (لا يتم تحديده في حالة استخدام التعليق التوضيحي في وحدة الكيان). إذا لم يتم تحديد المفتاح سيكون اسم الحقل.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. إذا كنت لا تريد هذا السلوك، فاختر استخدام أساليب المستودع (الطرق ذات اللاحقة "الكلاسيكية") التي تسمح لك بتعديل العلاقات التي تريد تعديلها فقط
ملاحظة 2:
الإسقاط، بغض النظر عما إذا كنت تريده أم لا، سوف يقوم دائمًا باستخراج الحقول التي تمثل المفاتيح الأساسية للكيان (أو الكيانات ذات الصلة)
ملاحظة 3:
ترقيم الصفحات غير مدعوم
InvalidFieldException
.JPASearchException
من الممكن فرض الصلات مع الجلب للسماح للإسبات (أو إطار عمل 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 );
// ...
يمكن أن تكون الحالة الخاصة الأخرى هي حيث يمكن تكرار كائن داخل نموذج المجال لتمثيل أجزاء متعددة من الكيان. الحل للبحث:
@ 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 '