tdilber / spring-jpa-generic-criteria

Spring Jpa Dynamic Query Project

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Spring Jpa Dynamic Query (JDQ)

This project is designed to overcome the sluggishness of Spring Data JPA's query creation and the need to write separate code for each query. At its core, it simplifies the Criteria API introduced in Jpa 2, enabling programmatic or dynamic runtime query creation.

The query creation capabilities include 9 different field operators, AND-OR conjunctions, SCOPE support, and single or multi JOIN features. The SELECT, DISTINCT, ORDER BY clauses in the query are also supported with Joined Column. In addition, all query results can be returned as both List and Page. After SELECT clause, PROJECTION support is also available. This PROJECTION works with ignore missing fields rules for more flexibility.

You don't need to write any lines of code to create these queries, all operations including all JOIN operations are done dynamically at runtime. It is sufficient to create just one Repository interface. The objects required to call the methods related to dynamic query in the interface can be taken from within the program or from outside the program (like DTO) as Serializable. Since all query creation and database query operations are done with Criteria Api during these operations, it is as secure as Spring Data JPA, and works as fast and effectively as Spring Data JPA.

Note:

Introduction

This is the base Models for Jpa Dynamic Query.

public enum CriteriaOperator {
    //    String Operators
    CONTAIN, DOES_NOT_CONTAIN, END_WITH, START_WITH,
    //    Null Check Operator
    SPECIFIED,
    //    String and Comparable Operators
    EQUAL, NOT_EQUAL,
    //    Comparable Operators
    GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL,
    //    OR Operator
    OR,
    //    SCOPE Operator
    PARENTHES
}


@Getter
@Setter
public class Criteria implements Serializable {
    protected String key;
    protected CriteriaOperator operation;
    protected List<Object> values;
}

@Getter
@Setter
public class DynamicQuery implements Serializable {
    protected boolean distinct = false;
    protected Integer pageSize = null;
    protected Integer pageNumber = null;
    protected List<Pair<String, String>> select = new ArrayList<>();
    protected List<Criteria> where = new CriteriaList();
    protected List<Pair<String, Order>> orderBy = new ArrayList<>();
}

This is the base Methods for JpaDynamicQueryRepository.

@NoRepositoryBean
public interface JpaDynamicQueryRepository<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
    // List Entity with WHERE Clause (JOIN supported)
    List<T> findAll(List<Criteria> criteriaList);

    // List Entity with WHERE Clause (JOIN supported) and Pageable
    Page<T> findAll(List<Criteria> criteriaList, Pageable pageable);

    // List Entity with SELECT, DISTINCT, WHERE, ORDER BY Clause (JOIN supported) Page Supported 
    List<T> findAll(DynamicQuery dynamicQuery);

    // List Entity with SELECT, DISTINCT, WHERE, ORDER BY Clause (JOIN supported) Page Supported
    Page<T> findAllAsPage(DynamicQuery dynamicQuery);

    // List Entity with SELECT, DISTINCT, WHERE, ORDER BY Clause (JOIN supported) Page Supported With Projection
    <ResultType> List<ResultType> findAll(DynamicQuery dynamicQuery, Class<ResultType> resultTypeClass);

    // List Entity with SELECT, DISTINCT, WHERE, ORDER BY Clause (JOIN supported) Page Supported With Projection
    <ResultType> Page<ResultType> findAllAsPage(DynamicQuery dynamicQuery, Class<ResultType> resultTypeClass);
}

Writing the Code

You can find the sample code from: https://github.com/tdilber/spring-jpa-dynamic-query-presentation-demo

1- Setting up the project with Maven

<dependency>
    <groupId>io.github.tdilber</groupId>
    <artifactId>spring-jpa-dynamic-query</artifactId>
    <version>0.3.1</version>
</dependency>

2- Enable Annotation

Add the @EnableJpaDynamicQuery annotation to the main class of your project. This annotation is used to enable the dynamic query feature.

@EnableJpaDynamicQuery
@SpringBootApplication
public class SpringJpaDynamicQueryDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringJpaDynamicQueryDemoApplication.class, args);
    }
}

3- Create a Repository

