[JAVA] Getting Started with Doma-Using Logical Operators such as AND and OR in the WHERE Clause of the Criteria API

Introduction

When constructing a SQL WHERE clause, you need to concatenate with logical operators such as AND and OR to specify multiple conditions. This article shows how such logical operator concatenation can be represented by the Criteria API in Doma 2.43.0.

Concatenation of logical operators is a simple function to express in SQL, but how to realize it as a programming language is a problem to be considered. It is characteristic that Doma can be expressed using a lambda expression.

For an overview of Doma and the Criteria API, read the other articles in Introduction to Doma.

Sample code used in this article

The database has only one employee table that represents employees.

schema.sql


create table employee (
    id integer not null primary key,
    name varchar(255) not null,
    age integer not null, 
    version integer not null);

Prepare the Employee class as the entity class corresponding to the employee table.

Employee.java


@Entity(metamodel = @Metamodel)
public class Employee {
  @Id
  public Integer id;
  public String name;
  public Integer age;
  @Version public Integer version;
}

We will prepare ʻEmployeeRepositoryand rewrite theselect` method of this class to show some examples.

public class EmployeeRepository {

  private final Entityql entityql;

  public EmployeeRepository(Config config) {
    this.entityql = new Entityql(config);
  }

  public List<Employee> select() {
    //Here is a code example rewritten in various ways
  }
}

Implicit AND logical operator

If you list the search conditions (ʻeq (..., ...) `in this example), they will be implicitly concatenated by AND.

    Employee_ e = new Employee_();
    return entityql.from(e).where(c -> {
      c.eq(e.id, 1);
      c.eq(e.name, "aaa");
      c.eq(e.age, 20);
    }).fetch();

The generated SQL looks like this: (Hereafter, the SQL with the bound value embedded is shown, but in reality, the SQL using the bind variable ? Is issued.)

select t0_.id, t0_.name, t0_.age, t0_.version from Employee t0_ where t0_.id = 1 and t0_.name = 'aaa' and t0_.age = 20

Explicit AND logical operator

In the previous example, the method corresponding to AND was not called, but it is also possible to explicitly call the method ʻand corresponding to AND as follows. The ʻand method takes a lambda expression.

    Employee_ e = new Employee_();
    return entityql.from(e).where(c -> {
      c.eq(e.id, 1);
      c.and(() -> c.eq(e.name, "aaa"));
      c.and(() -> c.eq(e.age, 20));
    }).fetch();

The generated SQL will be as follows, and the same SQL as in the previous example will be generated (however, there are differences such as the presence or absence of parentheses).

select t0_.id, t0_.name, t0_.age, t0_.version from Employee t0_ where t0_.id = 1 and (t0_.name = 'aaa') and (t0_.age = 20)

OR logical operator

To concatenate with an OR, call the ʻor method as follows: The ʻor method takes a lambda expression.

    Employee_ e = new Employee_();
    return entityql.from(e).where(c -> {
      c.eq(e.id, 1);
      c.or(() -> {
        c.eq(e.name, "aaa");
        c.eq(e.age, 20);
      });
    }).fetch();

The generated SQL looks like this:

select t0_.id, t0_.name, t0_.age, t0_.version from Employee t0_ where t0_.id = 1 or (t0_.name = 'aaa' and t0_.age = 20)

Automatic removal function of leading AND and OR

Similar to the previous example, but what kind of SQL does the following code generate?

    Employee_ e = new Employee_();
    return entityql.from(e).where(c -> {
      c.or(() -> {
        c.eq(e.name, "aaa");
        c.eq(e.age, 20);
      });
      c.eq(e.id, 1);
    }).fetch();

You might think that an extra OR is generated immediately after the WHERE clause, but Doma automatically removes the AND and OR at the beginning of the WHERE clause, so the following SQL is generated.

select t0_.id, t0_.name, t0_.age, t0_.version from Employee t0_ where (t0_.name = 'aaa' and t0_.age = 20) and t0_.id = 1

This function is useful when the necessity of AND or OR is not statically determined due to a conditional branch in the WHERE clause.

    Employee_ e = new Employee_();
    return entityql.from(e).where(c -> {
      if (Some condition) {
        c.or(() -> {
          c.eq(e.name, "aaa");
          c.eq(e.age, 20);
        });
      }
      if (Some condition 2) {
        c.or(() -> {
          c.eq(e.name, "bbb");
          c.eq(e.age, 30);
        });
      }
    }).fetch();

Nesting AND and OR

You can nest any number of ʻand and ʻor without any restrictions.

    Employee_ e = new Employee_();
    return entityql.from(e).where(c -> {
      c.eq(e.id, 1);
      c.or(() -> {
        c.eq(e.name, "aaa");
        c.eq(e.age, 20);
        c.or(() -> {
          c.eq(e.name, "bbb");
          c.eq(e.age, 30);
        });
      });
    }).fetch();

The above code will generate the following SQL:

select t0_.id, t0_.name, t0_.age, t0_.version from Employee t0_ where t0_.id = 1 or (t0_.name = 'aaa' and t0_.age = 20 or (t0_.name = 'bbb' and t0_.age = 30))

in conclusion

We have shown how logical operator concatenation can be represented in Doma's Criteria API.

As mentioned at the beginning, Doma's Criteira API expresses the concatenation of logical operators as a lambda expression. There is a method chain as another expression method, but I personally think that the method chain has a weakness. It is vulnerable to conditional branching. If there is a conditional branch, the chain will break and the complexity will increase at once. This is because you need to store the object to be chained in a variable in order to connect the chain. On the other hand, the method using a lambda expression such as Doma's Criteria API is simple and highly readable because there is no difference in writing style depending on whether conditional branching is included or not (just because it is surrounded by if blocks). I think.

Recommended Posts

Getting Started with Doma-Using Logical Operators such as AND and OR in the WHERE Clause of the Criteria API
Getting Started with Doma-Using Subqueries with the Criteria API
Getting Started with Doma-Dynamicly construct WHERE clauses with the Criteria API
Getting Started with Doma-Using Projection with the Criteira API
Getting Started with Doma-Using Joins with the Criteira API
Getting Started with Doma-Introduction to the Criteria API
Getting Started with Reactive Streams and the JDK 9 Flow API
[jOOQ] How to CASE WHEN in the WHERE / AND / OR clause
Summarize the main points of getting started with JPA learned with Hibernate
Let's consider the meaning of "stream" and "collect" in Java's Stream API.
[Firestore] Extract the collection with where condition in Ruby and delete the record