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.
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);
}
}
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.
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.
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'
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.
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()
}
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()
}
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()
}
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