Create a Repository for Existing Entity.

 public interface AdminUserRepository extends JpaDynamicQueryRepository<AdminUser, Long> {

}

4- Operator Examples

What is Criteria?

At the beginning we must understand what is Criteria. Criteria is SQL Query WHERE Clause item.

For example SELECT * FROM user WHERE id > 5 AND name like 'Ali%' AND surname = 'DILBER' AND age IN (29, 30, 31) AND status is not null

  • id > 5 is a Criteria => Criteria.of("id", CriteriaOperator.GREATER_THAN, 5)
  • name like 'Ali%' is a Criteria => Criteria.of("name", CriteriaOperator.START_WITH, "Ali")
  • surname = 'DILBER' is a Criteria => Criteria.of("name", CriteriaOperator.EQUAL, "DILBER")
  • age IN (29, 30, 31) is a Criteria => Criteria.of("age", CriteriaOperator.EQUAL, 29, 30, 31)
  • status is not null is a Criteria => Criteria.of("age", CriteriaOperator.SPECIFIED, true)

this is it :)

Multi Value Supported Criteria Operators

Some operators have multi value support. This means that you can use multiple values for the same field.

  • Multi Value support is available for the following operators: EQUAL, NOT_EQUAL, CONTAIN, DOES_NOT_CONTAIN, START_WITH, END_WITH
  • Multi Value not supported for the following operators: SPECIFIED, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL

Note: As you know Sql Where Clause Some operators have multi value input, for example IN, NOT IN. We develop more multi value operators with java code touches.

Comparable Operators

This operator is used to compare numbers and dates. Available Java Types are Date, Double, Long, LocalDate, ZonedDateTime, Instant, Integer.

The following operators are available:

EQUAL, NOT_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL

Enums supported for EQUAL, NOT_EQUAL operators.

userRepository.findAll(CriteriaList.of(Criteria.of("status", CriteriaOperator.EQUAL, User.Status.ACTIVE)));
customerRepository.findAll(CriteriaList.of(Criteria.of("age", CriteriaOperator.NOT_EQUAL, 23,24,25)));

Hibernate Query:

-- CriteriaOperator.EQUAL => User.Status.ACTIVE
select user0_.id        as id1_1_,
       user0_.age       as age2_1_,
       user0_.birthdate as birthdat3_1_,
       user0_.name      as name4_1_,
       user0_.status    as status5_1_,
       user0_.surname   as surname6_1_,
       user0_.type      as type7_1_
from test_user user0_
where user0_.status = ?

-- Multi Value CriteriaOperator.NOT_EQUAL => 23, 24, 25
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where customer0_.age <> 23
  and customer0_.age <> 24
  and customer0_.age <> 25

String Operators

This operator is used to compare strings. The following operators are available:

EQUAL, NOT_EQUAL, CONTAIN, DOES_NOT_CONTAIN, END_WITH, START_WITH

customerRepository.findAll(CriteriaList.of(Criteria.of("name", CriteriaOperator.CONTAIN, "Customer")));

Hibernate Query:

select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where customer0_.name like ?

Multi Value Support Examples:

customerRepository.findAll(CriteriaList.of(Criteria.of("name", CriteriaOperator.DOES_NOT_CONTAIN, "5","4")));
customerRepository.findAll(CriteriaList.of(Criteria.of("name", CriteriaOperator.START_WITH, "Customer 3","Customer 4")));

Hibernate Query:

-- Multi Value CriteriaOperator.DOES_NOT_CONTAIN
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where (customer0_.name not like ?)
  and (customer0_.name not like ?)

-- Multi Value CriteriaOperator.START_WITH
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where customer0_.name like ?
   or customer0_.name like ?

Null Check Operator

This operator is used to check if the field is null or not. The following operators are available: SPECIFIED

customerRepository.findAll(CriteriaList.of(Criteria.of("name", CriteriaOperator.SPECIFIED, true)));
customerRepository.findAll(CriteriaList.of(Criteria.of("name", CriteriaOperator.SPECIFIED, false)));

Hibernate Query:

-- CriteriaOperator.SPECIFIED true
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where customer0_.name is not null

-- CriteriaOperator.SPECIFIED false
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where customer0_.name is null

5- AND-OR Operator Examples

