[JAVA] Premiers pas avec les sous-requêtes utilisant Doma avec l'API Criteria

introduction

Doma Montre comment l'API Criteria dans 2.43.0 peut représenter des sous-requêtes SQL.

Pour un aperçu de Doma et de l'API Criteria, lisez les autres articles dans Introduction à Doma.

Exemple de code utilisé dans cet article

Voici un code simplifié. Voir le projet ci-dessous pour l'exemple de code complet.

La base de données a une table de service qui représente les services et une table d'employé qui représente les employés.

schema.sql


create table department (
    id integer not null primary key,
    name varchar(255) not null);

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

Préparez une classe Department correspondant à la table Department et une classe Employee correspondant à la table Employee.

Department.java


@Entity(metamodel = @Metamodel)
public class Department {
  @Id Integer id;
  String name;
}

Employee.java


@Entity(metamodel = @Metamodel)
public class Employee {
  @Id Integer id;
  String name;
  @Column(name = "DEPARTMENT_ID")
  Integer departmentId;
}

Nous préparerons ʻEmployeeRepository` et ajouterons quelques méthodes à cette classe pour montrer un exemple.

public class EmployeeRepository {

  private final Entityql entityql;

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

Sous-requête utilisant le prédicat IN

Appelez la méthode ʻin`.

Notez que l'expression lambda transmise au «where» de la sous-requête utilise l'instance «c2» passée par paramètre au lieu de l'instance externe «c».

  public List<Employee> selectByDepartmentName_in(String departmentName) {
    Employee_ e = new Employee_();
    Department_ d = new Department_();
    return entityql
        .from(e)
        .where(
            c ->
                c.in(
                    e.departmentId,
                    c.from(d).where(c2 -> c2.eq(d.name, departmentName)).select(d.id)))
        .fetch();
  }

Si le paramètre departmentName est" SALES ", le SQL généré ressemblera à ceci: (Ci-après, le SQL avec la valeur liée incorporée est affiché, mais en réalité, le SQL est émis en utilisant la variable de liaison ?.)

select
    t0_.id,
    t0_.name,
    t0_.DEPARTMENT_ID
from
    Employee t0_ 
where 
    t0_.DEPARTMENT_ID in (
        select 
            t1_.id
        from
            Department t1_
        where
            t1_.name = 'SALES'
    )

Si la table department a une clé composite, vous pouvez également rechercher par combinaison de touches en utilisant Tuple2 comme suit (cependant, le format dans lequel la base de données à utiliser spécifie plusieurs colonnes dans le prédicat IN). Doit être pris en charge).

    c.in(
        new Tuple2<>(e.departmentId1, e.departmentId2),
        c.from(d).where(c2 -> c2.eq(d.name, departmentName)).select(d.id1, d.id2)))

La méthode «notIn», qui est l'équivalent du prédicat NOT IN, est également fournie.

Sous-requête utilisant le prédicat EXISTS

Appelez la méthode ʻexists`.

Les précautions sont les mêmes que lors de l'utilisation du prédicat IN. Notez que l'expression lambda transmise au «where» de la sous-requête utilise l'instance «c2» passée par paramètre au lieu de l'instance externe «c».

  public List<Employee> selectByDepartmentName_exists(String departmentName) {
    Employee_ e = new Employee_();
    Department_ d = new Department_();
    return entityql
        .from(e)
        .where(
            c ->
                c.exists(
                    c.from(d)
                        .where(
                            c2 -> {
                              c2.eq(e.departmentId, d.id);
                              c2.eq(d.name, departmentName);
                            })))
        .fetch();
  }

Si le paramètre departmentName est" SALES ", le SQL généré ressemblera à ceci:

select
    t0_.id,
    t0_.name,
    t0_.DEPARTMENT_ID
from
    Employee t0_
where
    exists (
        select
            t1_.id,
            t1_.name
        from
            Department t1_
        where
            t0_.DEPARTMENT_ID = t1_.id 
            and
            t1_.name = 'SALES'
    )

La méthode notExists, qui est l'équivalent du prédicat NOT EXISTS, est également fournie.

Obtenez des résultats de recherche équivalents à l'aide de la clause JOIN

Les requêtes qui ne sont pas des sous-requêtes mais qui obtiennent des résultats de recherche équivalents peuvent également être représentées à l'aide de jointures. Vous n'avez pas à vous soucier des précautions (paramètres d'expression lambda) requises lors de l'utilisation des prédicats IN et EXISTS, donc si les résultats sont identiques, cette méthode est recommandée. Cet exemple appelle la méthode ʻinnerJoin`.

  public List<Employee> selectByDepartmentName_join(String departmentName) {
    Employee_ e = new Employee_();
    Department_ d = new Department_();
    return entityql
        .from(e)
        .innerJoin(d, on -> on.eq(e.departmentId, d.id))
        .where(c -> c.eq(d.name, departmentName))
        .fetch();
  }

