Skip to content

Commit 8ea1c87

Browse files
committed
Create UserDefinedCriteria
1 parent 9d9304a commit 8ea1c87

File tree

6 files changed

+299
-0
lines changed

6 files changed

+299
-0
lines changed

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/context/Criterion.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.Collections;
1919
import java.util.List;
2020
import java.util.Objects;
21+
import java.util.function.Consumer;
22+
import org.seasar.doma.jdbc.criteria.declaration.UserDefinedCriteriaContext;
2123
import org.seasar.doma.jdbc.criteria.option.LikeOption;
2224
import org.seasar.doma.jdbc.criteria.tuple.Tuple2;
2325
import org.seasar.doma.jdbc.criteria.tuple.Tuple3;
@@ -448,6 +450,20 @@ public void accept(Visitor visitor) {
448450
}
449451
}
450452

453+
class UserDefined implements Criterion {
454+
public final Consumer<UserDefinedCriteriaContext.Builder> contextDeclaration;
455+
456+
public UserDefined(Consumer<UserDefinedCriteriaContext.Builder> contextDeclaration) {
457+
Objects.requireNonNull(contextDeclaration);
458+
this.contextDeclaration = contextDeclaration;
459+
}
460+
461+
@Override
462+
public void accept(Visitor visitor) {
463+
visitor.visit(this);
464+
}
465+
}
466+
451467
interface Visitor {
452468
void visit(Eq criterion);
453469

@@ -504,5 +520,7 @@ interface Visitor {
504520
void visit(Or criterion);
505521

506522
void visit(Not criterion);
523+
524+
void visit(UserDefined criterion);
507525
}
508526
}

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/declaration/ComparisonDeclaration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,18 @@ public void or(Runnable block) {
903903
runBlock(block, Criterion.Or::new);
904904
}
905905

