Skip to content

Commit 3b5e86f

Browse files
authored
Merge pull request #1144 from orekyuu/support-duplicate-key-type
Add DuplicateKeyType to MultiInsert
2 parents 6206454 + b951ad4 commit 3b5e86f

File tree

22 files changed

+759
-98
lines changed

22 files changed

+759
-98
lines changed

doma-core/src/main/java/org/seasar/doma/MultiInsert.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.seasar.doma.jdbc.JdbcException;
1010
import org.seasar.doma.jdbc.SqlLogType;
1111
import org.seasar.doma.jdbc.UniqueConstraintException;
12+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
1213

1314
/**
1415
* Indicates a multi-row insert.
@@ -70,4 +71,20 @@
7071
* @return the output format of SQL logs.
7172
*/
7273
SqlLogType sqlLog() default SqlLogType.FORMATTED;
74+
75+
/**
76+
* This variable represents the type of duplicate key handling strategy for an insert operation.
77+
* It can have one of three values:
78+
*
79+
* <ul>
80+
* <li>UPDATE: If a duplicate key is encountered, the existing row in the table will be updated.
81+
* <li>IGNORE: If a duplicate key is encountered, the insert operation will be ignored, and no
82+
* changes will be made to the table.
83+
* <li>EXCEPTION: If a duplicate key is encountered, the operation will throw an exception,
84+
* indicating that a duplicate key exists.
85+
* </ul>
86+
*
87+
* @return the type of duplicate key handling strategy for an insert operation.
88+
*/
89+
DuplicateKeyType duplicateKeyType() default DuplicateKeyType.EXCEPTION;
7390
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
1414
import org.seasar.doma.jdbc.entity.EntityType;
1515
import org.seasar.doma.jdbc.query.AutoMultiInsertQuery;
16+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
1617
import org.seasar.doma.jdbc.query.Query;
1718

1819
public class EntityqlMultiInsertStatement<ENTITY>
@@ -23,6 +24,7 @@ public class EntityqlMultiInsertStatement<ENTITY>
2324
private final EntityMetamodel<ENTITY> entityMetamodel;
2425
private final List<ENTITY> entities;
2526
private final InsertSettings settings;
27+
private DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
2628

