Skip to content

Commit 22e5c28

Browse files
Support subQuery by NativeSql in from, Support alias in column (#1030)
* Support subQuery by NativeSql in from, Support alias in column * Express a derived table using SetOperationContext * Refactoring BuilderSupport#wrapAliasExpression * Add from_subquery_alias and from_subquery_union test-case * Renaming SelectContext#setOperationContextForSubQuery * Remove redundant space in query * Add from_subquery_union and from_subquery_union_all test-case * Fix NameAndAmount getter and setter name * Add from_subquery_doesnot_match_alias test-case --------- Co-authored-by: Toshihiro Nakamura <[email protected]>
1 parent ece3e2b commit 22e5c28

File tree

12 files changed

+651
-8
lines changed

12 files changed

+651
-8
lines changed

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.List;
44
import java.util.Objects;
5+
import java.util.Optional;
56
import java.util.function.Consumer;
67
import org.seasar.doma.jdbc.BatchResult;
78
import org.seasar.doma.jdbc.Config;
@@ -10,6 +11,7 @@
1011
import org.seasar.doma.jdbc.criteria.context.InsertSettings;
1112
import org.seasar.doma.jdbc.criteria.context.SelectContext;
1213
import org.seasar.doma.jdbc.criteria.context.SelectSettings;
14+
import org.seasar.doma.jdbc.criteria.context.SetOperationContext;
1315
import org.seasar.doma.jdbc.criteria.context.UpdateSettings;
1416
import org.seasar.doma.jdbc.criteria.declaration.SelectFromDeclaration;
1517
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
@@ -20,6 +22,7 @@
2022
import org.seasar.doma.jdbc.criteria.statement.EntityqlInsertStatement;
2123
import org.seasar.doma.jdbc.criteria.statement.EntityqlSelectStarting;
2224
import org.seasar.doma.jdbc.criteria.statement.EntityqlUpdateStatement;
25+
import org.seasar.doma.jdbc.criteria.statement.SetOperand;
2326
import org.seasar.doma.jdbc.criteria.statement.Statement;
2427

2528
/**
@@ -36,14 +39,29 @@ public Entityql(Config config) {
3639

3740
public <ENTITY> EntityqlSelectStarting<ENTITY> from(EntityMetamodel<ENTITY> entityMetamodel) {
3841
Objects.requireNonNull(entityMetamodel);
39-
return from(entityMetamodel, settings -> {});
42+
return from(entityMetamodel, null, settings -> {});
4043
}
4144

4245
public <ENTITY> EntityqlSelectStarting<ENTITY> from(
4346
EntityMetamodel<ENTITY> entityMetamodel, Consumer<SelectSettings> settingsConsumer) {
47+
return from(entityMetamodel, null, settingsConsumer);
48+
}
49+
50+
public <ENTITY> EntityqlSelectStarting<ENTITY> from(
51+
EntityMetamodel<ENTITY> entityMetamodel, SetOperand<?> setOperandForSubQuery) {
52+
return from(entityMetamodel, setOperandForSubQuery, settings -> {});
53+
}
54+
55+
public <ENTITY> EntityqlSelectStarting<ENTITY> from(
56+
EntityMetamodel<ENTITY> entityMetamodel,
57+
SetOperand<?> setOperandForSubQuery,
58+
Consumer<SelectSettings> settingsConsumer) {
4459
Objects.requireNonNull(entityMetamodel);
4560
Objects.requireNonNull(settingsConsumer);
46-
SelectContext context = new SelectContext(entityMetamodel);
61+
SetOperationContext<?> setOperationContextForSubQuery =
62+
setOperandForSubQuery == null ? null : setOperandForSubQuery.getContext();
63+
SelectContext context =
64+
new SelectContext(entityMetamodel, Optional.ofNullable(setOperationContextForSubQuery));
4765
settingsConsumer.accept(context.getSettings());
4866
SelectFromDeclaration declaration = new SelectFromDeclaration(context);
4967
return new EntityqlSelectStarting<>(config, declaration, entityMetamodel);

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.seasar.doma.jdbc.criteria;
22

33
import java.util.Objects;
4+
import java.util.Optional;
45
import java.util.function.Consumer;
56
import java.util.function.Function;
67
import org.seasar.doma.internal.jdbc.command.EntityProvider;
@@ -12,6 +13,7 @@
1213
import org.seasar.doma.jdbc.criteria.context.InsertSettings;
1314
import org.seasar.doma.jdbc.criteria.context.SelectContext;
1415
import org.seasar.doma.jdbc.criteria.context.SelectSettings;
16+
import org.seasar.doma.jdbc.criteria.context.SetOperationContext;
1517
import org.seasar.doma.jdbc.criteria.context.UpdateContext;
1618
import org.seasar.doma.jdbc.criteria.context.UpdateSettings;
1719
import org.seasar.doma.jdbc.criteria.declaration.DeleteDeclaration;
@@ -23,6 +25,7 @@
2325
import org.seasar.doma.jdbc.criteria.statement.NativeSqlInsertStarting;
2426
import org.seasar.doma.jdbc.criteria.statement.NativeSqlSelectStarting;
2527
import org.seasar.doma.jdbc.criteria.statement.NativeSqlUpdateStarting;
28+
import org.seasar.doma.jdbc.criteria.statement.SetOperand;
2629
import org.seasar.doma.jdbc.query.SelectQuery;
2730

2831
/**
@@ -41,11 +44,33 @@ public <ENTITY> NativeSqlSelectStarting<ENTITY> from(EntityMetamodel<ENTITY> ent
4144
return from(entityMetamodel, (settings) -> {});
4245
}
4346

47+
public <ENTITY> NativeSqlSelectStarting<ENTITY> from(
48+
EntityMetamodel<ENTITY> entityMetamodel, SetOperand<?> setOperandForSubQuery) {
49+
return from(entityMetamodel, setOperandForSubQuery, (settings) -> {});
50+
}
51+
4452
public <ENTITY> NativeSqlSelectStarting<ENTITY> from(
4553
EntityMetamodel<ENTITY> entityMetamodel, Consumer<SelectSettings> settingsConsumer) {
4654
Objects.requireNonNull(entityMetamodel);
4755
Objects.requireNonNull(settingsConsumer);
48-
SelectContext context = new SelectContext(entityMetamodel);
56+
SelectContext context = new SelectContext(entityMetamodel, Optional.empty());
57+
settingsConsumer.accept(context.getSettings());
58+
SelectFromDeclaration declaration = new SelectFromDeclaration(context);
59+
Function<SelectQuery, ObjectProvider<ENTITY>> factory =
60+
query -> new EntityProvider<>(entityMetamodel.asType(), query, false);
61+
return new NativeSqlSelectStarting<>(config, declaration, entityMetamodel, factory);
62+
}
63+
64+
public <ENTITY> NativeSqlSelectStarting<ENTITY> from(
65+
EntityMetamodel<ENTITY> entityMetamodel,
66+
SetOperand<?> setOperandForSubQuery,
67+
Consumer<SelectSettings> settingsConsumer) {
68+
Objects.requireNonNull(entityMetamodel);
69+
Objects.requireNonNull(settingsConsumer);
70+
SetOperationContext<?> setOperationContextForSubQuery =
71+
setOperandForSubQuery == null ? null : setOperandForSubQuery.getContext();
72+
SelectContext context =
73+
new SelectContext(entityMetamodel, Optional.ofNullable(setOperationContextForSubQuery));
4974
settingsConsumer.accept(context.getSettings());
5075
SelectFromDeclaration declaration = new SelectFromDeclaration(context);
5176
Function<SelectQuery, ObjectProvider<ENTITY>> factory =

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.Objects;
11+
import java.util.Optional;
1112
import java.util.function.BiFunction;
1213
import java.util.stream.Stream;
1314
import org.seasar.doma.internal.util.Pair;
@@ -32,8 +33,16 @@ public class SelectContext implements Context {
3233
associations = new LinkedHashMap<>();
3334
public final SelectSettings settings = new SelectSettings();
3435

35-
public SelectContext(EntityMetamodel<?> entityMetamodel) {
36+
/**
37+
* SetOperandContext for the subquery that serves as the derived table for this#entityMetamodel.
38+
*/
39+
public Optional<SetOperationContext<?>> setOperationContextForSubQuery;
40+
41+
public SelectContext(
42+
EntityMetamodel<?> entityMetamodel,
43+
Optional<SetOperationContext<?>> setOperationContextForSubQuery) {
3644
this.entityMetamodel = Objects.requireNonNull(entityMetamodel);
45+
this.setOperationContextForSubQuery = Objects.requireNonNull(setOperationContextForSubQuery);
3746
this.projection = new Projection.EntityMetamodels(entityMetamodel);
3847
}
3948

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Arrays;
55
import java.util.List;
66
import java.util.Objects;
7+
import java.util.Optional;
78
import java.util.function.Consumer;
89
import org.seasar.doma.jdbc.criteria.context.Projection;
910
import org.seasar.doma.jdbc.criteria.context.SelectContext;
@@ -19,7 +20,7 @@ public class SubSelectFromDeclaration<ENTITY> implements SubSelectContext<ENTITY
1920

2021
public SubSelectFromDeclaration(EntityMetamodel<?> entityMetamodel) {
2122
Objects.requireNonNull(entityMetamodel);
22-
SelectContext context = new SelectContext(entityMetamodel);
23+
SelectContext context = new SelectContext(entityMetamodel, Optional.empty());
2324
this.declaration = new SelectFromDeclaration(context);
2425
}
2526

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.seasar.doma.jdbc.criteria.expression;
2+
3+
import java.util.Objects;
4+
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
5+
import org.seasar.doma.jdbc.entity.EntityPropertyType;
6+
7+
public class AliasExpression<PROPERTY> implements PropertyMetamodel<PROPERTY> {
8+
9+
private final PropertyMetamodel<PROPERTY> originalPropertyMetamodel;
10+
private final String alias;
11+
12+
public AliasExpression(PropertyMetamodel<PROPERTY> originalPropertyMetamodel, String alias) {
13+
this.originalPropertyMetamodel = Objects.requireNonNull(originalPropertyMetamodel);
14+
this.alias = Objects.requireNonNull(alias);
15+
}
16+
17+
@Override
18+
public EntityPropertyType<?, ?> asType() {
19+
return originalPropertyMetamodel.asType();
20+
}
21+
22+
@Override
23+
public Class<?> asClass() {
24+
return originalPropertyMetamodel.getClass();
25+
}
26+
27+
@Override
28+
public String getName() {
29+
return originalPropertyMetamodel.getName();
30+
}
31+
32+
@Override
33+
public void accept(PropertyMetamodel.Visitor visitor) {
34+
if (visitor instanceof AliasExpression.Visitor) {
35+
AliasExpression.Visitor v = (AliasExpression.Visitor) visitor;
36+
v.visit(this);
37+
}
38+
}
39+
40+
public PropertyMetamodel<PROPERTY> getOriginalPropertyMetamodel() {
41+
return originalPropertyMetamodel;
42+
}
43+
44+
public String getAlias() {
45+
return alias;
46+
}
47+
48+
@Override
49+
public boolean equals(Object o) {
50+
if (this == o) return true;
51+
if (o == null || getClass() != o.getClass()) return false;
52+
AliasExpression<?> that = (AliasExpression<?>) o;
53+
return originalPropertyMetamodel.equals(that.originalPropertyMetamodel)
54+
&& alias.equals(that.alias);
55+
}
56+
57+
@Override
58+
public int hashCode() {
59+
return Objects.hash(originalPropertyMetamodel, alias);
60+
}
61+
62+
public interface Visitor {
63+
64+
void visit(AliasExpression<?> expression);
65+
}
66+
}

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.seasar.doma.jdbc.criteria.query;
22

3+
import java.util.ArrayList;
34
import java.util.List;
45
import java.util.Objects;
56
import java.util.Optional;
@@ -14,8 +15,11 @@
1415
import org.seasar.doma.jdbc.InParameter;
1516
import org.seasar.doma.jdbc.criteria.context.Criterion;
1617
import org.seasar.doma.jdbc.criteria.context.Operand;
18+
import org.seasar.doma.jdbc.criteria.context.Projection;
1719
import org.seasar.doma.jdbc.criteria.context.SelectContext;
20+
import org.seasar.doma.jdbc.criteria.context.SetOperationContext;
1821
import org.seasar.doma.jdbc.criteria.expression.AggregateFunction;
22+
import org.seasar.doma.jdbc.criteria.expression.AliasExpression;
1923
import org.seasar.doma.jdbc.criteria.expression.ArithmeticExpression;
2024
import org.seasar.doma.jdbc.criteria.expression.CaseExpression;
2125
import org.seasar.doma.jdbc.criteria.expression.LiteralExpression;
@@ -53,6 +57,74 @@ public BuilderSupport(
5357
this.propertyMetamodelVisitor = new PropertyMetamodelVisitor();
5458
}
5559

60+
public void subQuery(
61+
EntityMetamodel<?> entityMetamodel,
62+
SetOperationContext<?> setOperationContext,
63+
AliasManager aliasManager) {
64+
setOperationContext.accept(
65+
new SetOperationContext.Visitor<Void>() {
66+
@Override
67+
public Void visit(SetOperationContext.Select<?> select) {
68+
List<PropertyMetamodel<?>> projection =
69+
wrapAliasExpression(
70+
entityMetamodel, select.context.getProjectionPropertyMetamodels());
71+
select.context.projection = new Projection.PropertyMetamodels(projection);
72+
return null;
73+
}
74+
75+
@Override
76+
public Void visit(SetOperationContext.Union<?> union) {
77+
union.left.accept(this);
78+
union.right.accept(this);
79+
return null;
80+
}
81+
82+
@Override
83+
public Void visit(SetOperationContext.UnionAll<?> unionAll) {
84+
unionAll.left.accept(this);
85+
unionAll.right.accept(this);
86+
return null;
87+
}
88+
});
89+
SetOperationBuilder builder =
90+
new SetOperationBuilder(config, setOperationContext, commenter, buf, aliasManager);
91+
buf.appendSql("(");
92+
builder.build();
93+
buf.appendSql(") ");
94+
String alias = getAlias(entityMetamodel);
95+
buf.appendSql(alias);
96+
}
97+
98+
private List<PropertyMetamodel<?>> wrapAliasExpression(
99+
EntityMetamodel<?> entityMetamodel, List<PropertyMetamodel<?>> projectionPropertyMetamodels) {
100+
if (entityMetamodel.allPropertyMetamodels().size() != projectionPropertyMetamodels.size()) {
101+
throw new DomaException(
102+
Message.DOMA6011,
103+
entityMetamodel.allPropertyMetamodels().size(),
104+
projectionPropertyMetamodels.size());
105+
}
106+
107+
List<PropertyMetamodel<?>> aliasNamePropertyMetamodels =
108+
entityMetamodel.allPropertyMetamodels();
109+
110+
int index = 0;
111+
List<PropertyMetamodel<?>> wrappedPropertyMetamodels =
112+
new ArrayList<>(projectionPropertyMetamodels.size());
113+
for (PropertyMetamodel<?> projectionPropertyMetamodel : projectionPropertyMetamodels) {
114+
PropertyMetamodel<?> aliasNamePropertyMetamodel = aliasNamePropertyMetamodels.get(index++);
115+
if (projectionPropertyMetamodel instanceof AliasExpression) {
116+
wrappedPropertyMetamodels.add(projectionPropertyMetamodel);
117+
continue;
118+
}
119+
String name =
120+
aliasNamePropertyMetamodel
121+
.asType()
122+
.getColumnName(config.getNaming()::apply, config.getDialect()::applyQuote);
123+
wrappedPropertyMetamodels.add(new AliasExpression<>(projectionPropertyMetamodel, name));
124+
}
125+
return wrappedPropertyMetamodels;
126+
}
127+
56128
public void table(EntityMetamodel<?> entityMetamodel) {
57129
EntityType<?> entityType = entityMetamodel.asType();
58130
buf.appendSql(
@@ -532,6 +604,7 @@ private void not(List<Criterion> criterionList) {
532604

533605
class PropertyMetamodelVisitor
534606
implements PropertyMetamodel.Visitor,
607+
AliasExpression.Visitor,
535608
ArithmeticExpression.Visitor,
536609
StringExpression.Visitor,
537610
LiteralExpression.Visitor,
@@ -552,6 +625,12 @@ public void visit(PropertyMetamodel<?> propertyMetamodel) {
552625
propertyType.getColumnName(config.getNaming()::apply, config.getDialect()::applyQuote));
553626
}
554627

628+
@Override
629+
public void visit(AliasExpression<?> aliasExpression) {
630+
aliasExpression.getOriginalPropertyMetamodel().accept(this);
631+
buf.appendSql(" AS " + aliasExpression.getAlias());
632+
}
633+
555634
protected Optional<String> getAlias(PropertyMetamodel<?> propertyMetamodel) {
556635
String alias = aliasManager.getAlias(propertyMetamodel);
557636
if (alias == null) {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.seasar.doma.jdbc.criteria.context.JoinKind;
1717
import org.seasar.doma.jdbc.criteria.context.OrderByItem;
1818
import org.seasar.doma.jdbc.criteria.context.SelectContext;
19+
import org.seasar.doma.jdbc.criteria.context.SetOperationContext;
1920
import org.seasar.doma.jdbc.criteria.expression.AggregateFunction;
2021
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
2122
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
@@ -97,7 +98,13 @@ private void select() {
9798

9899
private void from() {
99100
buf.appendSql(" from ");
100-
table(context.entityMetamodel);
101+
SetOperationContext<?> setOperationContext =
102+
context.setOperationContextForSubQuery.orElse(null);
103+
if (setOperationContext != null) {
104+
subQuery(context.entityMetamodel, setOperationContext, aliasManager);
105+
} else {
106+
table(context.entityMetamodel);
107+
}
101108
if (context.forUpdate != null) {
102109
ForUpdateOption option = context.forUpdate.option;
103110
criteriaBuilder.lockWithTableHint(buf, option, this::column);
@@ -216,6 +223,13 @@ private void table(EntityMetamodel<?> entityMetamodel) {
216223
support.table(entityMetamodel);
217224
}
218225

226+
private void subQuery(
227+
EntityMetamodel<?> entityMetamodel,
228+
SetOperationContext<?> setOperationContext,
229+
AliasManager aliasManager) {
230+
support.subQuery(entityMetamodel, setOperationContext, aliasManager);
231+
}
232+
219233
private void column(PropertyMetamodel<?> propertyMetamodel) {
220234
support.column(propertyMetamodel);
221235
}

0 commit comments

Comments
 (0)