Sequentially, all criteria are evaluated with the AND operator. If you want to evaluate the criteria with the OR operator, you can use the Criteria.OR() method.

customerRepository.findAll(CriteriaList.of(
        Criteria.of("name", CriteriaOperator.EQUAL, "Customer 1"), 
        Criteria.OR(),
        Criteria.of("name", CriteriaOperator.EQUAL, "Customer 2")));

customerRepository.findAll(CriteriaList.of(
        Criteria.of("age", CriteriaOperator.EQUAL, 23, 24),
                        Criteria.of("age", CriteriaOperator.NOT_EQUAL, 20, 21),
                        Criteria.OR(), // ( [ (23 or 24) AND (not 20 and not 21) ] "OR" [ (not 24) AND (25 or 26) ])
                        Criteria.of("age", CriteriaOperator.NOT_EQUAL, 24),
                        Criteria.of("age", CriteriaOperator.EQUAL, 25, 26)));

Hibernate Query:

-- Criteria.OR() First Example
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where customer0_.name = ?
   or customer0_.name = ?

-- Criteria.OR() Second Example
select customer0_.id        as id1_0_,
       customer0_.age       as age2_0_,
       customer0_.birthdate as birthdat3_0_,
       customer0_.name      as name4_0_,
       customer0_.user_id   as user_id5_0_
from customer customer0_
where (customer0_.age = 23 or customer0_.age = 24) and customer0_.age <> 20 and customer0_.age <> 21
   or customer0_.age <> 24 and (customer0_.age = 25 or customer0_.age = 26)

6- SCOPE Operator Examples

Just And-Or operators are not enough for complex queries. For Example: you cannot this simple query with just AND-OR operators: (A OR B) AND (C OR D). For this reason, the CriteriaOperator.PARENTHES operator is used to create a scope. You can use PARENTHES in PARENTHES.

    //     (A OR B) AND (C OR D)
var criteriaList = CriteriaList.of(
        Criteria.of("", CriteriaOperator.PARENTHES,
                CriteriaList.of(Criteria.of(Course.Fields.id, CriteriaOperator.EQUAL, 1),
                        Criteria.OR(),
                        Criteria.of(Course.Fields.id, CriteriaOperator.EQUAL, 2))),
        Criteria.of("", CriteriaOperator.PARENTHES,
                CriteriaList.of(Criteria.of(Course.Fields.id, CriteriaOperator.EQUAL, 2),
                        Criteria.OR(),
                        Criteria.of(Course.Fields.id, CriteriaOperator.EQUAL, 3)))
);
List<Course> courseList = courseRepository.findAll(criteriaList);

Hibernate Query:

select course0_.id                as id1_3_,
       course0_.active            as active2_3_,
       course0_.description       as descript3_3_,
       course0_.max_student_count as max_stud4_3_,
       course0_.name              as name5_3_,
       course0_.start_date        as start_da6_3_
from course course0_
where (course0_.id = 1 or course0_.id = 2)
  and (course0_.id = 2 or course0_.id = 3)

7- JOIN Examples

The strongest feature of this project is JOIN. Joins work dynamically. If you use it, it automatically performs a JOIN. It understands which columns to match between two tables through the Join Annotations you specify in the entity ( ManyToMany, OneToMany, OneToOne, ManyToOne).

Inner join and Left join are supported. Although our system supports Right join, it is not supported because Hibernate does not support it.

When performing a join, the field names inside the entity object are used. If you put a dot (.) after the field name, it performs an inner join, if you put a less than sign (<), it performs a left join. Then you need to write the field name of the object you joined.

Also you can use multiple join in the same query.

Also you can use (multiple) join in select clause and order by clause.

// Inner Join
var criteriaList = CriteriaList.of(
        Criteria.of("department.name", CriteriaOperator.START_WITH, "P"),
        Criteria.of("name", CriteriaOperator.START_WITH, "Robert")
);
List<Student> students = studentRepository.findAll(criteriaList);


// Left Join
var criteriaList = CriteriaList.of(
        Criteria.of("department<id", CriteriaOperator.SPECIFIED, false),
        Criteria.of("id", CriteriaOperator.GREATER_THAN, 3)
);
List<Student> students = studentRepository.findAll(criteriaList);

