Skip to content

Commit 9a984c7

Browse files
authored
Merge pull request #1149 from domaframework/feat/improve-multi-row-insert
2 parents 3b5e86f + 6ec902f commit 9a984c7

23 files changed

+437
-288
lines changed

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/Dialect.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import org.seasar.doma.jdbc.SqlNode;
1717
import org.seasar.doma.jdbc.criteria.query.CriteriaBuilder;
1818
import org.seasar.doma.jdbc.id.AutoGeneratedKeysType;
19+
import org.seasar.doma.jdbc.query.MultiInsertAssembler;
20+
import org.seasar.doma.jdbc.query.MultiInsertAssemblerContext;
1921
import org.seasar.doma.jdbc.query.UpsertAssembler;
2022
import org.seasar.doma.jdbc.query.UpsertAssemblerContext;
2123
import org.seasar.doma.jdbc.type.JdbcType;
@@ -319,4 +321,14 @@ Sql<?> getIdentityReservationSql(
319321
* @return the UpsertAssembler object for the given context
320322
*/
321323
UpsertAssembler getUpsertAssembler(UpsertAssemblerContext context);
324+
325+
/**
326+
* Returns the MultiInsertAssembler implementation for the given context.
327+
*
328+
* @param <ENTITY> the entity type
329+
* @param context the MultiInsertAssemblerContext object
330+
* @return the MultiInsertAssembler object for the given context
331+
*/
332+
<ENTITY> MultiInsertAssembler getMultiInsertAssembler(
333+
MultiInsertAssemblerContext<ENTITY> context);
322334
}

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/H2UpsertAssembler.java

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.seasar.doma.jdbc.entity.EntityPropertyType;
66
import org.seasar.doma.jdbc.entity.EntityType;
77
import org.seasar.doma.jdbc.query.DuplicateKeyType;
8+
import org.seasar.doma.jdbc.query.InsertRow;
89
import org.seasar.doma.jdbc.query.QueryOperand;
910
import org.seasar.doma.jdbc.query.QueryOperandPair;
1011
import org.seasar.doma.jdbc.query.UpsertAssembler;
@@ -23,7 +24,9 @@ public class H2UpsertAssembler implements UpsertAssembler {
2324

2425
private final List<? extends EntityPropertyType<?, ?>> keys;
2526

26-
private final List<QueryOperandPair> insertValues;
27+
private final List<? extends EntityPropertyType<?, ?>> insertPropertyTypes;
28+
29+
private final List<InsertRow> insertRows;
2730

2831
private final List<QueryOperandPair> setValues;
2932

@@ -34,12 +37,10 @@ public H2UpsertAssembler(UpsertAssemblerContext context) {
3437
this.entityType = context.entityType;
3538
this.duplicateKeyType = context.duplicateKeyType;
3639
this.keys = context.keys;
37-
this.insertValues = context.insertValues.first().getPairs();
38-
this.setValues = context.setValues.getPairs();
40+
this.insertPropertyTypes = context.insertPropertyTypes;
41+
this.insertRows = context.insertRows;
42+
this.setValues = context.setValues;
3943
this.upsertAssemblerSupport = new UpsertAssemblerSupport(context.naming, context.dialect);
40-
if (context.insertValues.getRows().size() > 1) {
41-
throw new UnsupportedOperationException();
42-
}
4344
}
4445

4546
@Override
@@ -59,14 +60,14 @@ public void assemble() {
5960
}
6061
buf.cutBackSql(5);
6162
buf.appendSql(" when not matched then insert (");
62-
for (QueryOperandPair pair : insertValues) {
63-
column(pair.getLeft().getEntityPropertyType());
63+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
64+
column(p);
6465
buf.appendSql(", ");
6566
}
6667
buf.cutBackSql(2);
6768
buf.appendSql(") values (");
68-
for (QueryOperandPair pair : insertValues) {
69-
excludeColumn(pair.getLeft().getEntityPropertyType());
69+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
70+
excludeColumn(p);
7071
buf.appendSql(", ");
7172
}
7273
buf.cutBackSql(2);
@@ -85,20 +86,25 @@ public void assemble() {
8586

8687
private void excludeQuery() {
8788
buf.appendSql("select ");
88-
for (QueryOperandPair pair : insertValues) {
89-
column(pair.getLeft().getEntityPropertyType());
89+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
90+
column(p);
9091
buf.appendSql(", ");
9192
}
9293
buf.cutBackSql(2);
93-
buf.appendSql(" from values (");
94-
for (QueryOperandPair pair : insertValues) {
95-
pair.getRight().accept(queryOperandVisitor);
96-
buf.appendSql(", ");
94+
buf.appendSql(" from values ");
95+
for (InsertRow row : insertRows) {
96+
buf.appendSql("(");
97+
for (QueryOperand value : row) {
98+
value.accept(queryOperandVisitor);
99+
buf.appendSql(", ");
100+
}
101+
buf.cutBackSql(2);
102+
buf.appendSql("), ");
97103
}
98104
buf.cutBackSql(2);
99-
buf.appendSql(") as x (");
100-
for (QueryOperandPair pair : insertValues) {
101-
column(pair.getLeft().getEntityPropertyType());
105+
buf.appendSql(" as x (");
106+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
107+
column(p);
102108
buf.appendSql(", ");
103109
}
104110
buf.cutBackSql(2);

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/MssqlUpsertAssembler.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.seasar.doma.jdbc.entity.EntityPropertyType;
66
import org.seasar.doma.jdbc.entity.EntityType;
77
import org.seasar.doma.jdbc.query.DuplicateKeyType;
8+
import org.seasar.doma.jdbc.query.InsertRow;
89
import org.seasar.doma.jdbc.query.QueryOperand;
910
import org.seasar.doma.jdbc.query.QueryOperandPair;
1011
import org.seasar.doma.jdbc.query.UpsertAssembler;
@@ -17,7 +18,9 @@ public class MssqlUpsertAssembler implements UpsertAssembler {
1718
private final DuplicateKeyType duplicateKeyType;
1819
private final UpsertAssemblerSupport upsertAssemblerSupport;
1920
private final List<? extends EntityPropertyType<?, ?>> keys;
20-
private final List<QueryOperandPair> insertValues;
21+
22+
private final List<? extends EntityPropertyType<?, ?>> insertPropertyTypes;
23+
private final List<InsertRow> insertRows;
2124
private final List<QueryOperandPair> setValues;
2225
private final QueryOperand.Visitor queryOperandVisitor = new QueryOperandVisitor();
2326

@@ -26,19 +29,17 @@ public MssqlUpsertAssembler(UpsertAssemblerContext context) {
2629
this.entityType = context.entityType;
2730
this.duplicateKeyType = context.duplicateKeyType;
2831
this.keys = context.keys;
29-
this.insertValues = context.insertValues.first().getPairs();
30-
this.setValues = context.setValues.getPairs();
32+
this.insertPropertyTypes = context.insertPropertyTypes;
33+
this.insertRows = context.insertRows;
34+
this.setValues = context.setValues;
3135
this.upsertAssemblerSupport = new UpsertAssemblerSupport(context.naming, context.dialect);
32-
if (context.insertValues.getRows().size() > 1) {
33-
throw new UnsupportedOperationException();
34-
}
3536
}
3637

3738
@Override
3839
public void assemble() {
3940
buf.appendSql("merge into ");
4041
tableNameAndAlias(entityType);
41-
buf.appendSql(" using (");
42+
buf.appendSql(" using ");
4243
excludeQuery();
4344
buf.appendSql(" on ");
4445
for (EntityPropertyType<?, ?> key : keys) {
@@ -49,14 +50,14 @@ public void assemble() {
4950
}
5051
buf.cutBackSql(5);
5152
buf.appendSql(" when not matched then insert (");
52-
for (QueryOperandPair pair : insertValues) {
53-
column(pair.getLeft().getEntityPropertyType());
53+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
54+
column(p);
5455
buf.appendSql(", ");
5556
}
5657
buf.cutBackSql(2);
5758
buf.appendSql(") values (");
58-
for (QueryOperandPair pair : insertValues) {
59-
excludeColumn(pair.getLeft().getEntityPropertyType());
59+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
60+
excludeColumn(p);
6061
buf.appendSql(", ");
6162
}
6263
buf.cutBackSql(2);
@@ -75,17 +76,22 @@ public void assemble() {
7576
}
7677

7778
private void excludeQuery() {
78-
buf.appendSql("values (");
79-
for (QueryOperandPair pair : insertValues) {
80-
pair.getRight().accept(queryOperandVisitor);
81-
buf.appendSql(", ");
79+
buf.appendSql("(values ");
80+
for (InsertRow row : insertRows) {
81+
buf.appendSql("(");
82+
for (QueryOperand value : row) {
83+
value.accept(queryOperandVisitor);
84+
buf.appendSql(", ");
85+
}
86+
buf.cutBackSql(2);
87+
buf.appendSql("), ");
8288
}
8389
buf.cutBackSql(2);
84-
buf.appendSql(")) as ");
90+
buf.appendSql(") as ");
8591
excludeAlias();
8692
buf.appendSql(" (");
87-
for (QueryOperandPair pair : insertValues) {
88-
column(pair.getLeft().getEntityPropertyType());
93+
for (EntityPropertyType<?, ?> p : insertPropertyTypes) {
94+
column(p);
8995
buf.appendSql(", ");
9096
}
9197
buf.cutBackSql(2);

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/MysqlUpsertAssembler.java

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import org.seasar.doma.jdbc.entity.EntityPropertyType;
77
import org.seasar.doma.jdbc.entity.EntityType;
88
import org.seasar.doma.jdbc.query.DuplicateKeyType;
9+
import org.seasar.doma.jdbc.query.InsertRow;
910
import org.seasar.doma.jdbc.query.QueryOperand;
10-
import org.seasar.doma.jdbc.query.QueryOperandPairList;
11-
import org.seasar.doma.jdbc.query.QueryRows;
11+
import org.seasar.doma.jdbc.query.QueryOperandPair;
1212
import org.seasar.doma.jdbc.query.UpsertAssembler;
1313
import org.seasar.doma.jdbc.query.UpsertAssemblerContext;
1414
import org.seasar.doma.jdbc.query.UpsertAssemblerSupport;
@@ -18,16 +18,18 @@ public class MysqlUpsertAssembler implements UpsertAssembler {
1818
private final EntityType<?> entityType;
1919
private final DuplicateKeyType duplicateKeyType;
2020
private final UpsertAssemblerSupport upsertAssemblerSupport;
21-
private final QueryRows insertValues;
22-
private final QueryOperandPairList setValues;
21+
private final List<? extends EntityPropertyType<?, ?>> insertPropertyTypes;
22+
private final List<InsertRow> insertRows;
23+
private final List<QueryOperandPair> setValues;
2324
private final QueryOperand.Visitor queryOperandVisitor = new QueryOperandVisitor();
2425
private final MysqlDialect.MySqlVersion version;
2526

2627
public MysqlUpsertAssembler(UpsertAssemblerContext context, MysqlDialect.MySqlVersion version) {
2728
this.buf = context.buf;
2829
this.entityType = context.entityType;
2930
this.duplicateKeyType = context.duplicateKeyType;
30-
this.insertValues = context.insertValues;
31+
this.insertPropertyTypes = context.insertPropertyTypes;
32+
this.insertRows = context.insertRows;
3133
this.setValues = context.setValues;
3234
this.version = version;
3335
this.upsertAssemblerSupport = new UpsertAssemblerSupport(context.naming, context.dialect);
@@ -41,33 +43,15 @@ public void assemble() {
4143
}
4244
buf.appendSql(" into ");
4345
tableNameOnly(entityType);
44-
join(
45-
insertValues.first().getPairs(),
46-
", ",
47-
" (",
48-
")",
49-
buf,
50-
p -> {
51-
column(p.getLeft().getEntityPropertyType());
52-
});
46+
join(insertPropertyTypes, ", ", " (", ")", buf, this::column);
5347
buf.appendSql(" values ");
5448
join(
55-
insertValues.getRows(),
49+
insertRows,
5650
", ",
5751
"",
5852
"",
5953
buf,
60-
row -> {
61-
join(
62-
row.getPairs(),
63-
", ",
64-
"(",
65-
")",
66-
buf,
67-
p -> {
68-
p.getRight().accept(queryOperandVisitor);
69-
});
70-
});
54+
row -> join(row, ", ", "(", ")", buf, p -> p.accept(queryOperandVisitor)));
7155
switch (version) {
7256
case V5:
7357
buf.appendSql(" ");
@@ -82,7 +66,7 @@ public void assemble() {
8266
if (duplicateKeyType == DuplicateKeyType.UPDATE) {
8367
buf.appendSql(" on duplicate key update ");
8468
join(
85-
setValues.getPairs(),
69+
setValues,
8670
", ",
8771
"",
8872
"",
@@ -113,14 +97,14 @@ private void column(EntityPropertyType<?, ?> propertyType) {
11397
}
11498

11599
private <T> void join(
116-
List<T> list,
100+
Iterable<T> iterable,
117101
String delimiter,
118102
String start,
119103
String end,
120104
PreparedSqlBuilder buf,
121105
Consumer<T> consumer) {
122106
buf.appendSql(start);
123-
for (T val : list) {
107+
for (T val : iterable) {
124108
consumer.accept(val);
125109
buf.appendSql(delimiter);
126110
}

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/OracleDialect.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import org.seasar.doma.jdbc.JdbcMappingVisitor;
55
import org.seasar.doma.jdbc.SqlLogFormattingVisitor;
66
import org.seasar.doma.jdbc.id.AutoGeneratedKeysType;
7+
import org.seasar.doma.jdbc.query.MultiInsertAssembler;
8+
import org.seasar.doma.jdbc.query.MultiInsertAssemblerContext;
79
import org.seasar.doma.jdbc.query.UpsertAssembler;
810
import org.seasar.doma.jdbc.query.UpsertAssemblerContext;
911

@@ -51,6 +53,11 @@ public boolean supportsAutoGeneratedKeys() {
5153
return true;
5254
}
5355

56+
@Override
57+
public boolean supportsMultiRowInsertStatement() {
58+
return true;
59+
}
60+
5461
@Override
5562
public AutoGeneratedKeysType getAutoGeneratedKeysType() {
5663
return AutoGeneratedKeysType.FIRST_COLUMN;
@@ -83,4 +90,10 @@ public static class OracleScriptBlockContext extends Oracle11ScriptBlockContext
8390
public UpsertAssembler getUpsertAssembler(UpsertAssemblerContext context) {
8491
return new OracleUpsertAssembler(context);
8592
}
93+
94+
@Override
95+
public <ENTITY> MultiInsertAssembler getMultiInsertAssembler(
96+
MultiInsertAssemblerContext<ENTITY> context) {
97+
return new OracleMultiInsertAssembler<>(context);
98+
}
8699
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package org.seasar.doma.jdbc.dialect;
2+
3+
import java.util.List;
4+
import org.seasar.doma.internal.jdbc.sql.PreparedSqlBuilder;
5+
import org.seasar.doma.jdbc.Naming;
6+
import org.seasar.doma.jdbc.entity.EntityPropertyType;
7+
import org.seasar.doma.jdbc.entity.EntityType;
8+
import org.seasar.doma.jdbc.entity.Property;
9+
import org.seasar.doma.jdbc.query.MultiInsertAssembler;
10+
import org.seasar.doma.jdbc.query.MultiInsertAssemblerContext;
11+
12+
public class OracleMultiInsertAssembler<ENTITY> implements MultiInsertAssembler {
13+
14+
public final PreparedSqlBuilder buf;
15+
public final EntityType<?> entityType;
16+
public final Naming naming;
17+
public final Dialect dialect;
18+
public final List<EntityPropertyType<ENTITY, ?>> insertPropertyTypes;
19+
public final List<ENTITY> entities;
20+
21+
public OracleMultiInsertAssembler(MultiInsertAssemblerContext<ENTITY> context) {
22+
this.buf = context.buf;
23+
this.entityType = context.entityType;
24+
this.naming = context.naming;
25+
this.dialect = context.dialect;
26+
this.insertPropertyTypes = context.insertPropertyTypes;
27+
this.entities = context.entities;
28+
}
29+
30+
@Override
31+
public void assemble() {
32+
buf.appendSql("insert all ");
33+
for (ENTITY entity : entities) {
34+
buf.appendSql("into ");
35+
buf.appendSql(entityType.getQualifiedTableName(naming::apply, dialect::applyQuote));
36+
buf.appendSql(" (");
37+
if (!insertPropertyTypes.isEmpty()) {
38+
for (EntityPropertyType<?, ?> propertyType : insertPropertyTypes) {
39+
buf.appendSql(propertyType.getColumnName(naming::apply, dialect::applyQuote));
40+
buf.appendSql(", ");
41+
}
42+
buf.cutBackSql(2);
43+
}
44+
buf.appendSql(") values (");
45+
if (!insertPropertyTypes.isEmpty()) {
46+
for (EntityPropertyType<ENTITY, ?> propertyType : insertPropertyTypes) {
47+
Property<ENTITY, ?> property = propertyType.createProperty();
48+
property.load(entity);
49+
buf.appendParameter(property.asInParameter());
50+
buf.appendSql(", ");
51+
}
52+
}
53+
buf.cutBackSql(2);
54+
buf.appendSql(") ");
55+
}
56+
buf.appendSql("select 1 from dual");
57+
}
58+
}

0 commit comments

Comments
 (0)