2729
public EntityqlMultiInsertStatement(
2830
Config config,
@@ -35,6 +37,26 @@ public EntityqlMultiInsertStatement(
3537
this.settings = Objects.requireNonNull(settings);
3638
}
3739

40+
/**
41+
* Create statement that inserts or updates
42+
*
43+
* @return statement
44+
*/
45+
public Statement<MultiResult<ENTITY>> onDuplicateKeyUpdate() {
46+
this.duplicateKeyType = DuplicateKeyType.UPDATE;
47+
return this;
48+
}
49+
50+
/**
51+
* Create statement that inserts or ignore
52+
*
53+
* @return statement
54+
*/
55+
public Statement<MultiResult<ENTITY>> onDuplicateKeyIgnore() {
56+
this.duplicateKeyType = DuplicateKeyType.IGNORE;
57+
return this;
58+
}
59+
3860
/**
3961
* {@inheritDoc}
4062
*
@@ -64,6 +86,7 @@ protected Command<MultiResult<ENTITY>> createCommand() {
6486
query.setExcludedPropertyNames(
6587
settings.exclude().stream().map(PropertyMetamodel::getName).toArray(String[]::new));
6688
query.setMessage(settings.getComment());
89+
query.setDuplicateKeyType(this.duplicateKeyType);
6790
query.prepare();
6891
InsertCommand command =
6992
config.getCommandImplementors().createInsertCommand(EXECUTE_METHOD, query);

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@ public H2UpsertAssembler(UpsertAssemblerContext context) {
3434
this.entityType = context.entityType;
3535
this.duplicateKeyType = context.duplicateKeyType;
3636
this.keys = context.keys;
37-
this.insertValues = context.insertValues;
38-
this.setValues = context.setValues;
37+
this.insertValues = context.insertValues.first().getPairs();
38+
this.setValues = context.setValues.getPairs();
3939
this.upsertAssemblerSupport = new UpsertAssemblerSupport(context.naming, context.dialect);
40+
if (context.insertValues.getRows().size() > 1) {
41+
throw new UnsupportedOperationException();
42+
}
4043
}
4144

4245
@Override

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ public MssqlUpsertAssembler(UpsertAssemblerContext context) {
2626
this.entityType = context.entityType;
2727
this.duplicateKeyType = context.duplicateKeyType;
2828
this.keys = context.keys;
29-
this.insertValues = context.insertValues;
30-
this.setValues = context.setValues;
29+
this.insertValues = context.insertValues.first().getPairs();
30+
this.setValues = context.setValues.getPairs();
3131
this.upsertAssemblerSupport = new UpsertAssemblerSupport(context.naming, context.dialect);
32+
if (context.insertValues.getRows().size() > 1) {
33+
throw new UnsupportedOperationException();
34+
}
3235
}
3336

3437
@Override

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

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package org.seasar.doma.jdbc.dialect;
22

33
import java.util.List;
4+
import java.util.function.Consumer;
45
import org.seasar.doma.internal.jdbc.sql.PreparedSqlBuilder;
56
import org.seasar.doma.jdbc.entity.EntityPropertyType;
67
import org.seasar.doma.jdbc.entity.EntityType;
78
import org.seasar.doma.jdbc.query.DuplicateKeyType;
89
import org.seasar.doma.jdbc.query.QueryOperand;
9-
import org.seasar.doma.jdbc.query.QueryOperandPair;
10+
import org.seasar.doma.jdbc.query.QueryOperandPairList;
11+
import org.seasar.doma.jdbc.query.QueryRows;
1012
import org.seasar.doma.jdbc.query.UpsertAssembler;
1113
import org.seasar.doma.jdbc.query.UpsertAssemblerContext;
1214
import org.seasar.doma.jdbc.query.UpsertAssemblerSupport;
@@ -16,8 +18,8 @@ public class MysqlUpsertAssembler implements UpsertAssembler {
1618
private final EntityType<?> entityType;
1719
private final DuplicateKeyType duplicateKeyType;
1820
private final UpsertAssemblerSupport upsertAssemblerSupport;
19-
private final List<QueryOperandPair> insertValues;
20-
private final List<QueryOperandPair> setValues;
21+
private final QueryRows insertValues;
22+
private final QueryOperandPairList setValues;
2123
private final QueryOperand.Visitor queryOperandVisitor = new QueryOperandVisitor();
2224
private final MysqlDialect.MySqlVersion version;
2325

@@ -39,38 +41,57 @@ public void assemble() {
3941
}
4042
buf.appendSql(" into ");
4143
tableNameOnly(entityType);
42-
buf.appendSql(" (");
43-
for (QueryOperandPair pair : insertValues) {
44-
column(pair.getLeft().getEntityPropertyType());
45-
buf.appendSql(", ");
46-
}
47-
buf.cutBackSql(2);
48-
buf.appendSql(") values (");
49-
for (QueryOperandPair pair : insertValues) {
50-
pair.getRight().accept(queryOperandVisitor);
51-
buf.appendSql(", ");
52-
}
53-
buf.cutBackSql(2);
44+
join(
45+
insertValues.first().getPairs(),
46+
", ",
47+
" (",
48+
")",
49+
buf,
50+
p -> {
51+
column(p.getLeft().getEntityPropertyType());
52+
});
53+
buf.appendSql(" values ");
54+
join(
55+
insertValues.getRows(),
56+
", ",
57+
"",
58+
"",
59+
buf,
60+
row -> {
61+
join(
62+
row.getPairs(),
63+
", ",
64+
"(",
65+
")",
66+
buf,
67+
p -> {
68+
p.getRight().accept(queryOperandVisitor);
69+
});
70+
});
5471
switch (version) {
5572
case V5:
56-
buf.appendSql(") ");
73+
buf.appendSql(" ");
5774
break;
5875
case V8:
59-
buf.appendSql(") as ");
76+
buf.appendSql(" as ");
6077
excludeAlias();
6178
break;
6279
default:
6380
throw new IllegalStateException(version.toString());
6481
}
6582
if (duplicateKeyType == DuplicateKeyType.UPDATE) {
6683
buf.appendSql(" on duplicate key update ");
67-
for (QueryOperandPair pair : setValues) {
68-
column(pair.getLeft().getEntityPropertyType());
69-
buf.appendSql(" = ");
70-
pair.getRight().accept(queryOperandVisitor);
71-
buf.appendSql(", ");
72-
}
73-
buf.cutBackSql(2);
84+
join(
85+
setValues.getPairs(),
86+
", ",
87+
"",
88+
"",
89+
buf,
90+
p -> {
91+
column(p.getLeft().getEntityPropertyType());
92+
buf.appendSql(" = ");
93+
p.getRight().accept(queryOperandVisitor);
94+
});
7495
}
7596
}
7697

@@ -91,6 +112,22 @@ private void column(EntityPropertyType<?, ?> propertyType) {
91112
buf.appendSql(sql);
92113
}
93114

115+
private <T> void join(
116+
List<T> list,
117+
String delimiter,
118+
String start,
119+
String end,
120+
PreparedSqlBuilder buf,
121+
Consumer<T> consumer) {
122+
buf.appendSql(start);
123+
for (T val : list) {
124+
consumer.accept(val);
125+
buf.appendSql(delimiter);
126+
}
127+
buf.cutBackSql(delimiter.length());
128+
buf.appendSql(end);
129+
}
130+
94131
class QueryOperandVisitor implements QueryOperand.Visitor {
95132
@Override
96133
public void visit(QueryOperand.Param param) {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ public OracleUpsertAssembler(UpsertAssemblerContext context) {
2626
this.entityType = context.entityType;
2727
this.duplicateKeyType = context.duplicateKeyType;
2828
this.keys = context.keys;
29-
this.insertValues = context.insertValues;
30-
this.setValues = context.setValues;
29+
this.insertValues = context.insertValues.first().getPairs();
30+
this.setValues = context.setValues.getPairs();
3131
this.upsertAssemblerSupport = new UpsertAssemblerSupport(context.naming, context.dialect);
32+
if (context.insertValues.getRows().size() > 1) {
33+
throw new UnsupportedOperationException();
34+
}
3235
}
3336

3437
@Override

0 commit comments

Comments
 (0)