// Multi Inner Join
var criteriaList = CriteriaList.of(Criteria.of("roles.roleAuthorizations.authorization.menuIcon", CriteriaOperator.START_WITH, "icon"));
List<AdminUser> courseList = adminUserRepository.findAll(criteriaList);

// Multi Left Join
var criteriaList = CriteriaList.of(Criteria.of("roles<roleAuthorizations<authorization<menuIcon", CriteriaOperator.START_WITH, "icon"));
List<AdminUser> courseList = adminUserRepository.findAll(criteriaList);

Hibernate Query:

-- Inner Join
select student0_.id            as id1_8_,
       student0_.address_id    as address_3_8_,
       student0_.department_id as departme4_8_,
       student0_.name          as name2_8_
from student student0_
         inner join department department1_ on student0_.department_id = department1_.id
where (department1_.name like ?)
  and (student0_.name like ?)

-- Left Join
select student0_.id            as id1_8_,
       student0_.address_id    as address_3_8_,
       student0_.department_id as departme4_8_,
       student0_.name          as name2_8_
from student student0_
         left outer join department department1_ on student0_.department_id = department1_.id
where (department1_.id is null)
  and student0_.id > 3

-- Multi Inner Join
select adminuser0_.id as id1_1_, adminuser0_.password as password2_1_, adminuser0_.username as username3_1_
from admin_user adminuser0_
         inner join admin_user_role roles1_ on adminuser0_.id = roles1_.admin_user_id
         inner join role role2_ on roles1_.role_id = role2_.id
         inner join role_authorization roleauthor3_ on role2_.id = roleauthor3_.role_id
         inner join my_authorization authorizat4_ on roleauthor3_.authorization_id = authorizat4_.id
where authorizat4_.menu_icon like ?

-- Multi Left Join
select adminuser0_.id as id1_1_, adminuser0_.password as password2_1_, adminuser0_.username as username3_1_
from admin_user adminuser0_
         left outer join admin_user_role roles1_ on adminuser0_.id = roles1_.admin_user_id
         left outer join role role2_ on roles1_.role_id = role2_.id
         left outer join role_authorization roleauthor3_ on role2_.id = roleauthor3_.role_id
         left outer join my_authorization authorizat4_ on roleauthor3_.authorization_id = authorizat4_.id
where authorizat4_.menu_icon like ?

8- Projection Examples

Spring Data projections always boring. But this project projection is very simple. When you want to use specific fields in the result, you can add selected fields on select list on DynamicQuery object. You can add multiple fields to the select clause. You can also use the Pair class to give an alias to the field.

Why we are using Pair class? Because we want to use the same field name in the select clause. But we want to use different field names in the result. For example, we want to use id in the select clause, but we want to use adminId in the result.

When you are using this select clause, queries are created with the select clause. For this reason, your query will be faster and more efficient.

This method also support joined column pass value to result object. Like this following example:

DynamicQuery dynamicQuery = new DynamicQuery();
dynamicQuery.getSelect().add(Pair.of("id", "adminId"));
dynamicQuery.getSelect().add(Pair.of("username", "adminUsername"));
dynamicQuery.getSelect().add(Pair.of("roles.id", "roleId"));
dynamicQuery.getSelect().add(Pair.of("roles.name", "roleName"));
dynamicQuery.getSelect().add(Pair.of("roles.roleAuthorizations.authorization.id", "authorizationId"));
dynamicQuery.getSelect().add(Pair.of("roles.roleAuthorizations.authorization.name", "authorizationName"));
dynamicQuery.getSelect().add(Pair.of("roles.roleAuthorizations.authorization.menuIcon", "menuIcon"));
var criteriaList = CriteriaList.of(Criteria.of("roles.roleAuthorizations.authorization.menuIcon", CriteriaOperator.START_WITH, "icon"));
dynamicQuery.getWhere().addAll(criteriaList);
List<AuthorizationSummary> result = adminUserRepository.findAll(dynamicQuery, AuthorizationSummary.class);

Hibernate Query:

