From cb20f74b4f4eba1693e247cd4dae2980608faf65 Mon Sep 17 00:00:00 2001 From: Toshihiro Nakamura Date: Tue, 29 Oct 2024 20:39:56 +0900 Subject: [PATCH 1/2] Add query-dsl.rst --- docs/criteria-api.rst | 5 + docs/index.rst | 1 + docs/query-dsl.rst | 1967 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1973 insertions(+) create mode 100644 docs/query-dsl.rst diff --git a/docs/criteria-api.rst b/docs/criteria-api.rst index 6395a164a..f6eb56721 100644 --- a/docs/criteria-api.rst +++ b/docs/criteria-api.rst @@ -8,6 +8,11 @@ Criteria API Introduction ============ +.. warning:: + + Please use the :doc:`query-dsl` instead of the Entityql DSL or NativeSql DSL described on this page. + The Query DSL integrates both the Entityql DSL and NativeSql DSL. + .. note:: In Kotlin environment, use Kotlin specific DSLs instead of the following DSLs. diff --git a/docs/index.rst b/docs/index.rst index 42cbe8374..76853c8c2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -52,6 +52,7 @@ User Documentation query/index query-builder/index criteria-api + query-dsl sql expression transaction diff --git a/docs/query-dsl.rst b/docs/query-dsl.rst new file mode 100644 index 000000000..bf43fdeb3 --- /dev/null +++ b/docs/query-dsl.rst @@ -0,0 +1,1967 @@ +============ +Query DSL +============ + +.. contents:: + :depth: 4 + +Introduction +============ + +We use the following Entity classes to show you some examples: + +.. code-block:: java + + @Entity(metamodel = @Metamodel) + public class Employee { + + @Id private Integer employeeId; + private Integer employeeNo; + private String employeeName; + private Integer managerId; + private LocalDate hiredate; + private Salary salary; + private Integer departmentId; + private Integer addressId; + @Version private Integer version; + @OriginalStates private Employee states; + @Transient private Department department; + @Transient private Employee manager; + @Transient private Address address; + + // getters and setters + } + +.. code-block:: java + + @Entity(metamodel = @Metamodel) + public class Department { + + @Id private Integer departmentId; + private Integer departmentNo; + private String departmentName; + private String location; + @Version private Integer version; + @OriginalStates private Department originalStates; + @Transient private List employeeList = new ArrayList<>(); + + // getters and setters + } + +.. code-block:: java + + @Entity(immutable = true, metamodel = @Metamodel) + @Table(name = "EMPLOYEE") + public class Emp { + + @Id private final Integer employeeId; + private final Integer employeeNo; + private final String employeeName; + private final Integer managerId; + private final LocalDate hiredate; + private final Salary salary; + private final Integer departmentId; + private final Integer addressId; + @Version private final Integer version; + @Transient private final Dept department; + @Transient private final Emp manager; + + // constructor and getters + } + +.. code-block:: java + + @Entity(immutable = true, metamodel = @Metamodel) + @Table(name = "DEPARTMENT") + public class Dept { + + @Id private final Integer departmentId; + private final Integer departmentNo; + private final String departmentName; + private final String location; + @Version private final Integer version; + + // constructor and getters + } + +Note that the above classes are annotated with ``@Entity(metamodel = @Metamodel)``. +The ``metamodel = @Metamodel`` indicates that the annotated entity +has a corresponding metamodel class generated by Doma's annotation processor . + +In our examples, the metamodel classes are ``Employee_``, ``Department_``, ``Emp_`` and ``Dept_``. +These metamodels allow you to make your query typesafe. + +You can customize the name of the metamodels by the Metamodel annotation elements. + +To customize all metamodels in bulk, you can use the annotation processor options. +See :doc:`annotation-processing` and check the following options: + +* doma.metamodel.enabled +* doma.metamodel.prefix +* doma.metamodel.suffix + +Query DSL +------------ + +The Query DSL can query and associate entities. +The entry point is the ``org.seasar.doma.jdbc.criteria.QueryDsl`` class. +This class has the following methods: + +* from +* insert +* delete +* update + +You can instantiate the ``QueryDsl`` class as follows: + +.. code-block:: java + + QueryDsl queryDsl = new QueryDsl(config); + +For example, to query ``Employee`` and ``Department`` entities and associate them, write as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, + t0_.HIREDATE, t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION, + t1_.DEPARTMENT_ID, t1_.DEPARTMENT_NO, t1_.DEPARTMENT_NAME, t1_.LOCATION, t1_.VERSION + from EMPLOYEE t0_ inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + where t1_.DEPARTMENT_NAME = ? + +.. note:: + + In Kotlin environment, use ``org.seasar.doma.kotlin.jdbc.criteria.KQueryDsl`` instead of ``QueryDsl``. + ``KQueryDsl`` is included in the doma-kotlin module. + +Select statement +================ + +Select settings +------------------------------------- + +We support the following settings: + +* allowEmptyWhere +* comment +* fetchSize +* maxRows +* queryTimeout +* sqlLogType + +They are all optional. +You can apply them as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = queryDsl.from(e, settings -> { + settings.setAllowEmptyWhere(false); + settings.setComment("all employees"); + settings.setFetchSize(100); + settings.setMaxRows(100); + settings.setSqlLogType(SqlLogType.RAW); + settings.setQueryTimeout(1000); + }).fetch(); + +Fetching +------------------------------ + +The Query DSL supports the following methods to fetch data from a database: + +* fetch +* fetchOne +* fetchOptional +* stream + +.. code-block:: java + + Employee_ e = new Employee_(); + + // The fetch method returns results as a list. + List list = + queryDsl.from(e).fetch(); + + // The fetchOne method returns a single result. The result may be null. + Employee employee = + queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).fetchOne(); + + // The fetchOptional method returns a single result as an optional object. + Optional optional = + queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).fetchOptional(); + + // The stream method returns results as a stream. + // The following code is equivalent to "queryDsl.from(e).fetch().stream()" + Stream stream = + queryDsl.from(e).stream(); + +Streaming +--------------------- + +The Query DSL supports the following methods: + +* mapStream +* collect +* openStream + +.. code-block:: java + + Employee_ e = new Employee_(); + + // The mapStream method handles a stream. + Map> map = + queryDsl + .from(e) + .mapStream(stream -> stream.collect(groupingBy(Employee::getDepartmentId))); + + // The collect method is a shortcut of the mapStream method. + // The following code does the same thing with the above. + Map> map2 = + queryDsl.from(e).collect(groupingBy(Employee::getDepartmentId)); + + // The openStream method returns a stream. + // You MUST close the stream explicitly. + try (Stream stream = queryDsl.from(e).openStream()) { + stream.forEach(employee -> { + // do something + }); + } + +These methods handle the stream that wraps a JDBC ResultSet. +So they are useful to process a large ResultSet effectively. + +Select expression +----------------------------- + +Entity selection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, the result entity type is the same as the one specified at the ``from`` method. +See the following code: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, + t0_.HIREDATE, t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + +To choose a joined entity type as the result entity type, +call the ``project`` or ``select`` method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .project(d) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t1_.DEPARTMENT_ID, t1_.DEPARTMENT_NO, t1_.DEPARTMENT_NAME, t1_.LOCATION, t1_.VERSION + from EMPLOYEE t0_ + inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + +.. note:: + + The ``project`` method removes duplicate entities from the results, + whereas the ``select`` method does not. + If you call neither method, duplicate entities are removed from the results by default. + +Multiple entity selection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify multiple entity types and fetch them as a tuple as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List> list = + queryDsl + .from(d) + .leftJoin(e, on -> on.eq(d.departmentId, e.departmentId)) + .where(c -> c.eq(d.departmentId, 4)) + .select(d, e) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, + t0_.VERSION, t1_.EMPLOYEE_ID, t1_.EMPLOYEE_NO, t1_.EMPLOYEE_NAME, t1_.MANAGER_ID, + t1_.HIREDATE, t1_.SALARY, t1_.DEPARTMENT_ID, t1_.ADDRESS_ID, t1_.VERSION + from DEPARTMENT t0_ left outer join EMPLOYEE t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + where t0_.DEPARTMENT_ID = ? + +The entity included in the tuple may be null when the all properties of the entity are null. + +.. note:: + + The ``select`` method does not remove duplicate entities from the results. + +Column projection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To project columns, use the ``select`` method: + +To project one column, pass one property to the select method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = queryDsl.from(e).select(e.employeeName).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_NAME from EMPLOYEE t0_ + +To project two or more columns, pass two or more properties to the select method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List> list = + queryDsl.from(e).select(e.employeeName, e.employeeNo).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_NAME, t0_.EMPLOYEE_NO from EMPLOYEE t0_ + +Up to 9 numbers, the column results are held by ``Tuple2`` to ``Tuple9``. +For more than 9 numbers, the results are held by ``Row``. + +You can get a ``Row`` list explicitly by using ``selectAsRow`` as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = + queryDsl.from(e).selectAsRow(e.employeeName, e.employeeNo).fetch(); + +Column projection and mapping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To project columns and map them to an entity, use the ``projectTo`` or ``selectTo`` method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = queryDsl.from(e).selectTo(e, e.employeeName).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NAME from EMPLOYEE t0_ + +Note that the select clause of the above SQL statement contains the primary key "EMPLOYEE_ID". +The ``projectTo`` and ``selectTo`` methods always include the id properties of the entity, even if you don't specify them. + +.. note:: + + The ``projectTo`` method removes duplicate entity ids from the results, + whereas the ``selectTo`` method does not. + +.. _query_dsl_where: + +Where expression +-------------------------------------- + +We support the following operators and predicates: + +* eq - (=) +* ne - (<>) +* ge - (>=) +* gt - (>) +* le - (<=) +* lt - (<) +* isNull - (is null) +* isNotNull - (is not null) +* like +* notLike - (not like) +* between +* in +* notIn - (not in) +* exists +* notExists - (not exists) + +.. note:: + + If the right hand operand is ``null``, the WHERE or the HAVING clause doesn't include the operator. + See WhereDeclaration_ and HavingDeclaration_ javadoc for more details. + +.. _WhereDeclaration: https://www.javadoc.io/doc/org.seasar.doma/doma-core/latest/org/seasar/doma/jdbc/criteria/declaration/WhereDeclaration.html +.. _HavingDeclaration: https://www.javadoc.io/doc/org.seasar.doma/doma-core/latest/org/seasar/doma/jdbc/criteria/declaration/HavingDeclaration.html + +We also support the following utility operators: + +* eqOrIsNull - ("=" or "is null") +* neOrIsNotNull - ("<>" or "is not null") + +We also support the following logical operators: + +* and +* or +* not + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = + queryDsl + .from(e) + .where( + c -> { + c.eq(e.departmentId, 2); + c.isNotNull(e.managerId); + c.or( + () -> { + c.gt(e.salary, new Salary("1000")); + c.lt(e.salary, new Salary("2000")); + }); + }) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + where t0_.DEPARTMENT_ID = ? and t0_.MANAGER_ID is not null or (t0_.SALARY > ? and t0_.SALARY < ?) + +You can write a subquery as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + Employee_ e2 = new Employee_(); + + List list = + queryDsl + .from(e) + .where(c -> c.in(e.employeeId, c.from(e2).select(e2.managerId))) + .orderBy(c -> c.asc(e.employeeId)) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + where t0_.EMPLOYEE_ID in (select t1_.MANAGER_ID from EMPLOYEE t1_) + order by t0_.EMPLOYEE_ID asc + +Dynamic where expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A where expression uses only evaluated operators to build a WHERE clause. + +When every operators are not evaluated in a where expression, +the built statement doesn't have any WHERE clause. + +As well as, when every operators are not evaluated in a logical operator expression, +the built statement doesn't have the logical operator expression. + +For example, suppose that a where expression contains a conditional expression as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = + queryDsl + .from(e) + .where( + c -> { + c.eq(e.departmentId, 1); + if (enableNameCondition) { + c.like(e.employeeName, name); + } + }) + .fetch(); + +In the case that the ``enableNameCondition`` variable is ``false``, the ``like`` expression is ignored. +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ where t0_.DEPARTMENT_ID = ? + +Join expression +--------------- + +We support the following expressions: + +- innerJoin - (inner join) +- leftJoin - (left outer join) + +innerJoin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl.from(e).innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + +leftJoin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl.from(e).leftJoin(d, on -> on.eq(e.departmentId, d.departmentId)).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + left outer join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + +.. _query_dsl_associate: + +association +~~~~~~~~~~~~~~~~~~~~~~ + +You can associate entities with the ``associate`` operation. +You have to use the ``associate`` operation with join expression. + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, + t0_.HIREDATE, t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION, + t1_.DEPARTMENT_ID, t1_.DEPARTMENT_NO, t1_.DEPARTMENT_NAME, t1_.LOCATION, t1_.VERSION + from EMPLOYEE t0_ inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + where t1_.DEPARTMENT_NAME = ? + +You can associate many entities: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + Address_ a = new Address_(); + + List list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .innerJoin(a, on -> on.eq(e.addressId, a.addressId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }) + .associate(e, a, Employee::setAddress) + .fetch(); + +association for immutable entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can associate immutable entities with the ``associateWith`` operation. +You have to use the ``associateWith`` operation with join expression. + +.. code-block:: java + + Emp_ e = new Emp_(); + Emp_ m = new Emp_(); + Dept_ d = new Dept_(); + + List list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .leftJoin(m, on -> on.eq(e.managerId, m.employeeId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associateWith(e, d, Emp::withDept) + .associateWith(e, m, Emp::withManager) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION, + t1_.DEPARTMENT_ID, t1_.DEPARTMENT_NO, t1_.DEPARTMENT_NAME, t1_.LOCATION, t1_.VERSION, + t2_.EMPLOYEE_ID, t2_.EMPLOYEE_NO, t2_.EMPLOYEE_NAME, t2_.MANAGER_ID, t2_.HIREDATE, + t2_.SALARY, t2_.DEPARTMENT_ID, t2_.ADDRESS_ID, t2_.VERSION + from EMPLOYEE t0_ + inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + left outer join EMPLOYEE t2_ on (t0_.MANAGER_ID = t2_.EMPLOYEE_ID) + where t1_.DEPARTMENT_NAME = ? + +Dynamic join expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A join expression uses only evaluated operators to build a JOIN clause. + +When every operators are not evaluated in a join expression, +the built statement doesn't have any JOIN clause. + +For example, suppose that a join expression contains a conditional expression as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + Employee_ e2 = new Employee_(); + + List list = + queryDsl + .from(e) + .innerJoin( + e2, + on -> { + if (join) { + on.eq(e.managerId, e2.employeeId); + } + }) + .fetch(); + +In the case that the ``join`` variable is ``false``, the ``on`` expression is ignored. +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + +Dynamic association +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you use the above dynamic join expression, the association must be optional. +To do it, pass the result of ``AssociationOption.optional()`` to the associate method: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List list = + queryDsl + .from(e) + .innerJoin( + d, + on -> { + if (join) { + on.eq(e.departmentId, d.departmentId); + } + }) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }, + AssociationOption.optional()) + .fetch(); + +Aggregate Functions +------------------------------- + +We support the following aggregate functions: + +* avg(property) +* avgAsDouble(property) +* count() +* count(property) +* countDistinct(property) +* max(property) +* min(property) +* sum(property) + +These are defined in the ``org.seasar.doma.jdbc.criteria.expression.Expressions`` class. +Use them with static import. + +For example, you can pass the ``sum`` function to the select method: + +.. code-block:: java + + Employee_ e = new Employee_(); + + Salary salary = queryDsl.from(e).select(sum(e.salary)).fetchOne(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select sum(t0_.SALARY) from EMPLOYEE t0_ + +Group by expression +------------------------------- + +.. code-block:: java + + Employee_ e = new Employee_(); + + List> list = + queryDsl.from(e).groupBy(e.departmentId).select(e.departmentId, count()).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.DEPARTMENT_ID, count(*) from EMPLOYEE t0_ group by t0_.DEPARTMENT_ID + +When you don't specify a group by expression, +the expression is inferred from the select expression automatically. +So the following code issue the same SQL statement above: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List> list = + queryDsl.from(e).select(e.departmentId, count()).fetch(); + +Having expression +----------------------------- + +We support the following operators: + +* eq - (=) +* ne - (<>) +* ge - (>=) +* gt - (>) +* le - (<=) +* lt - (<) + +We also support the following logical operators: + +* and +* or +* not + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List> list = + queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .having(c -> c.gt(count(), 3L)) + .orderBy(c -> c.asc(count())) + .select(count(), d.departmentName) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select count(*), t1_.DEPARTMENT_NAME + from EMPLOYEE t0_ + inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + group by t1_.DEPARTMENT_NAME having count(*) > ? or (min(t0_.SALARY) <= ?) + order by count(*) asc + +Dynamic having expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A having expression uses only evaluated operators to build a HAVING clause. + +When every operators are not evaluated in a having expression, +the built statement doesn't have any HAVING clause. + +As well as, when every operators are not evaluated in a logical operator expression, +the built statement doesn't have the logical operator expression. + +Order by expression +----------------------------------------- + +We support the following order operations: + +* asc +* desc + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = + queryDsl + .from(e) + .orderBy( + c -> { + c.asc(e.departmentId); + c.desc(e.salary); + }) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + order by t0_.DEPARTMENT_ID asc, t0_.SALARY desc + +Dynamic order by expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An order by expression uses only evaluated operators to build an ORDER BY clause. + +When every operators are not evaluated in a order by expression, +the built statement doesn't have any ORDER BY clause. + +Distinct expression +----------------------------------------- + +.. code-block:: java + + List list = + queryDsl + .from(d) + .distinct() + .leftJoin(e, on -> on.eq(d.departmentId, e.departmentId)) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select distinct t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, + t0_.LOCATION, t0_.VERSION + from DEPARTMENT t0_ + left outer join EMPLOYEE t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) + +Limit and Offset expression +------------------------------------------------- + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = + queryDsl.from(e).limit(5).offset(3).orderBy(c -> c.asc(e.employeeNo)).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + order by t0_.EMPLOYEE_NO asc + offset 3 rows fetch first 5 rows only + +Dynamic Limit and Offset expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A limit expressions uses only non-null value to build a FETCH FIRST clause. +When the value is null ,the built statement doesn't have any FETCH FIRST clause. + +As well as, an offset expressions uses only non-null value to build a OFFSET clause. +When the value is null ,the built statement doesn't have any OFFSET clause. + +For Update expression +------------------------------------------- + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).forUpdate().fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + where t0_.EMPLOYEE_ID = ? + for update + +Union expression +---------------------------- + +We support the following expressions: + +- union +- unionAll - (union all) + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List> list = + queryDsl + .from(e) + .select(e.employeeId, e.employeeName) + .union(queryDsl.from(d) + .select(d.departmentId, d.departmentName)) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NAME from EMPLOYEE t0_ + union + select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NAME from DEPARTMENT t0_ + +The order by expression with index is supported: + +.. code-block:: java + + Employee_ e = new Employee_(); + Department_ d = new Department_(); + + List> list = + queryDsl + .from(e) + .select(e.employeeId, e.employeeName) + .union(queryDsl.from(d) + .select(d.departmentId, d.departmentName)) + .orderBy(c -> c.asc(2)) + .fetch(); + +Derived Table expression +---------------------------------------------- + +We support subqueries using derived tables. +However, an entity class corresponding to the derived table is required. + +Define the entity class corresponding to the derived table as follows: + +.. code-block:: java + + @Entity(metamodel = @Metamodel) + public class NameAndAmount { + private String name; + private Integer amount; + + public NameAndAmount() {} + + public NameAndAmount(String accounting, BigDecimal bigDecimal) { + this.name = accounting; + this.amount = bigDecimal.intValue(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NameAndAmount that = (NameAndAmount) o; + return Objects.equals(name, that.name) && Objects.equals(amount, that.amount); + } + + @Override + public int hashCode() { + return Objects.hash(name, amount); + } + } + + +A subquery using a derived table can be written as follows. + +.. code-block:: java + + Department_ d = new Department_(); + Employee_ e = new Employee_(); + NameAndAmount_ t = new NameAndAmount_(); + + SetOperand subquery = + queryDsl + .from(e) + .innerJoin(d, c -> c.eq(e.departmentId, d.departmentId)) + .groupBy(d.departmentName) + .select(d.departmentName, Expressions.sum(e.salary)); + + List list = + queryDsl.from(t, subquery).orderBy(c -> c.asc(t.name)).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select + t0_.NAME, + t0_.AMOUNT + from + ( + select + t2_.DEPARTMENT_NAME AS NAME, + sum(t1_.SALARY) AS AMOUNT + from + EMPLOYEE t1_ + inner join + DEPARTMENT t2_ on (t1_.DEPARTMENT_ID = t2_.DEPARTMENT_ID) + group by + t2_.DEPARTMENT_NAME + ) t0_ + order by + t0_.NAME asc + +Delete statement +============================ + +For the specification of the where expression, see :ref:`query_dsl_where`. +The same rule is applied to delete statements. + +Delete settings +------------------------------------- + +We support the following settings: + +* allowEmptyWhere +* batchSize +* comment +* ignoreVersion +* queryTimeout +* sqlLogType +* suppressOptimisticLockException + +They are all optional. + +You can apply them as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = queryDsl.delete(e, settings -> { + settings.setAllowEmptyWhere(true); + settings.setBatchSize(20); + settings.setComment("delete all"); + settings.setIgnoreVersion(true); + settings.setQueryTimeout(1000); + settings.setSqlLogType(SqlLogType.RAW); + settings.setSuppressOptimisticLockException(true); + }) + .where { } + .execute(); + +.. note:: + + If you want to build a delete statement with an empty WHERE clause, + you have to enable the `allowEmptyWhere` setting. + +Delete record by entity +----------------------- + +.. code-block:: java + + Employee_ e = new Employee_(); + + Employee employee = queryDsl.from(e).where(c -> c.eq(e.employeeId, 5)).fetchOne(); + + Result result = queryDsl.delete(e).single(employee).execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + delete from EMPLOYEE where EMPLOYEE_ID = ? and VERSION = ? + +Batch Delete is also supported: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List employees = + queryDsl.from(e).where(c -> c.in(e.employeeId, Arrays.asList(5, 6))).fetch(); + + BatchResult result = queryDsl.delete(e).batch(employees).execute(); + +The execute method may throw following exceptions: + +* OptimisticLockException: if the entity has a version property and an update count is 0 + +Delete records by where expression +---------------------------------- + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = queryDsl.delete(e).where(c -> c.ge(e.salary, new Salary("2000"))).execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + delete from EMPLOYEE t0_ where t0_.SALARY >= ? + +To delete all records, call the ``all`` method: + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = queryDsl.delete(e).all().execute(); + +Insert statement +============================ + +Insert settings +------------------------------------- + +We support the following settings: + +* comment +* queryTimeout +* sqlLogType +* batchSize +* excludeNull +* include +* exclude +* ignoreGeneratedKeys + +They are all optional. + +You can apply them as follows: + +.. code-block:: java + + Department_ d = new Department_(); + + int count = + queryDsl + .insert(d, settings -> { + settings.setComment("insert department"); + settings.setQueryTimeout(1000); + settings.setSqlLogType(SqlLogType.RAW); + settings.setBatchSize(20); + settings.excludeNull(true); + }) + .values( + c -> { + c.value(d.departmentId, 99); + c.value(d.departmentNo, 99); + c.value(d.departmentName, "aaa"); + c.value(d.location, "bbb"); + c.value(d.version, 1); + }) + .execute(); + +.. code-block:: java + + Department_ d = new Department_(); + + Department department = ...; + + Result result = queryDsl.insert(d, settings -> + settings.exclude(d.departmentName, d.location) + ) + .single(department) + .execute(); + +Insert record with entity +---------------------------- + +.. code-block:: java + + Department_ d = new Department_(); + + Department department = new Department(); + department.setDepartmentId(99); + department.setDepartmentNo(99); + department.setDepartmentName("aaa"); + department.setLocation("bbb"); + + Result result = queryDsl.insert(d).single(department).execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) + values (?, ?, ?, ?, ?) + +Batch Insert is also supported: + +.. code-block:: java + + Department_ d = new Department_(); + + Department department = ...; + Department department2 = ...; + List departments = Arrays.asList(department, department2); + + BatchResult result = queryDsl.insert(d).batch(departments).execute(); + +Multi-row Insert is also supported: + +.. code-block:: java + + Department_ d = new Department_(); + + Department department = ...; + Department department2 = ...; + List departments = Arrays.asList(department, department2); + + MultiResult result = queryDsl.insert(d).multi(departments).execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) + values (?, ?, ?, ?, ?), (?, ?, ?, ?, ?) + +Upsert is also supported: + +By calling on `onDuplicateKeyUpdate`, you can update when a key is duplicated. + +.. code-block:: java + + BatchResult = queryDsl + .insert(d) + .multi(departments) + .onDuplicateKeyUpdate() + .execute(); + +By calling on `onDuplicateKeyIgnore`, you can ignore errors when a key is duplicated. + +.. code-block:: java + + BatchResult = queryDsl + .insert(d) + .multi(departments) + .onDuplicateKeyIgnore() + .execute(); + +The execute method may throw following exceptions: + +* UniqueConstraintException: if an unique constraint is violated + +Insert record with specified values +----------------------------------- + +.. code-block:: java + + Department_ d = new Department_(); + + int count = + queryDsl + .insert(d) + .values( + c -> { + c.value(d.departmentId, 99); + c.value(d.departmentNo, 99); + c.value(d.departmentName, "aaa"); + c.value(d.location, "bbb"); + c.value(d.version, 1); + }) + .execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) + values (?, ?, ?, ?, ?) + +The execute method may throw following exceptions: + +* UniqueConstraintException: if an unique constraint is violated + +We also support the INSERT SELECT syntax as follows: + +.. code-block:: java + + Department_ da = new Department_("DEPARTMENT_ARCHIVE"); + Department_ d = new Department_(); + + int count = + queryDsl + .insert(da) + .select(c -> c.from(d).where(cc -> cc.in(d.departmentId, Arrays.asList(1, 2)))) + .execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + insert into DEPARTMENT_ARCHIVE (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, + LOCATION, VERSION) select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, + t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_ID in (?, ?) + +Upsert is also supported: + +By calling on `onDuplicateKeyUpdate`, you can update when a key is duplicated. +You can specify keys for duplicate check targets in `keys`. +You can specify the value of the update in case of duplicates in `set`. + +.. code-block:: java + + int count result = queryDsl + .insert(d) + .values( + c -> { + c.value(d.departmentId, 1); + c.value(d.departmentNo, 60); + c.value(d.departmentName, "DEVELOPMENT"); + c.value(d.location, "KYOTO"); + c.value(d.version, 2); + }) + .onDuplicateKeyUpdate() + .keys(d.departmentId) + .set( + c -> { + c.value(d.departmentName, c.excluded(d.departmentName)); + c.value(d.location, "KYOTO"); + c.value(d.version, 3); + }) + .execute(); + +By calling on `onDuplicateKeyIgnore`, you can ignore errors when a key is duplicated. +You can specify keys for duplicate check targets in `keys`. + +.. code-block:: java + + int count result = queryDsl + .insert(d, departments) + .values( + c -> { + c.value(d.departmentId, 1); + c.value(d.departmentNo, 60); + c.value(d.departmentName, "DEVELOPMENT"); + c.value(d.location, "KYOTO"); + c.value(d.version, 2); + }) + .onDuplicateKeyIgnore() + .keys(d.departmentId) + .execute(); + +Update statement +============================ + +For the specification of the where expression, see :ref:`query_dsl_where`. +The same rule is applied to update statements. + +Update settings +------------------------------------- + +We support the following settings: + +* allowEmptyWhere +* batchSize +* comment +* ignoreVersion +* queryTimeout +* sqlLogType +* suppressOptimisticLockException +* excludeNull +* include +* exclude + +They are all optional. + +You can apply them as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = queryDsl.update(e, settings -> { + settings.setAllowEmptyWhere(true); + settings.setBatchSize(20); + settings.setComment("update all"); + settings.setIgnoreVersion(true); + settings.setQueryTimeout(1000); + settings.setSqlLogType(SqlLogType.RAW); + settings.setSuppressOptimisticLockException(true); + settings.excludeNull(true); + }).set(c -> { + c.value(e.employeeName, "aaa"); + }).execute(); + +.. code-block:: java + + Employee_ e = new Employee_(); + + Employee employee = ...; + + Result result = queryDsl.update(e, settings -> + settings.exclude(e.hiredate, e.salary) + ) + .single(employee) + .execute(); + +.. note:: + + If you want to build a update statement without a WHERE clause, + you have to enable the `allowEmptyWhere` setting. + +Update record by entity +----------------------- + +.. code-block:: java + + Employee_ e = new Employee_(); + + Employee employee = queryDsl.from(e).where(c -> c.eq(e.employeeId, 5)).fetchOne(); + employee.setEmployeeName("aaa"); + employee.setSalary(new Salary("2000")); + + Result result = queryDsl.update(e).single(employee).execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + update EMPLOYEE set EMPLOYEE_NAME = ?, SALARY = ?, VERSION = ? + 1 + where EMPLOYEE_ID = ? and VERSION = ? + +Batch Update is also supported: + +.. code-block:: java + + Employee_ e = new Employee_(); + + Employee employee = ...; + Employee employee2 = ...; + List departments = Arrays.asList(employee, employee2); + + BatchResult result = queryDsl.update(e).batch(employees).execute(); + +The execute method may throw following exceptions: + +* OptimisticLockException: if the entity has a version property and an update count is 0 +* UniqueConstraintException: if an unique constraint is violated + +Update records by where expression +------------------------------------ + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = + queryDsl + .update(e) + .set(c -> c.value(e.departmentId, 3)) + .where( + c -> { + c.isNotNull(e.managerId); + c.ge(e.salary, new Salary("2000")); + }) + .execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + update EMPLOYEE t0_ set t0_.DEPARTMENT_ID = ? + where t0_.MANAGER_ID is not null and t0_.SALARY >= ? + +The execute method may throw following exceptions: + +* UniqueConstraintException: if an unique constraint is violated + +Property expressions +========================================== + +All expression methods are defined +in the ``org.seasar.doma.jdbc.criteria.expression.Expressions`` class. + +Use them with static import. + +Arithmetic expressions +---------------------- + +We provide the following methods: + +* add - (+) +* sub - (-) +* mul - (*) +* div - (/) +* mod - (%) + +You can use the ``add`` method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = + queryDsl + .update(e) + .set(c -> c.value(e.version, add(e.version, 10))) + .where(c -> c.eq(e.employeeId, 1)) + .execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + update EMPLOYEE t0_ + set t0_.VERSION = (t0_.VERSION + ?) + where t0_.EMPLOYEE_ID = ? + +String functions +---------------- + +We provide the following method: + +* concat +* lower +* upper +* trim +* ltrim +* rtrim + +You can use the ``concat`` method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + int count = + queryDsl + .update(e) + .set(c -> c.value(e.employeeName, concat("[", concat(e.employeeName, "]")))) + .where(c -> c.eq(e.employeeId, 1)) + .execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + update EMPLOYEE t0_ + set t0_.EMPLOYEE_NAME = concat(?, concat(t0_.EMPLOYEE_NAME, ?)) + where t0_.EMPLOYEE_ID = ? + +Literal expression +------------------ + +We provide the following method: + +* literal (for all basic data types) + +You can use the ``literal`` method as follows: + +.. code-block:: java + + Employee employee = queryDsl.from(e).where(c -> c.eq(e.employeeId, literal(1))).fetchOne(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, + t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION + from EMPLOYEE t0_ + where t0_.EMPLOYEE_ID = 1 + +.. note:: + + Note that the literal expressions are not recognized as bind variables. + +Case expression +--------------- + +We support the following method: + +* when + +You can use the ``when`` method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + List list = + queryDsl + .from(e) + .select( + when( + c -> { + c.eq(e.employeeName, literal("SMITH"), lower(e.employeeName)); + c.eq(e.employeeName, literal("KING"), lower(e.employeeName)); + }, + literal("_"))) + .fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select case + when t0_.EMPLOYEE_NAME = 'SMITH' then lower(t0_.EMPLOYEE_NAME) + when t0_.EMPLOYEE_NAME = 'KING' then lower(t0_.EMPLOYEE_NAME) + else '_' end + from EMPLOYEE t0_ + +Subquery select expression +-------------------------- + +We support the following method: + +* select + +You can use the ``select`` method as follows: + +.. code-block:: java + + Employee_ e = new Employee_(); + + Employee_ e = new Employee_(); + Employee_ e2 = new Employee_(); + Department_ d = new Department_(); + + SelectExpression subSelect = + select( + c -> + c.from(e2) + .innerJoin(d, on -> on.eq(e2.departmentId, d.departmentId)) + .where(cc -> cc.eq(e.departmentId, d.departmentId)) + .groupBy(d.departmentId) + .select(max(e2.salary))); + + int count = + queryDsl + .update(e) + .set(c -> c.value(e.salary, subSelect)) + .where(c -> c.eq(e.employeeId, 1)) + .execute(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + update EMPLOYEE t0_ + set t0_.SALARY = ( + select max(t1_.SALARY) + from EMPLOYEE t1_ + inner join DEPARTMENT t2_ on (t1_.DEPARTMENT_ID = t2_.DEPARTMENT_ID) + where t0_.DEPARTMENT_ID = t2_.DEPARTMENT_ID group by t2_.DEPARTMENT_ID + ) + where t0_.EMPLOYEE_ID = ? + +User-defined expressions +------------------------ + +You can define user-defined expressions by calling ``Expressions.userDefined``. + +In the example below, the replace function is defined: + +.. code-block:: java + + UserDefinedExpression replace(PropertyMetamodel expression, PropertyMetamodel from, PropertyMetamodel to) { + return Expressions.userDefined(expression, "replace", from, to, c -> { + c.appendSql("replace("); + c.appendExpression(expression); + c.appendSql(", "); + c.appendExpression(from); + c.appendSql(", "); + c.appendExpression(to); + c.appendSql(")"); + }); + } + +You can use the replace function in your query as follows: + +.. code-block:: java + + Department_ d = new Department_(); + + List list = + queryDsl + .from(d).select(replace(d.location, Expressions.literal("NEW"), Expressions.literal("new"))).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select replace(t0_.LOCATION, 'NEW', 'new') from DEPARTMENT t0_ + +Scopes +========================================== + +Scoping allow you to specify commonly-used query conditions. + +To define a simple scope, +create the class which has a method annotated with ``@Scope``: + +.. code-block:: java + + public class DepartmentScope { + @Scope + public Consumer onlyTokyo(Department_ d) { + return c -> c.eq(d.location, "Tokyo"); + } + } + +To enable the scope, +specify the above class in the scopes element of ``@Metamodel``: + +.. code-block:: java + + @Entity(metamodel = @Metamodel(scopes = { DepartmentScope.class })) + public class Department { ... } + +Now the metamodel ``Department_`` has a ``onlyTokyo`` method. +You can use it as follows: + +.. code-block:: java + + Department_ d = new Department_(); + + List list = queryDsl.from(d).where(d.onlyTokyo()).fetch(); + +The above query issues the following SQL statement: + +.. code-block:: sql + + select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ + where t0_.LOCATION = ? + +When you want to combine other query conditions with scopes, +compose them using the `andThen` method: + +.. code-block:: java + + Department_ d = new Department_(); + + List list = queryDsl.from(d).where(d.onlyTokyo().andThen(c -> c.gt(d.departmentNo, 50))).fetch(); + +You can define several scopes in a class as follows: + +.. code-block:: java + + public class DepartmentScope { + @Scope + public Consumer onlyTokyo(Department_ d) { + return c -> c.eq(d.location, "Tokyo"); + } + + @Scope + public Consumer locationStartsWith(Department_ d, String prefix) { + return c -> c.like(d.location, prefix, LikeOption.prefix()); + } + + @Scope + public Consumer sortByNo(Department_ d) { + return c -> c.asc(d.departmentNo); + } + } + +Tips +==== + +Execution in DAO +-------------------------------------- + +It is useful to execute DSLs in the default method of the DAO interface. +To get a ``config`` object, call ``Config.get(this)`` in the default method as follows: + +.. code-block:: java + + @Dao + public interface EmployeeDao { + + default Optional selectById(Integer id) { + QueryDsl queryDsl = new QueryDsl(Config.get(this)); + + Employee_ e = new Employee_(); + return queryDsl.from(e).where(c -> c.eq(e.employeeId, id)).fetchOptional(); + } + } + +Overwriting the table name +------------------------------------------------ + +A metamodel constructor accepts the qualified table name and +the metamodel overwrites its table name. + +It is useful to handle two tables that have the same data structure: + +.. code-block:: java + + Department_ da = new Department_("DEPARTMENT_ARCHIVE"); + Department_ d = new Department_(); + + int count = + queryDsl + .insert(da) + .select(c -> c.from(d)) + .execute(); + +.. code-block:: sql + + insert into DEPARTMENT_ARCHIVE (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, + LOCATION, VERSION) select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, + t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ + +Debugging +------------------------------- + +To know the SQL statement built by the DSLs, use the ``asSql`` method: + +.. code-block:: java + + Department_ d = new Department_(); + + Listable stmt = queryDsl.from(d).where(c -> c.eq(d.departmentName, "SALES")); + + Sql sql = stmt.asSql(); + System.out.printf("Raw SQL : %s\n", sql.getRawSql()); + System.out.printf("Formatted SQL: %s\n", sql.getFormattedSql()); + +The above code prints as follows: + +.. code-block:: sh + + Raw SQL : select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = ? + Formatted SQL: select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = 'SALES' + +The ``asSql`` method doesn't issue the SQL statement to your Database. +It only builds the SQL statement and return it as an ``Sql`` object. + +You can also get the ``Sql`` object by calling the ``peek`` method. + +.. code-block:: java + + Department_ d = new Department_(); + + List locations = queryDsl + .from(d) + .peek(System.out::println) + .where(c -> c.eq(d.departmentName, "SALES")) + .peek(System.out::println) + .orderBy(c -> c.asc(d.location)) + .peek(sql -> System.out.println(sql.getFormattedSql())) + .select(d.location) + .peek(sql -> System.out.println(sql.getFormattedSql())) + .fetch(); + +The above code prints as follows: + +.. code-block:: sql + + select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ + select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = ? + select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = 'SALES' order by t0_.LOCATION asc + select t0_.LOCATION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = 'SALES' order by t0_.LOCATION asc + +Sample projects +=============== + +* `simple-examples `_ +* `kotlin-sample `_ From 33acf6a0592b649ff867f34f1d4c049fc44b5c6a Mon Sep 17 00:00:00 2001 From: Toshihiro Nakamura Date: Tue, 29 Oct 2024 21:15:10 +0900 Subject: [PATCH 2/2] Polish --- docs/query-dsl.rst | 1331 ++++++++++++++++++++------------------------ 1 file changed, 600 insertions(+), 731 deletions(-) diff --git a/docs/query-dsl.rst b/docs/query-dsl.rst index bf43fdeb3..cd0b33c6a 100644 --- a/docs/query-dsl.rst +++ b/docs/query-dsl.rst @@ -1,6 +1,6 @@ -============ +========= Query DSL -============ +========= .. contents:: :depth: 4 @@ -8,7 +8,7 @@ Query DSL Introduction ============ -We use the following Entity classes to show you some examples: +The following entity classes are used in the examples below: .. code-block:: java @@ -86,39 +86,39 @@ We use the following Entity classes to show you some examples: Note that the above classes are annotated with ``@Entity(metamodel = @Metamodel)``. The ``metamodel = @Metamodel`` indicates that the annotated entity -has a corresponding metamodel class generated by Doma's annotation processor . +has a corresponding metamodel class generated by Doma's annotation processor. In our examples, the metamodel classes are ``Employee_``, ``Department_``, ``Emp_`` and ``Dept_``. -These metamodels allow you to make your query typesafe. +These metamodels enable type-safe query creation. -You can customize the name of the metamodels by the Metamodel annotation elements. +You can customize the metamodel names using the elements in the `Metamodel` annotation. -To customize all metamodels in bulk, you can use the annotation processor options. -See :doc:`annotation-processing` and check the following options: +To bulk customize all metamodels, you can use annotation processor options. +See :doc:`annotation-processing` and refer to the following options: * doma.metamodel.enabled * doma.metamodel.prefix * doma.metamodel.suffix Query DSL ------------- +--------- -The Query DSL can query and associate entities. +The Query DSL can perform entity queries and associations. The entry point is the ``org.seasar.doma.jdbc.criteria.QueryDsl`` class. -This class has the following methods: +This class includes the following methods: * from * insert * delete * update -You can instantiate the ``QueryDsl`` class as follows: +Instantiate the ``QueryDsl`` class as follows: .. code-block:: java QueryDsl queryDsl = new QueryDsl(config); -For example, to query ``Employee`` and ``Department`` entities and associate them, write as follows: +For example, to query ``Employee`` and ``Department`` entities and associate them, use: .. code-block:: java @@ -139,7 +139,7 @@ For example, to query ``Employee`` and ``Department`` entities and associate the }) .fetch(); -The above query issues the following SQL statement: +The query above generates the following SQL statement: .. code-block:: sql @@ -151,14 +151,14 @@ The above query issues the following SQL statement: .. note:: - In Kotlin environment, use ``org.seasar.doma.kotlin.jdbc.criteria.KQueryDsl`` instead of ``QueryDsl``. + In Kotlin, use ``org.seasar.doma.kotlin.jdbc.criteria.KQueryDsl`` instead of ``QueryDsl``. ``KQueryDsl`` is included in the doma-kotlin module. -Select statement +Select Statement ================ -Select settings -------------------------------------- +Select Settings +--------------- We support the following settings: @@ -169,8 +169,7 @@ We support the following settings: * queryTimeout * sqlLogType -They are all optional. -You can apply them as follows: +All are optional and can be applied as follows: .. code-block:: java @@ -186,9 +185,9 @@ You can apply them as follows: }).fetch(); Fetching ------------------------------- +-------- -The Query DSL supports the following methods to fetch data from a database: +The Query DSL provides the following data-fetching methods: * fetch * fetchOne @@ -200,26 +199,21 @@ The Query DSL supports the following methods to fetch data from a database: Employee_ e = new Employee_(); // The fetch method returns results as a list. - List list = - queryDsl.from(e).fetch(); + List list = queryDsl.from(e).fetch(); - // The fetchOne method returns a single result. The result may be null. - Employee employee = - queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).fetchOne(); + // The fetchOne method returns a single result, possibly null. + Employee employee = queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).fetchOne(); - // The fetchOptional method returns a single result as an optional object. - Optional optional = - queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).fetchOptional(); + // The fetchOptional method returns a single result as an Optional object. + Optional optional = queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).fetchOptional(); // The stream method returns results as a stream. - // The following code is equivalent to "queryDsl.from(e).fetch().stream()" - Stream stream = - queryDsl.from(e).stream(); + Stream stream = queryDsl.from(e).stream(); Streaming ---------------------- +--------- -The Query DSL supports the following methods: +The Query DSL supports the following stream-handling methods: * mapStream * collect @@ -229,49 +223,42 @@ The Query DSL supports the following methods: Employee_ e = new Employee_(); - // The mapStream method handles a stream. - Map> map = - queryDsl - .from(e) - .mapStream(stream -> stream.collect(groupingBy(Employee::getDepartmentId))); + // mapStream allows processing of a stream. + Map> map = queryDsl + .from(e) + .mapStream(stream -> stream.collect(groupingBy(Employee::getDepartmentId))); - // The collect method is a shortcut of the mapStream method. - // The following code does the same thing with the above. - Map> map2 = - queryDsl.from(e).collect(groupingBy(Employee::getDepartmentId)); + // collect is a shorthand for mapStream. + Map> map2 = queryDsl.from(e).collect(groupingBy(Employee::getDepartmentId)); - // The openStream method returns a stream. - // You MUST close the stream explicitly. + // openStream returns a stream. You MUST close the stream explicitly. try (Stream stream = queryDsl.from(e).openStream()) { - stream.forEach(employee -> { - // do something - }); + stream.forEach(employee -> { + // do something + }); } -These methods handle the stream that wraps a JDBC ResultSet. -So they are useful to process a large ResultSet effectively. +These methods provide efficient processing for large result sets. -Select expression ------------------------------ +Select Expression +----------------- -Entity selection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Entity Selection +~~~~~~~~~~~~~~~~ -By default, the result entity type is the same as the one specified at the ``from`` method. -See the following code: +By default, the result entity type is the same as the type specified in the ``from`` method: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List list = - queryDsl - .from(e) - .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) - .fetch(); + List list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .fetch(); -The above query issues the following SQL statement: +The above query generates the following SQL statement: .. code-block:: sql @@ -280,22 +267,20 @@ The above query issues the following SQL statement: from EMPLOYEE t0_ inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) -To choose a joined entity type as the result entity type, -call the ``project`` or ``select`` method as follows: +To choose a joined entity type as the result entity type, use ``project`` or ``select``: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List list = - queryDsl - .from(e) - .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) - .project(d) - .fetch(); + List list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .project(d) + .fetch(); -The above query issues the following SQL statement: +This query generates the following SQL: .. code-block:: sql @@ -305,29 +290,27 @@ The above query issues the following SQL statement: .. note:: - The ``project`` method removes duplicate entities from the results, - whereas the ``select`` method does not. - If you call neither method, duplicate entities are removed from the results by default. + The ``project`` method removes duplicate entities, while ``select`` does not. + If you call neither method, duplicates are removed by default. -Multiple entity selection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Multiple Entity Selection +~~~~~~~~~~~~~~~~~~~~~~~~~ -You can specify multiple entity types and fetch them as a tuple as follows: +Specify multiple entity types and fetch them as tuples: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List> list = - queryDsl - .from(d) - .leftJoin(e, on -> on.eq(d.departmentId, e.departmentId)) - .where(c -> c.eq(d.departmentId, 4)) - .select(d, e) - .fetch(); + List> list = queryDsl + .from(d) + .leftJoin(e, on -> on.eq(d.departmentId, e.departmentId)) + .where(c -> c.eq(d.departmentId, 4)) + .select(d, e) + .fetch(); -The above query issues the following SQL statement: +This query generates: .. code-block:: sql @@ -337,18 +320,16 @@ The above query issues the following SQL statement: from DEPARTMENT t0_ left outer join EMPLOYEE t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) where t0_.DEPARTMENT_ID = ? -The entity included in the tuple may be null when the all properties of the entity are null. +In the tuple, an entity is null if all its properties are null. .. note:: - The ``select`` method does not remove duplicate entities from the results. - -Column projection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The ``select`` method does not remove duplicates. -To project columns, use the ``select`` method: +Column Projection +~~~~~~~~~~~~~~~~~ -To project one column, pass one property to the select method as follows: +To project columns, use ``select``. For one column: .. code-block:: java @@ -356,43 +337,43 @@ To project one column, pass one property to the select method as follows: List list = queryDsl.from(e).select(e.employeeName).fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql select t0_.EMPLOYEE_NAME from EMPLOYEE t0_ -To project two or more columns, pass two or more properties to the select method as follows: +For multiple columns: .. code-block:: java Employee_ e = new Employee_(); - List> list = - queryDsl.from(e).select(e.employeeName, e.employeeNo).fetch(); + List> list = queryDsl + .from(e) + .select(e.employeeName, e.employeeNo) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql select t0_.EMPLOYEE_NAME, t0_.EMPLOYEE_NO from EMPLOYEE t0_ -Up to 9 numbers, the column results are held by ``Tuple2`` to ``Tuple9``. -For more than 9 numbers, the results are held by ``Row``. +Columns up to 9 are held in ``Tuple2`` to ``Tuple9``. Beyond that, they are held in ``Row``. -You can get a ``Row`` list explicitly by using ``selectAsRow`` as follows: +Use ``selectAsRow`` for a ``Row`` list: .. code-block:: java Employee_ e = new Employee_(); - List list = - queryDsl.from(e).selectAsRow(e.employeeName, e.employeeNo).fetch(); + List list = queryDsl.from(e).selectAsRow(e.employeeName, e.employeeNo).fetch(); -Column projection and mapping -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Column Projection and Mapping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To project columns and map them to an entity, use the ``projectTo`` or ``selectTo`` method as follows: +To project columns and map them to an entity, use the ``projectTo`` or ``selectTo`` methods: .. code-block:: java @@ -400,26 +381,24 @@ To project columns and map them to an entity, use the ``projectTo`` or ``selectT List list = queryDsl.from(e).selectTo(e, e.employeeName).fetch(); -The above query issues the following SQL statement: +This query generates: .. code-block:: sql select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NAME from EMPLOYEE t0_ -Note that the select clause of the above SQL statement contains the primary key "EMPLOYEE_ID". -The ``projectTo`` and ``selectTo`` methods always include the id properties of the entity, even if you don't specify them. +Note that the SQL select clause includes the primary key "EMPLOYEE_ID". The ``projectTo`` and ``selectTo`` methods always include the entity's ID properties, even if they aren't explicitly specified. .. note:: - The ``projectTo`` method removes duplicate entity ids from the results, - whereas the ``selectTo`` method does not. + The ``projectTo`` method removes duplicate entity IDs from the results, while ``selectTo`` does not. .. _query_dsl_where: -Where expression --------------------------------------- +Where Expression +---------------- -We support the following operators and predicates: +The following operators and predicates are supported: * eq - (=) * ne - (<>) @@ -439,18 +418,14 @@ We support the following operators and predicates: .. note:: - If the right hand operand is ``null``, the WHERE or the HAVING clause doesn't include the operator. - See WhereDeclaration_ and HavingDeclaration_ javadoc for more details. - -.. _WhereDeclaration: https://www.javadoc.io/doc/org.seasar.doma/doma-core/latest/org/seasar/doma/jdbc/criteria/declaration/WhereDeclaration.html -.. _HavingDeclaration: https://www.javadoc.io/doc/org.seasar.doma/doma-core/latest/org/seasar/doma/jdbc/criteria/declaration/HavingDeclaration.html + If the right-hand operand is ``null``, the WHERE or HAVING clause will exclude the operator. See `WhereDeclaration`_ and `HavingDeclaration`_ javadoc for details. -We also support the following utility operators: +We also support utility operators: * eqOrIsNull - ("=" or "is null") * neOrIsNotNull - ("<>" or "is not null") -We also support the following logical operators: +Additionally, the following logical operators are supported: * and * or @@ -460,22 +435,19 @@ We also support the following logical operators: Employee_ e = new Employee_(); - List list = - queryDsl - .from(e) - .where( - c -> { - c.eq(e.departmentId, 2); - c.isNotNull(e.managerId); - c.or( - () -> { - c.gt(e.salary, new Salary("1000")); - c.lt(e.salary, new Salary("2000")); - }); - }) - .fetch(); + List list = queryDsl + .from(e) + .where(c -> { + c.eq(e.departmentId, 2); + c.isNotNull(e.managerId); + c.or(() -> { + c.gt(e.salary, new Salary("1000")); + c.lt(e.salary, new Salary("2000")); + }); + }) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -484,21 +456,20 @@ The above query issues the following SQL statement: from EMPLOYEE t0_ where t0_.DEPARTMENT_ID = ? and t0_.MANAGER_ID is not null or (t0_.SALARY > ? and t0_.SALARY < ?) -You can write a subquery as follows: +Subqueries can be written as follows: .. code-block:: java Employee_ e = new Employee_(); Employee_ e2 = new Employee_(); - List list = - queryDsl - .from(e) - .where(c -> c.in(e.employeeId, c.from(e2).select(e2.managerId))) - .orderBy(c -> c.asc(e.employeeId)) - .fetch(); + List list = queryDsl + .from(e) + .where(c -> c.in(e.employeeId, c.from(e2).select(e2.managerId))) + .orderBy(c -> c.asc(e.employeeId)) + .fetch(); -The above query issues the following SQL statement: +The above query generates: .. code-block:: sql @@ -508,37 +479,28 @@ The above query issues the following SQL statement: where t0_.EMPLOYEE_ID in (select t1_.MANAGER_ID from EMPLOYEE t1_) order by t0_.EMPLOYEE_ID asc -Dynamic where expression -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A where expression uses only evaluated operators to build a WHERE clause. +Dynamic Where Expression +~~~~~~~~~~~~~~~~~~~~~~~~ -When every operators are not evaluated in a where expression, -the built statement doesn't have any WHERE clause. +A WHERE expression uses only evaluated operators to build a WHERE clause. When no operators are evaluated in the expression, the statement omits the WHERE clause. -As well as, when every operators are not evaluated in a logical operator expression, -the built statement doesn't have the logical operator expression. - -For example, suppose that a where expression contains a conditional expression as follows: +For example, with a conditional expression: .. code-block:: java Employee_ e = new Employee_(); - List list = - queryDsl - .from(e) - .where( - c -> { - c.eq(e.departmentId, 1); - if (enableNameCondition) { - c.like(e.employeeName, name); - } - }) - .fetch(); + List list = queryDsl + .from(e) + .where(c -> { + c.eq(e.departmentId, 1); + if (enableNameCondition) { + c.like(e.employeeName, name); + } + }) + .fetch(); -In the case that the ``enableNameCondition`` variable is ``false``, the ``like`` expression is ignored. -The above query issues the following SQL statement: +If ``enableNameCondition`` is ``false``, the ``like`` expression is ignored, generating: .. code-block:: sql @@ -546,26 +508,27 @@ The above query issues the following SQL statement: t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION from EMPLOYEE t0_ where t0_.DEPARTMENT_ID = ? -Join expression +Join Expression --------------- -We support the following expressions: +We support the following join expressions: -- innerJoin - (inner join) -- leftJoin - (left outer join) +* innerJoin - (inner join) +* leftJoin - (left outer join) -innerJoin -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Example for innerJoin: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List list = - queryDsl.from(e).innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)).fetch(); + List list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -574,18 +537,19 @@ The above query issues the following SQL statement: from EMPLOYEE t0_ inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) -leftJoin -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Example for leftJoin: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List list = - queryDsl.from(e).leftJoin(d, on -> on.eq(e.departmentId, d.departmentId)).fetch(); + List list = queryDsl + .from(e) + .leftJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -594,34 +558,30 @@ The above query issues the following SQL statement: from EMPLOYEE t0_ left outer join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) -.. _query_dsl_associate: - -association -~~~~~~~~~~~~~~~~~~~~~~ +Association +----------- -You can associate entities with the ``associate`` operation. -You have to use the ``associate`` operation with join expression. +You can associate entities using the ``associate`` operation in conjunction with a join expression: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List list = - queryDsl - .from(e) - .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) - .where(c -> c.eq(d.departmentName, "SALES")) - .associate( - e, - d, - (employee, department) -> { - employee.setDepartment(department); - department.getEmployeeList().add(employee); - }) - .fetch(); + List list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }) + .fetch(); -The above query issues the following SQL statement: +This query generates: .. code-block:: sql @@ -631,7 +591,7 @@ The above query issues the following SQL statement: from EMPLOYEE t0_ inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) where t1_.DEPARTMENT_NAME = ? -You can associate many entities: +Associating Multiple Entities: .. code-block:: java @@ -639,27 +599,25 @@ You can associate many entities: Department_ d = new Department_(); Address_ a = new Address_(); - List list = - queryDsl - .from(e) - .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) - .innerJoin(a, on -> on.eq(e.addressId, a.addressId)) - .where(c -> c.eq(d.departmentName, "SALES")) - .associate( - e, - d, - (employee, department) -> { - employee.setDepartment(department); - department.getEmployeeList().add(employee); - }) - .associate(e, a, Employee::setAddress) - .fetch(); + List list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .innerJoin(a, on -> on.eq(e.addressId, a.addressId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }) + .associate(e, a, Employee::setAddress) + .fetch(); -association for immutable entities -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Associating Immutable Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can associate immutable entities with the ``associateWith`` operation. -You have to use the ``associateWith`` operation with join expression. +To associate immutable entities, use the ``associateWith`` operation with a join expression: .. code-block:: java @@ -667,17 +625,16 @@ You have to use the ``associateWith`` operation with join expression. Emp_ m = new Emp_(); Dept_ d = new Dept_(); - List list = - queryDsl - .from(e) - .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) - .leftJoin(m, on -> on.eq(e.managerId, m.employeeId)) - .where(c -> c.eq(d.departmentName, "SALES")) - .associateWith(e, d, Emp::withDept) - .associateWith(e, m, Emp::withManager) - .fetch(); + List list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .leftJoin(m, on -> on.eq(e.managerId, m.employeeId)) + .where(c -> c.eq(d.departmentName, "SALES")) + .associateWith(e, d, Emp::withDept) + .associateWith(e, m, Emp::withManager) + .fetch(); -The above query issues the following SQL statement: +This query generates: .. code-block:: sql @@ -691,35 +648,28 @@ The above query issues the following SQL statement: left outer join EMPLOYEE t2_ on (t0_.MANAGER_ID = t2_.EMPLOYEE_ID) where t1_.DEPARTMENT_NAME = ? -Dynamic join expression -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A join expression uses only evaluated operators to build a JOIN clause. +Dynamic Join Expression +~~~~~~~~~~~~~~~~~~~~~~~ -When every operators are not evaluated in a join expression, -the built statement doesn't have any JOIN clause. +A join expression uses only evaluated operators to build a JOIN clause. When no operators are evaluated, the JOIN clause is omitted. -For example, suppose that a join expression contains a conditional expression as follows: +For example, with a conditional join: .. code-block:: java Employee_ e = new Employee_(); Employee_ e2 = new Employee_(); - List list = - queryDsl - .from(e) - .innerJoin( - e2, - on -> { - if (join) { - on.eq(e.managerId, e2.employeeId); - } - }) - .fetch(); + List list = queryDsl + .from(e) + .innerJoin(e2, on -> { + if (join) { + on.eq(e.managerId, e2.employeeId); + } + }) + .fetch(); -In the case that the ``join`` variable is ``false``, the ``on`` expression is ignored. -The above query issues the following SQL statement: +If ``join`` is ``false``, the ``on`` expression is ignored, generating: .. code-block:: sql @@ -727,41 +677,37 @@ The above query issues the following SQL statement: t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION from EMPLOYEE t0_ -Dynamic association -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Dynamic Association +~~~~~~~~~~~~~~~~~~~ -When you use the above dynamic join expression, the association must be optional. -To do it, pass the result of ``AssociationOption.optional()`` to the associate method: +With dynamic join expressions, associations can be made optional. Use ``AssociationOption.optional()`` in the ``associate`` method: .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List list = - queryDsl - .from(e) - .innerJoin( - d, - on -> { - if (join) { - on.eq(e.departmentId, d.departmentId); - } - }) - .associate( - e, - d, - (employee, department) -> { - employee.setDepartment(department); - department.getEmployeeList().add(employee); - }, - AssociationOption.optional()) - .fetch(); - -Aggregate Functions -------------------------------- - -We support the following aggregate functions: + List list = queryDsl + .from(e) + .innerJoin(d, on -> { + if (join) { + on.eq(e.departmentId, d.departmentId); + } + }) + .associate( + e, + d, + (employee, department) -> { + employee.setDepartment(department); + department.getEmployeeList().add(employee); + }, + AssociationOption.optional()) + .fetch(); + +Aggregate Functions +------------------- + +The following aggregate functions are supported: * avg(property) * avgAsDouble(property) @@ -772,10 +718,9 @@ We support the following aggregate functions: * min(property) * sum(property) -These are defined in the ``org.seasar.doma.jdbc.criteria.expression.Expressions`` class. -Use them with static import. +These functions are defined in the ``org.seasar.doma.jdbc.criteria.expression.Expressions`` class and can be used with static imports. -For example, you can pass the ``sum`` function to the select method: +For example, to pass the ``sum`` function to the select method: .. code-block:: java @@ -783,43 +728,45 @@ For example, you can pass the ``sum`` function to the select method: Salary salary = queryDsl.from(e).select(sum(e.salary)).fetchOne(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql select sum(t0_.SALARY) from EMPLOYEE t0_ -Group by expression -------------------------------- +Group By Expression +------------------- + +Group by expressions allow for grouping results based on specified columns: .. code-block:: java Employee_ e = new Employee_(); - List> list = - queryDsl.from(e).groupBy(e.departmentId).select(e.departmentId, count()).fetch(); + List> list = queryDsl + .from(e) + .groupBy(e.departmentId) + .select(e.departmentId, count()) + .fetch(); -The above query issues the following SQL statement: +The above code generates: .. code-block:: sql select t0_.DEPARTMENT_ID, count(*) from EMPLOYEE t0_ group by t0_.DEPARTMENT_ID -When you don't specify a group by expression, -the expression is inferred from the select expression automatically. -So the following code issue the same SQL statement above: +When a group by expression is not specified, the expression is inferred from the select expression automatically. Thus, the following code issues the same SQL as above: .. code-block:: java Employee_ e = new Employee_(); - List> list = - queryDsl.from(e).select(e.departmentId, count()).fetch(); + List> list = queryDsl.from(e).select(e.departmentId, count()).fetch(); -Having expression ------------------------------ +Having Expression +----------------- -We support the following operators: +The following operators are supported in having expressions: * eq - (=) * ne - (<>) @@ -828,7 +775,7 @@ We support the following operators: * le - (<=) * lt - (<) -We also support the following logical operators: +Logical operators are also supported: * and * or @@ -839,40 +786,54 @@ We also support the following logical operators: Employee_ e = new Employee_(); Department_ d = new Department_(); - List> list = - queryDsl - .from(e) - .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) - .having(c -> c.gt(count(), 3L)) - .orderBy(c -> c.asc(count())) - .select(count(), d.departmentName) - .fetch(); + List> list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .having(c -> c.gt(count(), 3L)) + .orderBy(c -> c.asc(count())) + .select(count(), d.departmentName) + .fetch(); -The above query issues the following SQL statement: +The above query generates: .. code-block:: sql select count(*), t1_.DEPARTMENT_NAME from EMPLOYEE t0_ inner join DEPARTMENT t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) - group by t1_.DEPARTMENT_NAME having count(*) > ? or (min(t0_.SALARY) <= ?) + group by t1_.DEPARTMENT_NAME having count(*) > ? order by count(*) asc -Dynamic having expression -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Dynamic Having Expression +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A having expression includes only evaluated operators, omitting the HAVING clause if no operators are evaluated. + +For instance, a conditional expression in a having clause: + +.. code-block:: java -A having expression uses only evaluated operators to build a HAVING clause. + Employee_ e = new Employee_(); + Department_ d = new Department_(); -When every operators are not evaluated in a having expression, -the built statement doesn't have any HAVING clause. + List> list = queryDsl + .from(e) + .innerJoin(d, on -> on.eq(e.departmentId, d.departmentId)) + .groupBy(d.departmentName) + .having(c -> { + if (countCondition) { + c.gt(count(), 3L); + } + }) + .select(count(), d.departmentName) + .fetch(); -As well as, when every operators are not evaluated in a logical operator expression, -the built statement doesn't have the logical operator expression. +If ``countCondition`` is ``false``, the ``having`` clause is ignored in the SQL statement. -Order by expression ------------------------------------------ +Order By Expression +------------------- -We support the following order operations: +Supported ordering operations are: * asc * desc @@ -881,17 +842,15 @@ We support the following order operations: Employee_ e = new Employee_(); - List list = - queryDsl - .from(e) - .orderBy( - c -> { - c.asc(e.departmentId); - c.desc(e.salary); - }) - .fetch(); + List list = queryDsl + .from(e) + .orderBy(c -> { + c.asc(e.departmentId); + c.desc(e.salary); + }) + .fetch(); -The above query issues the following SQL statement: +The query above generates: .. code-block:: sql @@ -900,27 +859,25 @@ The above query issues the following SQL statement: from EMPLOYEE t0_ order by t0_.DEPARTMENT_ID asc, t0_.SALARY desc -Dynamic order by expression -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Dynamic Order By Expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -An order by expression uses only evaluated operators to build an ORDER BY clause. +Order by expressions use only evaluated operators to build the ORDER BY clause. When no operators are evaluated, the ORDER BY clause is omitted. -When every operators are not evaluated in a order by expression, -the built statement doesn't have any ORDER BY clause. +Distinct Expression +------------------- -Distinct expression ------------------------------------------ +To select distinct rows, use ``distinct()``: .. code-block:: java - List list = - queryDsl - .from(d) - .distinct() - .leftJoin(e, on -> on.eq(d.departmentId, e.departmentId)) - .fetch(); + List list = queryDsl + .from(d) + .distinct() + .leftJoin(e, on -> on.eq(d.departmentId, e.departmentId)) + .fetch(); -The above query issues the following SQL statement: +This query generates: .. code-block:: sql @@ -929,17 +886,23 @@ The above query issues the following SQL statement: from DEPARTMENT t0_ left outer join EMPLOYEE t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) -Limit and Offset expression -------------------------------------------------- +Limit and Offset Expression +--------------------------- + +To limit the number of rows and specify an offset: .. code-block:: java Employee_ e = new Employee_(); - List list = - queryDsl.from(e).limit(5).offset(3).orderBy(c -> c.asc(e.employeeNo)).fetch(); + List list = queryDsl + .from(e) + .limit(5) + .offset(3) + .orderBy(c -> c.asc(e.employeeNo)) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -949,25 +912,27 @@ The above query issues the following SQL statement: order by t0_.EMPLOYEE_NO asc offset 3 rows fetch first 5 rows only -Dynamic Limit and Offset expression -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Dynamic Limit and Offset Expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A limit expressions uses only non-null value to build a FETCH FIRST clause. -When the value is null ,the built statement doesn't have any FETCH FIRST clause. +Limit and offset expressions include only non-null values in the SQL. If either value is null, the corresponding FETCH FIRST or OFFSET clause is omitted. -As well as, an offset expressions uses only non-null value to build a OFFSET clause. -When the value is null ,the built statement doesn't have any OFFSET clause. +For Update Expression +--------------------- -For Update expression -------------------------------------------- +The ``forUpdate`` method allows row locking in SQL: .. code-block:: java Employee_ e = new Employee_(); - List list = queryDsl.from(e).where(c -> c.eq(e.employeeId, 1)).forUpdate().fetch(); + List list = queryDsl + .from(e) + .where(c -> c.eq(e.employeeId, 1)) + .forUpdate() + .fetch(); -The above query issues the following SQL statement: +The query above generates: .. code-block:: sql @@ -977,28 +942,27 @@ The above query issues the following SQL statement: where t0_.EMPLOYEE_ID = ? for update -Union expression ----------------------------- +Union Expression +---------------- -We support the following expressions: +Supported union operations include: -- union -- unionAll - (union all) +* union +* unionAll - (union all) .. code-block:: java Employee_ e = new Employee_(); Department_ d = new Department_(); - List> list = - queryDsl - .from(e) - .select(e.employeeId, e.employeeName) - .union(queryDsl.from(d) + List> list = queryDsl + .from(e) + .select(e.employeeId, e.employeeName) + .union(queryDsl.from(d) .select(d.departmentId, d.departmentName)) - .fetch(); + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1006,29 +970,24 @@ The above query issues the following SQL statement: union select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NAME from DEPARTMENT t0_ -The order by expression with index is supported: +Using order by with an index in union queries: .. code-block:: java - Employee_ e = new Employee_(); - Department_ d = new Department_(); - - List> list = - queryDsl - .from(e) - .select(e.employeeId, e.employeeName) - .union(queryDsl.from(d) + List> list = queryDsl + .from(e) + .select(e.employeeId, e.employeeName) + .union(queryDsl.from(d) .select(d.departmentId, d.departmentName)) - .orderBy(c -> c.asc(2)) - .fetch(); + .orderBy(c -> c.asc(2)) + .fetch(); -Derived Table expression ----------------------------------------------- +Derived Table Expression +------------------------ -We support subqueries using derived tables. -However, an entity class corresponding to the derived table is required. +Subqueries using derived tables are supported. A corresponding entity class for the derived table is required. -Define the entity class corresponding to the derived table as follows: +Define the entity class for the derived table as follows: .. code-block:: java @@ -1044,21 +1003,10 @@ Define the entity class corresponding to the derived table as follows: this.amount = bigDecimal.intValue(); } - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getAmount() { - return amount; - } - - public void setAmount(Integer amount) { - this.amount = amount; - } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public Integer getAmount() { return amount; } + public void setAmount(Integer amount) { this.amount = amount; } @Override public boolean equals(Object o) { @@ -1069,13 +1017,10 @@ Define the entity class corresponding to the derived table as follows: } @Override - public int hashCode() { - return Objects.hash(name, amount); - } + public int hashCode() { return Objects.hash(name, amount); } } - -A subquery using a derived table can be written as follows. +A subquery using a derived table can be written as follows: .. code-block:: java @@ -1083,17 +1028,18 @@ A subquery using a derived table can be written as follows. Employee_ e = new Employee_(); NameAndAmount_ t = new NameAndAmount_(); - SetOperand subquery = - queryDsl - .from(e) - .innerJoin(d, c -> c.eq(e.departmentId, d.departmentId)) - .groupBy(d.departmentName) - .select(d.departmentName, Expressions.sum(e.salary)); + SetOperand subquery = queryDsl + .from(e) + .innerJoin(d, c -> c.eq(e.departmentId, d.departmentId)) + .groupBy(d.departmentName) + .select(d.departmentName, Expressions.sum(e.salary)); - List list = - queryDsl.from(t, subquery).orderBy(c -> c.asc(t.name)).fetch(); + List list = queryDsl + .from(t, subquery) + .orderBy(c -> c.asc(t.name)) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1115,16 +1061,15 @@ The above query issues the following SQL statement: order by t0_.NAME asc -Delete statement -============================ +Delete Statement +================ -For the specification of the where expression, see :ref:`query_dsl_where`. -The same rule is applied to delete statements. +The delete statement follows the same rules as the :ref:`query_dsl_where`. -Delete settings -------------------------------------- +Delete Settings +--------------- -We support the following settings: +The following settings are supported: * allowEmptyWhere * batchSize @@ -1134,9 +1079,7 @@ We support the following settings: * sqlLogType * suppressOptimisticLockException -They are all optional. - -You can apply them as follows: +All are optional and can be applied as follows: .. code-block:: java @@ -1151,15 +1094,14 @@ You can apply them as follows: settings.setSqlLogType(SqlLogType.RAW); settings.setSuppressOptimisticLockException(true); }) - .where { } + .where(c -> {}) .execute(); .. note:: - If you want to build a delete statement with an empty WHERE clause, - you have to enable the `allowEmptyWhere` setting. + To allow a delete statement with an empty WHERE clause, enable the `allowEmptyWhere` setting. -Delete record by entity +Delete Record by Entity ----------------------- .. code-block:: java @@ -1170,7 +1112,7 @@ Delete record by entity Result result = queryDsl.delete(e).single(employee).execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1180,47 +1122,42 @@ Batch Delete is also supported: .. code-block:: java - Employee_ e = new Employee_(); - - List employees = - queryDsl.from(e).where(c -> c.in(e.employeeId, Arrays.asList(5, 6))).fetch(); + List employees = queryDsl.from(e).where(c -> c.in(e.employeeId, Arrays.asList(5, 6))).fetch(); BatchResult result = queryDsl.delete(e).batch(employees).execute(); -The execute method may throw following exceptions: +Exceptions thrown by the execute method include: * OptimisticLockException: if the entity has a version property and an update count is 0 -Delete records by where expression +Delete Records by Where Expression ---------------------------------- -.. code-block:: java +To delete by a condition: - Employee_ e = new Employee_(); +.. code-block:: java int count = queryDsl.delete(e).where(c -> c.ge(e.salary, new Salary("2000"))).execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql delete from EMPLOYEE t0_ where t0_.SALARY >= ? -To delete all records, call the ``all`` method: +To delete all records, use the ``all`` method: .. code-block:: java - Employee_ e = new Employee_(); - int count = queryDsl.delete(e).all().execute(); -Insert statement -============================ +Insert Statement +================ -Insert settings -------------------------------------- +Insert Settings +--------------- -We support the following settings: +Supported insert settings include: * comment * queryTimeout @@ -1231,51 +1168,44 @@ We support the following settings: * exclude * ignoreGeneratedKeys -They are all optional. - -You can apply them as follows: +All are optional and can be applied as follows: .. code-block:: java Department_ d = new Department_(); - int count = - queryDsl - .insert(d, settings -> { - settings.setComment("insert department"); - settings.setQueryTimeout(1000); - settings.setSqlLogType(SqlLogType.RAW); - settings.setBatchSize(20); - settings.excludeNull(true); - }) - .values( - c -> { - c.value(d.departmentId, 99); - c.value(d.departmentNo, 99); - c.value(d.departmentName, "aaa"); - c.value(d.location, "bbb"); - c.value(d.version, 1); - }) - .execute(); + int count = queryDsl.insert(d, settings -> { + settings.setComment("insert department"); + settings.setQueryTimeout(1000); + settings.setSqlLogType(SqlLogType.RAW); + settings.setBatchSize(20); + settings.excludeNull(true); + }) + .values(c -> { + c.value(d.departmentId, 99); + c.value(d.departmentNo, 99); + c.value(d.departmentName, "aaa"); + c.value(d.location, "bbb"); + c.value(d.version, 1); + }) + .execute(); -.. code-block:: java +You can specify excluded columns: - Department_ d = new Department_(); +.. code-block:: java Department department = ...; - Result result = queryDsl.insert(d, settings -> + Result result = queryDsl.insert(d, settings -> settings.exclude(d.departmentName, d.location) - ) - .single(department) - .execute(); + ).single(department).execute(); -Insert record with entity ----------------------------- +Insert Record with Entity +------------------------- -.. code-block:: java +Inserting a single entity: - Department_ d = new Department_(); +.. code-block:: java Department department = new Department(); department.setDepartmentId(99); @@ -1285,7 +1215,7 @@ Insert record with entity Result result = queryDsl.insert(d).single(department).execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1296,8 +1226,6 @@ Batch Insert is also supported: .. code-block:: java - Department_ d = new Department_(); - Department department = ...; Department department2 = ...; List departments = Arrays.asList(department, department2); @@ -1308,24 +1236,18 @@ Multi-row Insert is also supported: .. code-block:: java - Department_ d = new Department_(); - - Department department = ...; - Department department2 = ...; - List departments = Arrays.asList(department, department2); - MultiResult result = queryDsl.insert(d).multi(departments).execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) values (?, ?, ?, ?, ?), (?, ?, ?, ?, ?) -Upsert is also supported: +Upsert is supported as well, with options to handle duplicate keys: -By calling on `onDuplicateKeyUpdate`, you can update when a key is duplicated. +To update on duplicate key: .. code-block:: java @@ -1335,7 +1257,7 @@ By calling on `onDuplicateKeyUpdate`, you can update when a key is duplicated. .onDuplicateKeyUpdate() .execute(); -By calling on `onDuplicateKeyIgnore`, you can ignore errors when a key is duplicated. +To ignore duplicates: .. code-block:: java @@ -1345,55 +1267,50 @@ By calling on `onDuplicateKeyIgnore`, you can ignore errors when a key is duplic .onDuplicateKeyIgnore() .execute(); -The execute method may throw following exceptions: +Exceptions include: -* UniqueConstraintException: if an unique constraint is violated +* UniqueConstraintException: if a unique constraint is violated. -Insert record with specified values +Insert Record with Specified Values ----------------------------------- -.. code-block:: java +Inserting records by specifying values: - Department_ d = new Department_(); +.. code-block:: java - int count = - queryDsl - .insert(d) - .values( - c -> { - c.value(d.departmentId, 99); - c.value(d.departmentNo, 99); - c.value(d.departmentName, "aaa"); - c.value(d.location, "bbb"); - c.value(d.version, 1); - }) - .execute(); + int count = queryDsl.insert(d) + .values(c -> { + c.value(d.departmentId, 99); + c.value(d.departmentNo, 99); + c.value(d.departmentName, "aaa"); + c.value(d.location, "bbb"); + c.value(d.version, 1); + }) + .execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) values (?, ?, ?, ?, ?) -The execute method may throw following exceptions: +Unique constraints may throw: -* UniqueConstraintException: if an unique constraint is violated +* UniqueConstraintException: if a unique constraint is violated. -We also support the INSERT SELECT syntax as follows: +We also support the INSERT SELECT syntax: .. code-block:: java Department_ da = new Department_("DEPARTMENT_ARCHIVE"); Department_ d = new Department_(); - int count = - queryDsl - .insert(da) - .select(c -> c.from(d).where(cc -> cc.in(d.departmentId, Arrays.asList(1, 2)))) - .execute(); + int count = queryDsl.insert(da) + .select(c -> c.from(d).where(cc -> cc.in(d.departmentId, Arrays.asList(1, 2)))) + .execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1401,63 +1318,54 @@ The above query issues the following SQL statement: LOCATION, VERSION) select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_ID in (?, ?) -Upsert is also supported: - -By calling on `onDuplicateKeyUpdate`, you can update when a key is duplicated. -You can specify keys for duplicate check targets in `keys`. -You can specify the value of the update in case of duplicates in `set`. +For upserts, specify keys and update values on duplicates: .. code-block:: java int count result = queryDsl .insert(d) - .values( - c -> { - c.value(d.departmentId, 1); - c.value(d.departmentNo, 60); - c.value(d.departmentName, "DEVELOPMENT"); - c.value(d.location, "KYOTO"); - c.value(d.version, 2); - }) + .values(c -> { + c.value(d.departmentId, 1); + c.value(d.departmentNo, 60); + c.value(d.departmentName, "DEVELOPMENT"); + c.value(d.location, "KYOTO"); + c.value(d.version, 2); + }) .onDuplicateKeyUpdate() .keys(d.departmentId) - .set( - c -> { - c.value(d.departmentName, c.excluded(d.departmentName)); - c.value(d.location, "KYOTO"); - c.value(d.version, 3); - }) + .set(c -> { + c.value(d.departmentName, c.excluded(d.departmentName)); + c.value(d.location, "KYOTO"); + c.value(d.version, 3); + }) .execute(); -By calling on `onDuplicateKeyIgnore`, you can ignore errors when a key is duplicated. -You can specify keys for duplicate check targets in `keys`. +To ignore duplicates and specify keys: .. code-block:: java int count result = queryDsl - .insert(d, departments) - .values( - c -> { - c.value(d.departmentId, 1); - c.value(d.departmentNo, 60); - c.value(d.departmentName, "DEVELOPMENT"); - c.value(d.location, "KYOTO"); - c.value(d.version, 2); - }) + .insert(d) + .values(c -> { + c.value(d.departmentId, 1); + c.value(d.departmentNo, 60); + c.value(d.departmentName, "DEVELOPMENT"); + c.value(d.location, "KYOTO"); + c.value(d.version, 2); + }) .onDuplicateKeyIgnore() .keys(d.departmentId) .execute(); -Update statement -============================ +Update Statement +================ -For the specification of the where expression, see :ref:`query_dsl_where`. -The same rule is applied to update statements. +The update statement follows the same specifications as the :ref:`query_dsl_where`. -Update settings -------------------------------------- +Update Settings +--------------- -We support the following settings: +The following settings are supported: * allowEmptyWhere * batchSize @@ -1470,9 +1378,7 @@ We support the following settings: * include * exclude -They are all optional. - -You can apply them as follows: +All are optional and can be applied as follows: .. code-block:: java @@ -1491,29 +1397,26 @@ You can apply them as follows: c.value(e.employeeName, "aaa"); }).execute(); -.. code-block:: java +You can also specify excluded columns: - Employee_ e = new Employee_(); +.. code-block:: java Employee employee = ...; - Result result = queryDsl.update(e, settings -> - settings.exclude(e.hiredate, e.salary) - ) - .single(employee) - .execute(); + Result result = queryDsl.update(e, settings -> + settings.exclude(e.hiredate, e.salary) + ).single(employee).execute(); .. note:: - If you want to build a update statement without a WHERE clause, - you have to enable the `allowEmptyWhere` setting. + To perform an update without a WHERE clause, enable the `allowEmptyWhere` setting. -Update record by entity +Update Record by Entity ----------------------- -.. code-block:: java +Updating a single entity: - Employee_ e = new Employee_(); +.. code-block:: java Employee employee = queryDsl.from(e).where(c -> c.eq(e.employeeId, 5)).fetchOne(); employee.setEmployeeName("aaa"); @@ -1521,7 +1424,7 @@ Update record by entity Result result = queryDsl.update(e).single(employee).execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1532,60 +1435,52 @@ Batch Update is also supported: .. code-block:: java - Employee_ e = new Employee_(); - Employee employee = ...; Employee employee2 = ...; - List departments = Arrays.asList(employee, employee2); + List employees = Arrays.asList(employee, employee2); BatchResult result = queryDsl.update(e).batch(employees).execute(); -The execute method may throw following exceptions: +Exceptions from the execute method may include: -* OptimisticLockException: if the entity has a version property and an update count is 0 -* UniqueConstraintException: if an unique constraint is violated +* OptimisticLockException: if the entity has a version property and the update count is 0 +* UniqueConstraintException: if a unique constraint is violated -Update records by where expression ------------------------------------- +Update Records by Where Expression +---------------------------------- -.. code-block:: java +To update records based on a condition: - Employee_ e = new Employee_(); +.. code-block:: java - int count = - queryDsl - .update(e) - .set(c -> c.value(e.departmentId, 3)) - .where( - c -> { - c.isNotNull(e.managerId); - c.ge(e.salary, new Salary("2000")); - }) - .execute(); + int count = queryDsl.update(e) + .set(c -> c.value(e.departmentId, 3)) + .where(c -> { + c.isNotNull(e.managerId); + c.ge(e.salary, new Salary("2000")); + }) + .execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql update EMPLOYEE t0_ set t0_.DEPARTMENT_ID = ? where t0_.MANAGER_ID is not null and t0_.SALARY >= ? -The execute method may throw following exceptions: - -* UniqueConstraintException: if an unique constraint is violated +Exceptions may include: -Property expressions -========================================== +* UniqueConstraintException: if a unique constraint is violated -All expression methods are defined -in the ``org.seasar.doma.jdbc.criteria.expression.Expressions`` class. +Property Expressions +==================== -Use them with static import. +All property expression methods are in the ``org.seasar.doma.jdbc.criteria.expression.Expressions`` class and can be used with static imports. -Arithmetic expressions +Arithmetic Expressions ---------------------- -We provide the following methods: +The following methods are available for arithmetic expressions: * add - (+) * sub - (-) @@ -1593,20 +1488,16 @@ We provide the following methods: * div - (/) * mod - (%) -You can use the ``add`` method as follows: +Example of using the ``add`` method: .. code-block:: java - Employee_ e = new Employee_(); - - int count = - queryDsl - .update(e) - .set(c -> c.value(e.version, add(e.version, 10))) - .where(c -> c.eq(e.employeeId, 1)) - .execute(); + int count = queryDsl.update(e) + .set(c -> c.value(e.version, add(e.version, 10))) + .where(c -> c.eq(e.employeeId, 1)) + .execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1614,10 +1505,10 @@ The above query issues the following SQL statement: set t0_.VERSION = (t0_.VERSION + ?) where t0_.EMPLOYEE_ID = ? -String functions +String Functions ---------------- -We provide the following method: +The following string functions are provided: * concat * lower @@ -1626,20 +1517,16 @@ We provide the following method: * ltrim * rtrim -You can use the ``concat`` method as follows: +Example using ``concat``: .. code-block:: java - Employee_ e = new Employee_(); - - int count = - queryDsl - .update(e) - .set(c -> c.value(e.employeeName, concat("[", concat(e.employeeName, "]")))) - .where(c -> c.eq(e.employeeId, 1)) - .execute(); + int count = queryDsl.update(e) + .set(c -> c.value(e.employeeName, concat("[", concat(e.employeeName, "]")))) + .where(c -> c.eq(e.employeeId, 1)) + .execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1647,20 +1534,20 @@ The above query issues the following SQL statement: set t0_.EMPLOYEE_NAME = concat(?, concat(t0_.EMPLOYEE_NAME, ?)) where t0_.EMPLOYEE_ID = ? -Literal expression +Literal Expression ------------------ -We provide the following method: - -* literal (for all basic data types) +The ``literal`` method supports all basic data types. -You can use the ``literal`` method as follows: +Example of using ``literal``: .. code-block:: java - Employee employee = queryDsl.from(e).where(c -> c.eq(e.employeeId, literal(1))).fetchOne(); + Employee employee = queryDsl.from(e) + .where(c -> c.eq(e.employeeId, literal(1))) + .fetchOne(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1671,34 +1558,29 @@ The above query issues the following SQL statement: .. note:: - Note that the literal expressions are not recognized as bind variables. + Literal expressions are not recognized as bind variables. -Case expression +Case Expression --------------- -We support the following method: +The following method is supported for case expressions: * when -You can use the ``when`` method as follows: +Example of using ``when``: .. code-block:: java - Employee_ e = new Employee_(); - - List list = - queryDsl - .from(e) - .select( - when( - c -> { - c.eq(e.employeeName, literal("SMITH"), lower(e.employeeName)); - c.eq(e.employeeName, literal("KING"), lower(e.employeeName)); - }, - literal("_"))) - .fetch(); + List list = queryDsl + .from(e) + .select( + when(c -> { + c.eq(e.employeeName, literal("SMITH"), lower(e.employeeName)); + c.eq(e.employeeName, literal("KING"), lower(e.employeeName)); + }, literal("_"))) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1708,40 +1590,33 @@ The above query issues the following SQL statement: else '_' end from EMPLOYEE t0_ -Subquery select expression +Subquery Select Expression -------------------------- -We support the following method: - -* select +The ``select`` method supports subquery select expressions. -You can use the ``select`` method as follows: +Example usage: .. code-block:: java - Employee_ e = new Employee_(); - Employee_ e = new Employee_(); Employee_ e2 = new Employee_(); Department_ d = new Department_(); - SelectExpression subSelect = - select( - c -> - c.from(e2) - .innerJoin(d, on -> on.eq(e2.departmentId, d.departmentId)) - .where(cc -> cc.eq(e.departmentId, d.departmentId)) - .groupBy(d.departmentId) - .select(max(e2.salary))); - - int count = - queryDsl - .update(e) - .set(c -> c.value(e.salary, subSelect)) - .where(c -> c.eq(e.employeeId, 1)) - .execute(); + SelectExpression subSelect = select(c -> + c.from(e2) + .innerJoin(d, on -> on.eq(e2.departmentId, d.departmentId)) + .where(cc -> cc.eq(e.departmentId, d.departmentId)) + .groupBy(d.departmentId) + .select(max(e2.salary)) + ); + + int count = queryDsl.update(e) + .set(c -> c.value(e.salary, subSelect)) + .where(c -> c.eq(e.employeeId, 1)) + .execute(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql @@ -1750,16 +1625,17 @@ The above query issues the following SQL statement: select max(t1_.SALARY) from EMPLOYEE t1_ inner join DEPARTMENT t2_ on (t1_.DEPARTMENT_ID = t2_.DEPARTMENT_ID) - where t0_.DEPARTMENT_ID = t2_.DEPARTMENT_ID group by t2_.DEPARTMENT_ID + where t0_.DEPARTMENT_ID = t2_.DEPARTMENT_ID + group by t2_.DEPARTMENT_ID ) where t0_.EMPLOYEE_ID = ? -User-defined expressions +User-Defined Expressions ------------------------ -You can define user-defined expressions by calling ``Expressions.userDefined``. +You can define user-defined expressions using ``Expressions.userDefined``. -In the example below, the replace function is defined: +Example of defining a custom ``replace`` function: .. code-block:: java @@ -1775,29 +1651,27 @@ In the example below, the replace function is defined: }); } -You can use the replace function in your query as follows: +Using the custom ``replace`` function in a query: .. code-block:: java - Department_ d = new Department_(); - - List list = - queryDsl - .from(d).select(replace(d.location, Expressions.literal("NEW"), Expressions.literal("new"))).fetch(); + List list = queryDsl + .from(d) + .select(replace(d.location, Expressions.literal("NEW"), Expressions.literal("new"))) + .fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql select replace(t0_.LOCATION, 'NEW', 'new') from DEPARTMENT t0_ Scopes -========================================== +====== -Scoping allow you to specify commonly-used query conditions. +Scopes allow you to specify commonly-used query conditions. -To define a simple scope, -create the class which has a method annotated with ``@Scope``: +To define a scope, create a class with a method annotated with ``@Scope``: .. code-block:: java @@ -1808,40 +1682,36 @@ create the class which has a method annotated with ``@Scope``: } } -To enable the scope, -specify the above class in the scopes element of ``@Metamodel``: +To enable the scope, specify the scope class in the ``scopes`` element of ``@Metamodel``: .. code-block:: java @Entity(metamodel = @Metamodel(scopes = { DepartmentScope.class })) public class Department { ... } -Now the metamodel ``Department_`` has a ``onlyTokyo`` method. -You can use it as follows: +Now ``Department_`` includes the ``onlyTokyo`` method, which can be used as follows: .. code-block:: java - Department_ d = new Department_(); - List list = queryDsl.from(d).where(d.onlyTokyo()).fetch(); -The above query issues the following SQL statement: +This generates: .. code-block:: sql select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.LOCATION = ? -When you want to combine other query conditions with scopes, -compose them using the `andThen` method: +To combine other query conditions with scopes, use the ``andThen`` method: .. code-block:: java - Department_ d = new Department_(); - - List list = queryDsl.from(d).where(d.onlyTokyo().andThen(c -> c.gt(d.departmentNo, 50))).fetch(); + List list = queryDsl + .from(d) + .where(d.onlyTokyo().andThen(c -> c.gt(d.departmentNo, 50))) + .fetch(); -You can define several scopes in a class as follows: +Defining multiple scopes within a class: .. code-block:: java @@ -1866,10 +1736,10 @@ Tips ==== Execution in DAO --------------------------------------- +---------------- -It is useful to execute DSLs in the default method of the DAO interface. -To get a ``config`` object, call ``Config.get(this)`` in the default method as follows: +It can be useful to execute DSLs within a default method of the DAO interface. +To obtain a ``config`` object, call ``Config.get(this)`` within the default method: .. code-block:: java @@ -1884,24 +1754,24 @@ To get a ``config`` object, call ``Config.get(this)`` in the default method as f } } -Overwriting the table name ------------------------------------------------- +Overwriting the Table Name +-------------------------- -A metamodel constructor accepts the qualified table name and -the metamodel overwrites its table name. +A metamodel constructor can accept a qualified table name, which allows the metamodel to overwrite its default table name. -It is useful to handle two tables that have the same data structure: +This feature is useful for working with two tables that share the same structure: .. code-block:: java Department_ da = new Department_("DEPARTMENT_ARCHIVE"); Department_ d = new Department_(); - int count = - queryDsl - .insert(da) - .select(c -> c.from(d)) - .execute(); + int count = queryDsl + .insert(da) + .select(c -> c.from(d)) + .execute(); + +This generates: .. code-block:: sql @@ -1910,9 +1780,9 @@ It is useful to handle two tables that have the same data structure: t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ Debugging -------------------------------- +========= -To know the SQL statement built by the DSLs, use the ``asSql`` method: +To inspect the SQL statement generated by DSLs, use the ``asSql`` method: .. code-block:: java @@ -1924,34 +1794,31 @@ To know the SQL statement built by the DSLs, use the ``asSql`` method: System.out.printf("Raw SQL : %s\n", sql.getRawSql()); System.out.printf("Formatted SQL: %s\n", sql.getFormattedSql()); -The above code prints as follows: +The code above outputs the following: .. code-block:: sh Raw SQL : select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = ? Formatted SQL: select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = 'SALES' -The ``asSql`` method doesn't issue the SQL statement to your Database. -It only builds the SQL statement and return it as an ``Sql`` object. +The ``asSql`` method does not execute the SQL statement against the database; it only builds the SQL statement and returns it as an ``Sql`` object. -You can also get the ``Sql`` object by calling the ``peek`` method. +You can also obtain the ``Sql`` object by using the ``peek`` method: .. code-block:: java - Department_ d = new Department_(); - List locations = queryDsl - .from(d) - .peek(System.out::println) - .where(c -> c.eq(d.departmentName, "SALES")) - .peek(System.out::println) - .orderBy(c -> c.asc(d.location)) - .peek(sql -> System.out.println(sql.getFormattedSql())) - .select(d.location) - .peek(sql -> System.out.println(sql.getFormattedSql())) - .fetch(); - -The above code prints as follows: + .from(d) + .peek(System.out::println) + .where(c -> c.eq(d.departmentName, "SALES")) + .peek(System.out::println) + .orderBy(c -> c.asc(d.location)) + .peek(sql -> System.out.println(sql.getFormattedSql())) + .select(d.location) + .peek(sql -> System.out.println(sql.getFormattedSql())) + .fetch(); + +The code above outputs SQL statements at various stages of the query: .. code-block:: sql @@ -1960,8 +1827,10 @@ The above code prints as follows: select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = 'SALES' order by t0_.LOCATION asc select t0_.LOCATION from DEPARTMENT t0_ where t0_.DEPARTMENT_NAME = 'SALES' order by t0_.LOCATION asc -Sample projects +Sample Projects =============== +You can refer to the following sample projects for additional guidance: + * `simple-examples `_ * `kotlin-sample `_