Si le paramètre departmentName est" SALES ", le SQL généré ressemblera à ceci:

select
    t0_.id,
    t0_.name,
    t0_.DEPARTMENT_ID 
from
    Employee t0_
inner join
    Department t1_
on 
    (t0_.DEPARTMENT_ID = t1_.id)
where
    t1_.name = 'SALES'

Lorsqu'il est écrit en Kotlin

Surtout pour les sous-requêtes, le code a tendance à être un peu désordonné. Si ce point vous préoccupe, vous pouvez envisager d'écrire en Kotlin. (Doma fournit l'API Criteria pour Kotlin) Si vous écrivez une méthode équivalente à celle ci-dessus dans Kotlin, elle sera globalement rafraîchissante et vous n'aurez pas à vous soucier des paramètres qui sont passés dans l'expression lambda.

Vous trouverez ci-dessous un exemple de code écrit en Kotlin, mais si vous êtes intéressé, veuillez également vous référer à ce projet. J'ai un exemple de code de travail plus complet.

Exemple utilisant le prédicat IN

    fun selectByDepartmentName_in(departmentName: String): List<Employee> {
        val e = Employee_()
        val d = Department_()
        return entityql
            .from(e)
            .where {
                `in`(e.departmentId, from(d).where { eq(d.name, departmentName) }.select(d.id))
            }
            .fetch()
    }

Exemple utilisant le prédicat EXISTS

    fun selectByDepartmentName_exists(departmentName: String): List<Employee> {
        val e = Employee_()
        val d = Department_()
        return entityql
            .from(e)
            .where {
                exists(
                    from(d).where {
                        eq(e.departmentId, d.id)
                        eq(d.name, departmentName)
                    }
                )
            }
            .fetch()
    }

Exemple utilisant la clause JOIN

    fun selectByDepartmentName_join(departmentName: String): List<Employee> {
        val e = Employee_()
        val d = Department_()
        return entityql
            .from(e)
            .innerJoin(d) { eq(e.departmentId, d.id) }
            .where {
                eq(d.name, departmentName)
            }
            .fetch()
    }

en conclusion

Démonstration de la représentation des sous-requêtes dans l'API Criteria de Doma 2.43.0.

Dans cet article, le code Java est formaté au format google-java et le code Kotlin est formaté en ktlint, mais vous souhaiterez peut-être personnaliser un peu le formatage pour améliorer la lisibilité du code. Personnellement, j'ai l'impression que google-java-format a un format avec quelques sauts de ligne autour de l'expression lambda.

Recommended Posts

Premiers pas avec les sous-requêtes utilisant Doma avec l'API Criteria
Premiers pas avec Doma-Using Projection avec l'API Criteira
Premiers pas avec Doma-Using Joins avec l'API Criteira
Premiers pas avec Doma - Introduction à l'API Criteria
Premiers pas avec Doma-Dynamic Construction de clauses WHERE avec l'API Criteria
Prise en main des opérateurs logiques utilisant Doma tels que AND et OR dans la clause WHERE de l'API Criteria
Premiers pas avec Doma-Criteria API Cheet Sheet
Démarrez avec le fonctionnement de JVM GC
Premiers pas avec DBUnit
Premiers pas avec Ruby
Premiers pas avec Swift
Premiers pas avec Doma-Transactions
Premiers pas avec le traitement Doma-Annotation
Premiers pas avec Java Collection
Premiers pas avec JSP et servlet
Premiers pas avec les bases de Java
Premiers pas avec Spring Boot
Premiers pas avec les modules Ruby
Il est maintenant temps de commencer avec l'API Stream
Premiers pas avec Java_Chapitre 5_Exercices pratiques 5_4
[Google Cloud] Premiers pas avec Docker
Premiers pas avec Docker avec VS Code
Revenir au début, démarrer avec Java ② Instructions de contrôle, instructions de boucle
Résumer les principaux points de démarrage avec JPA appris avec Hibernate
Premiers pas avec Docker pour Mac (installation)
Introduction au test de paramétrage dans JUnit
Introduction à Java à partir de 0 Partie 1
Premiers pas avec Ratpack (4) - Routage et contenu statique
Premiers pas avec Language Server Protocol avec LSP4J
Premiers pas avec la création d'ensembles de ressources avec ListResoueceBundle
Premiers pas avec Java_Chapter 8_A propos des "Instances" et des "Classes"
Liens et mémos pour démarrer avec Java (pour moi-même)
Premiers pas avec Java 1 Assembler des éléments similaires
Premiers pas avec Kotlin à envoyer aux développeurs Java
J'ai essayé de démarrer avec Gradle sur Heroku
Revenir au début et démarrer avec Java ① Types de données et modificateurs d'accès
Prise en main des programmes Java à l'aide de Visual Studio Code
Premiers pas avec les anciens ingénieurs Java (Stream + Lambda)
Commencez avec Java sans serveur avec le framework léger Micronaut!