select adminuser0_.id         as col_0_0_,
       adminuser0_.username   as col_1_0_,
       role2_.id              as col_2_0_,
       role2_.name            as col_3_0_,
       authorizat4_.id        as col_4_0_,
       authorizat4_.name      as col_5_0_,
       authorizat4_.menu_icon as col_6_0_
from admin_user adminuser0_
         inner join admin_user_role roles1_ on adminuser0_.id = roles1_.admin_user_id
         inner join role role2_ on roles1_.role_id = role2_.id
         inner join role_authorization roleauthor3_ on role2_.id = roleauthor3_.role_id
         inner join my_authorization authorizat4_ on roleauthor3_.authorization_id = authorizat4_.id
where authorizat4_.menu_icon like ?

Note: you can find the example on demo github repository.

9- Pagination Examples

You can find all pagination methods in the JpaDynamicQueryRepository interface. You can use the findAllAsPage method to get the result as a page.

DynamicQuery dynamicQuery = new DynamicQuery();
dynamicQuery.setWhere(CriteriaList.of(Criteria.of(Course.Fields.id, CriteriaOperator.GREATER_THAN, 3)));
dynamicQuery.setPageSize(2);
dynamicQuery.setPageNumber(1);

Page<Course> result = courseRepository.findAllAsPage(dynamicQuery);

Note: you can find the example on demo github repository.

10- Query Builder Examples

When you want to use Dynamic Query on programmatic way, you can use Query Builder. Query Builder is a fluent API. You can use it to create a dynamic query. I inspired from QueryDSL project but it is just easy to use DTO create builder for DynamicQuery object.

Page<AuthorizationSummary> result = adminUserRepository.queryBuilder()
        .select(Select("id", "adminId"),
                Select("username", "adminUsername"),
                Select("roles.id", "roleId"),
                Select("roles.name", "roleName"),
                Select("roles.roleAuthorizations.authorization.id", "authorizationId"),
                Select("roles.roleAuthorizations.authorization.name", "authorizationName"),
                Select("roles.roleAuthorizations.authorization.menuIcon", "menuIcon"))
        .distinct(false)
        .where(Field("roles.roleAuthorizations.authorization.menuIcon").startWith("icon"), Parantesis(Field("id").eq(3), OR, Field("roles.id").eq(4), OR, Field("id").eq(5)), Parantesis(Field("id").eq(5), OR, Field("id").eq(4), OR, Field("roles.id").eq(3)))
        .orderBy(OrderBy("roles.id", Order.DESC))
        .page(1, 2)
        .getResultAsPage(AuthorizationSummary.class);

Note: you can find the example on demo github repository.

11- Argument Resolver Examples

Argument resolvers are used to automatically create dynamic queries from the request parameters. You can use the @EnableJpaDynamicQueryArgumentResolvers annotation to enable this feature.

Your controller methods can take CriteriaList and DynamicQuery objects as parameters.

@GetMapping("/course")
public ResponseEntity<List<Course>> getCourseWithCriteria(CriteriaList criteriaList) {
    List<Course> customerList = courseRepository.findAll(criteriaList);
    return ResponseEntity.ok().body(customerList);
}

@GetMapping("/course/as-list")
public ResponseEntity<List<AuthorizationSummary>> getCourseWithDynamicQueryAsList(DynamicQuery dynamicQuery) {
    List<AuthorizationSummary> customerList = adminUserRepository.findAll(dynamicQuery, AuthorizationSummary.class);
    return ResponseEntity.ok().body(customerList);
}

Test the API with the following request:

printRequestedResult(COURSE_CRITERIA_API_URL, "key0=name&operation0=CONTAIN&values0=Calculus",Course[].class);

printRequestedResult(COURSE_SEARCH_LIST_API_URL,
            "select0=id&select1=username&select2=roles.id&select3=roles.name&select4=roles.roleAuthorizations.authorization.id&select5=roles.roleAuthorizations.authorization.name&select6=roles.roleAuthorizations.authorization.menuIcon&"+
                    "selectAs0=adminId&selectAs1=adminUsername&selectAs2=roleId&selectAs3=roleName&selectAs4=authorizationId&selectAs5=authorizationName&selectAs6=menuIcon&"+
                    "orderBy0=roles.id&orderByDirection0=desc&"+
                    "page=1&"+
                    "pageSize=2&"+
                    "key0=roles.roleAuthorizations.authorization.menuIcon&operation0=START_WITH&values0=icon",S9_Query_Builder.AuthorizationSummary[].class);