906+
public <EXTENSION> void extension(
907+
Function<UserDefinedCriteriaContext, EXTENSION> construct,
908+
Consumer<EXTENSION> extensionDeclaration) {
909+
Objects.requireNonNull(construct);
910+
Objects.requireNonNull(extensionDeclaration);
911+
912+
var extension =
913+
construct.apply(
914+
contextDeclaration -> this.add(new Criterion.UserDefined(contextDeclaration)));
915+
extensionDeclaration.accept(extension);
916+
}
917+
906918
/**
907919
* Add a {@code NOT} operator.
908920
*
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright Doma Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.seasar.doma.jdbc.criteria.declaration;
17+
18+
import java.util.function.Consumer;
19+
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
20+
import org.seasar.doma.jdbc.dialect.Dialect;
21+
22+
/** Represents a user-defined comparison criteria declaration. */
23+
@FunctionalInterface
24+
public interface UserDefinedCriteriaContext {
25+
void add(Consumer<Builder> builder);
26+
27+
interface Builder {
28+
/**
29+
* Appends SQL code to the declaration.
30+
*
31+
* @param sql the SQL code to be appended
32+
*/
33+
void appendSql(String sql);
34+
35+
/**
36+
* cutback SQL.
37+
*
38+
* @param length the length to cutback the SQL code
39+
*/
40+
void cutBackSql(int length);
41+
42+
/**
43+
* Append a expression.
44+
*
45+
* @param propertyMetamodel the {@link PropertyMetamodel} to be added as a expression in the
46+
* declaration
47+
*/
48+
void appendExpression(PropertyMetamodel<?> propertyMetamodel);
49+
50+
/**
51+
* Represents the specific database dialect. This is utilized to modify user-defined expressions
52+
* based on the dialect in use.
53+
*/
54+
Dialect getDialect();
55+
}
56+
}

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/query/BuilderSupport.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Objects;
2121
import java.util.Optional;
2222
import java.util.function.BiFunction;
23+
import java.util.function.Consumer;
2324
import java.util.function.Function;
2425
import org.seasar.doma.DomaException;
2526
import org.seasar.doma.expr.ExpressionFunctions;
@@ -36,6 +37,7 @@
3637
import org.seasar.doma.jdbc.criteria.context.SelectContext;
3738
import org.seasar.doma.jdbc.criteria.context.SetOperationContext;
3839
import org.seasar.doma.jdbc.criteria.context.WithContext;
40+
import org.seasar.doma.jdbc.criteria.declaration.UserDefinedCriteriaContext;
3941
import org.seasar.doma.jdbc.criteria.expression.AggregateFunction;
4042
import org.seasar.doma.jdbc.criteria.expression.AliasExpression;
4143
import org.seasar.doma.jdbc.criteria.expression.ArithmeticExpression;
@@ -49,6 +51,7 @@
4951
import org.seasar.doma.jdbc.criteria.option.LikeOption;
5052
import org.seasar.doma.jdbc.criteria.tuple.Tuple2;
5153
import org.seasar.doma.jdbc.criteria.tuple.Tuple3;
54+
import org.seasar.doma.jdbc.dialect.Dialect;
5255
import org.seasar.doma.jdbc.entity.EntityPropertyType;
5356
import org.seasar.doma.jdbc.entity.EntityType;
5457
import org.seasar.doma.jdbc.entity.Property;
@@ -65,6 +68,7 @@ public class BuilderSupport {
6568
private final PropertyMetamodelVisitor propertyMetamodelVisitor;
6669
private final UserDefinedExpressionDeclarationItemVisitor
6770
userDefinedExpressionDeclarationItemVisitor;
71+
private final UserDefinedCriteriaContextBuilder userDefinedCriteriaContextBuilder;
6872

6973
public BuilderSupport(
7074
Config config,
@@ -79,6 +83,7 @@ public BuilderSupport(
7983
this.propertyMetamodelVisitor = new PropertyMetamodelVisitor();
8084
this.userDefinedExpressionDeclarationItemVisitor =
8185
new UserDefinedExpressionDeclarationItemVisitor();
86+
this.userDefinedCriteriaContextBuilder = new UserDefinedCriteriaContextBuilder();
8287
}
8388

8489
public void with(List<WithContext> withContexts) {
@@ -424,6 +429,11 @@ public void visit(Criterion.Not criterion) {
424429
not(criterion.criterionList);
425430
}
426431

432+
@Override
433+
public void visit(Criterion.UserDefined criterion) {
434+
extension(criterion.contextDeclaration);
435+
}
436+
427437
private void comparison(Operand.Prop left, Operand right, String op) {
428438
column(left);
429439
buf.appendSql(" " + op + " ");
@@ -690,6 +700,10 @@ private void not(List<Criterion> criterionList) {
690700
buf.appendSql(")");
691701
}
692702
}
703+
704+
private void extension(Consumer<UserDefinedCriteriaContext.Builder> contextDeclaration) {
705+
contextDeclaration.accept(userDefinedCriteriaContextBuilder);
706+
}
693707
}
694708

695709
class PropertyMetamodelVisitor
@@ -924,4 +938,28 @@ public void visit(UserDefinedExpression.DeclarationItem.CutbackSql cutbackSql) {
924938
buf.cutBackSql(cutbackSql.get());
925939
}
926940
}
941+
942+
class UserDefinedCriteriaContextBuilder implements UserDefinedCriteriaContext.Builder {
943+
@Override
944+
public void appendSql(String sql) {
945+
Objects.requireNonNull(sql);
946+
buf.appendSql(sql);
947+
}
948+
949+
@Override
950+
public void cutBackSql(int length) {
951+
buf.cutBackSql(length);
952+
}
953+
954+
@Override
955+
public void appendExpression(PropertyMetamodel<?> propertyMetamodel) {
956+
Objects.requireNonNull(propertyMetamodel);
957+
propertyMetamodel.accept(propertyMetamodelVisitor);
958+
}
959+
960+
@Override
961+
public Dialect getDialect() {
962+
return config.getDialect();
963+
}
964+
}
927965
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Doma Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.seasar.doma.it.criteria;
17+
18+
import org.seasar.doma.jdbc.criteria.declaration.UserDefinedCriteriaContext;
19+
import org.seasar.doma.jdbc.criteria.expression.Expressions;
20+
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
21+
22+
record MyExtension(UserDefinedCriteriaContext context) {
23+
public void regexp(PropertyMetamodel<?> entityMetamodel, String regexp) {
24+
context.add(
25+
(b) -> {
26+
var dialectName = b.getDialect().getName();
27+
if (dialectName.startsWith("mysql")) {
28+
b.appendExpression(entityMetamodel);
29+
b.appendSql(" regexp ");
30+
b.appendExpression(Expressions.literal(regexp));
31+
} else if (dialectName.equals("postgres")) {
32+
b.appendExpression(entityMetamodel);
33+
b.appendSql(" ~ ");
34+
b.appendExpression(Expressions.literal(regexp));
35+
} else if (dialectName.equals("oracle")) {
36+
b.appendSql("regexp_like(");
37+
b.appendExpression(entityMetamodel);
38+
b.appendSql(",");
39+
b.appendExpression(Expressions.literal(regexp));
40+
b.appendSql(")");
41+
} else {
42+
b.appendExpression(entityMetamodel);
43+
b.appendSql(" like ");
44+
b.appendExpression(Expressions.literal("%" + regexp + "%"));
45+
}
46+
});
47+
}
48+
}

integration-test-java/src/test/java/org/seasar/doma/it/criteria/QueryDslSqlSelectTest.java

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,4 +1091,131 @@ void select_optional_property_with_aggregate_function() {
10911091
assertTrue(result.isPresent());
10921092
assertEquals(13, result.get());
10931093
}
1094+
1095+
@Test
1096+
void extension() {
1097+
var d = new Department_();
1098+
var list =
1099+
dsl.from(d)
1100+
.where(
1101+
c -> {
1102+
c.extension(
1103+
MyExtension::new,
1104+
(ext) -> {
1105+
ext.regexp(d.departmentName, "A");
1106+
});
1107+
})
1108+
.orderBy(c -> c.asc(d.departmentId))
1109+
.select()
1110+
.fetch();
1111+
1112+
assertEquals(4, list.size());
1113+
assertEquals("ACCOUNTING", list.get(0).getDepartmentName());
1114+
assertEquals("RESEARCH", list.get(1).getDepartmentName());
1115+
assertEquals("SALES", list.get(2).getDepartmentName());
1116+
assertEquals("OPERATIONS", list.get(3).getDepartmentName());
1117+
}
1118+
1119+
@Test
1120+
void extension_multiple_condition() {
1121+
var d = new Department_();
1122+
var list =
1123+
dsl.from(d)
1124+
.where(
1125+
c -> {
1126+
c.extension(
1127+
MyExtension::new,
1128+
(ext) -> {
1129+
ext.regexp(d.departmentName, "SA");
1130+
ext.regexp(d.departmentName, "LE");
1131+
});
1132+
})
1133+
.orderBy(c -> c.asc(d.departmentId))
1134+
.select()
1135+
.fetch();
1136+
1137+
assertEquals(1, list.size());
1138+
assertEquals("SALES", list.get(0).getDepartmentName());
1139+
}
1140+
1141+
@Test
1142+
void extension_and() {
1143+
var d = new Department_();
1144+
var list =
1145+
dsl.from(d)
1146+
.where(
1147+
c -> {
1148+
c.extension(
1149+
MyExtension::new,
1150+
(ext) -> {
1151+
ext.regexp(d.departmentName, "SA");
1152+
c.and(
1153+
() -> {
1154+
ext.regexp(d.departmentName, "LE");
1155+
});
1156+
});
1157+
})
1158+
.orderBy(c -> c.asc(d.departmentId))
1159+
.select()
1160+
.fetch();
1161+
1162+
assertEquals(1, list.size());
1163+
assertEquals("SALES", list.get(0).getDepartmentName());
1164+
}
1165+
1166+
@Test
1167+
void extension_or() {
1168+
var d = new Department_();
1169+
var list =
1170+
dsl.from(d)
1171+
.where(
1172+
c -> {
1173+
c.extension(
1174+
MyExtension::new,
1175+
(ext) -> {
1176+
ext.regexp(d.departmentName, "CC");
1177+
c.or(
1178+
() -> {
1179+
ext.regexp(d.departmentName, "SA");
1180+
});
1181+
});
1182+
})
1183+
.orderBy(c -> c.asc(d.departmentId))
1184+
.select()
1185+
.fetch();
1186+
1187+
assertEquals(2, list.size());
1188+
assertEquals("ACCOUNTING", list.get(0).getDepartmentName());
1189+
assertEquals("SALES", list.get(1).getDepartmentName());
1190+
}
1191+
1192+
@Test
1193+
void extension_or_and() {
1194+
var d = new Department_();
1195+
var list =
1196+
dsl.from(d)
1197+
.where(
1198+
c -> {
1199+
c.extension(
1200+
MyExtension::new,
1201+
(ext) -> {
1202+
ext.regexp(d.departmentName, "CC");
1203+
c.or(
1204+
() -> {
1205+
ext.regexp(d.departmentName, "SA");
1206+
c.and(
1207+
() -> {
1208+
ext.regexp(d.departmentName, "LE");
1209+
});
1210+
});
1211+
});
1212+
})
1213+
.orderBy(c -> c.asc(d.departmentId))
1214+
.select()
1215+
.fetch();
1216+
1217+
assertEquals(2, list.size());
1218+
assertEquals("ACCOUNTING", list.get(0).getDepartmentName());
1219+
assertEquals("SALES", list.get(1).getDepartmentName());
1220+
}
10941221
}

0 commit comments

Comments
 (0)