Note: you can find the example on demo github repository.

-- COURSE_CRITERIA_API_URL Example
select course0_.id                as id1_3_,
       course0_.active            as active2_3_,
       course0_.description       as descript3_3_,
       course0_.max_student_count as max_stud4_3_,
       course0_.name              as name5_3_,
       course0_.start_date        as start_da6_3_
from course course0_
where course0_.name like ?

-- COURSE_SEARCH_LIST_API_URL Example
select adminuser0_.id         as col_0_0_,
       adminuser0_.username   as col_1_0_,
       role2_.id              as col_2_0_,
       role2_.name            as col_3_0_,
       authorizat4_.id        as col_4_0_,
       authorizat4_.name      as col_5_0_,
       authorizat4_.menu_icon as col_6_0_
from admin_user adminuser0_
         inner join admin_user_role roles1_ on adminuser0_.id = roles1_.admin_user_id
         inner join role role2_ on roles1_.role_id = role2_.id
         inner join role_authorization roleauthor3_ on role2_.id = roleauthor3_.role_id
         inner join my_authorization authorizat4_ on roleauthor3_.authorization_id = authorizat4_.id
where authorizat4_.menu_icon like ?
order by role2_.id desc limit ?
offset ?

12- Custom Converter

When you start using this library, there will be things that will really trouble you. But don't worry, we have a solution for you.

Firstly, BasicDeserializer is our default deserializer.

When you want to change Date object deserialization, You can create a custom deserializer and override the deserialize method. Filter the class what you want to change the deserialization process. After that, pass the other object types to the default deserializer.

@Component
public class DateTimeDeserializer extends BasicDeserializer {

    @Override
    public <T> T deserialize(Object value, Class<T> clazz) throws Exception {
        if (clazz.isAssignableFrom(java.util.Date.class)) { // only Date class changing to deserialization
            if (value instanceof Date date) {
                return (T) date;
            }
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            simpleDateFormat.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
            return (T) simpleDateFormat.parse(value.toString()); // Date deserialization complete
        }

        return super.deserialize(value, clazz); // other deserialization is supported from our project default deserializer
    }
}

13- Custom Entity Manager Provider

14- Additional Features

More Potential(Future) Features

  • Policy Management: For ensure security, we can add policy management for each query. For example, we can add a policy for each query. If the user does not have permission to access the query, we can throw an exception.

Performance

Security

Is Production Ready?

This project is production ready. But you must be careful when you are using this project. Because if you are using Argument Resolvers on customer api, then bad people can access unauthorized data using with select and join features. This project is fully secured for sql injection but careful to unauthorized queries. You can use programatic queries safely with QueryBuilder or DynamicQuery object.

You can easily use this project on your backoffice or admin panel. But you must be careful when you are using this project on customer api. Don't miss the use Spring Security or JWT token or another security mechanism for secure your api.

Not: Don't worry when you are using Argument Resolver feature if your entities don't have @ManyToMany, @OneToMany, @OneToOne, @ManyToOne annotations. Because this project is not support join without these annotations. Just check unauthorized queries with OR and/or PARENTHES operators.

License

Apache License 2.0

Conclusion

In conclusion, this project is designed to overcome the sluggishness of Spring Data JPA's query creation and the need to write separate code for each query. It simplifies the Criteria API, enabling programmatic or dynamic runtime query creation. No operation cancels the previous operation. You can use many different operations together such as Operators with Join, Join with Or-Scope, Or and Scope with Projection, Projection with OrderBy, OrderBy with Pageable. You can use all of these with Query Builder or dto with Argument Resolver or creating new DynamicQuery object. You can create a custom deserializer for avoiding bad times. Finally, I want to say this: They say Java is a cumbersome language, but if you design and use Java in a cumbersome way, it becomes cumbersome. In this project, I tried to eliminate the clumsiness of Spring Data JPA, which is cumbersome. I hope I have been successful. Good work.

About

Spring Jpa Dynamic Query Project

License:Apache License 2.0


Languages

Language:Java 100.0%